rpm 5.2.1

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

parseChangelog.c
Go to the documentation of this file.
1
6 #include "system.h"
7
8 #include <rpmio.h>
9 #include <rpmiotypes.h>
10 #include <rpmlog.h>
11 #include "rpmbuild.h"
12 #include "debug.h"
13
14 #define mySKIPSPACE(s) { while (*(s) && isspace(*(s))) (s)++; }
15 #define mySKIPNONSPACE(s) { while (*(s) && !isspace(*(s))) (s)++; }
16
17 void addChangelogEntry(Header h, time_t time, const char *name, const char *text)
18 {
19 HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
20 rpmuint32_t mytime = (rpmuint32_t)time; /* XXX convert to rpmuint32_t for header */
21 int xx;
22
23 he->tag = RPMTAG_CHANGELOGTIME;
24 he->t = RPM_UINT32_TYPE;
25 he->p.ui32p = &mytime;
26 he->c = 1;
27 he->append = 1;
28 xx = headerPut(h, he, 0);
29 he->append = 0;
30
31 he->tag = RPMTAG_CHANGELOGNAME;
32 he->t = RPM_STRING_ARRAY_TYPE;
33 he->p.argv = &name;
34 he->c = 1;
35 he->append = 1;
36 xx = headerPut(h, he, 0);
37 he->append = 0;
38
39 he->tag = RPMTAG_CHANGELOGTEXT;
40 he->t = RPM_STRING_ARRAY_TYPE;
41 he->p.argv = &text;
42 he->c = 1;
43 he->append = 1;
44 xx = headerPut(h, he, 0);
45 he->append = 0;
46 }
47
54 static int dateToTimet(const char * datestr, /*@out@*/ time_t * secs)
55 /*@modifies *secs @*/
56 {
57 struct tm time;
58 time_t timezone_offset;
59 char * p, * pe, * q, ** idx;
60 char * date = strcpy(alloca(strlen(datestr) + 1), datestr);
61 /*@observer@*/ static char * days[] =
62 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL };
63 /*@observer@*/ static char * months[] =
64 { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
65 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL };
66 /*@observer@*/ static char lengths[] =
67 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
68
69 memset(&time, 0, sizeof(time));
70
71 pe = date;
72
73 /* day of week */
74 p = pe; mySKIPSPACE(p);
75 if (*p == '\0') return -1;
76 pe = p; mySKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
77 for (idx = days; *idx && strcmp(*idx, p); idx++)
78 {};
79 if (*idx == NULL) return -1;
80
81 /* month */
82 p = pe; mySKIPSPACE(p);
83 if (*p == '\0') return -1;
84 pe = p; mySKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
85 for (idx = months; *idx && strcmp(*idx, p); idx++)
86 {};
87 if (*idx == NULL) return -1;
88 time.tm_mon = idx - months;
89
90 /* day */
91 p = pe; mySKIPSPACE(p);
92 if (*p == '\0') return -1;
93 pe = p; mySKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
94
95 /* make this noon so the day is always right (as we make this UTC) */
96 time.tm_hour = 12;
97
98 time.tm_mday = strtol(p, &q, 10);
99 if (!(q && *q == '\0')) return -1;
100 if (time.tm_mday < 0 || time.tm_mday > lengths[time.tm_mon]) return -1;
101
102 /* year */
103 p = pe; mySKIPSPACE(p);
104 if (*p == '\0') return -1;
105 pe = p; mySKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
106 time.tm_year = strtol(p, &q, 10);
107 if (!(q && *q == '\0')) return -1;
108 if (time.tm_year < 1990 || time.tm_year >= 3000) return -1;
109 time.tm_year -= 1900;
110
111 *secs = mktime(&time);
112 if (*secs == -1) return -1;
113
114 /* determine timezone offset */
115 /*@-nullpass@*/ /* gmtime(3) unlikely to return NULL soon. */
116 timezone_offset = mktime(gmtime(secs)) - *secs;
117 /*@=nullpass@*/
118
119 /* adjust to UTC */
120 *secs += timezone_offset;
121
122 return 0;
123 }
124
125 /*@-redecl@*/
126 extern time_t get_date(const char * p, void * now); /* XXX expedient lies */
127 /*@=redecl@*/
128
135 static rpmRC addChangelog(Header h, rpmiob iob)
136 /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
137 /*@modifies h, rpmGlobalMacroContext, internalState @*/
138 {
139 char * s = rpmiobStr(iob);
140 char * se;
141 char *date, *name, *text;
142 int i;
143 time_t time;
144 time_t lastTime = 0;
145 int nentries = 0;
146 static time_t last = 0;
147 static int oneshot = 0;
148
149 /* Determine changelog truncation criteria. */
150 if (!oneshot++) {
151 char * t = rpmExpand("%{?_changelog_truncate}", NULL);
152 char *te = NULL;
153 if (t && *t) {
154 long res = strtol(t, &te, 0);
155 if (res >= 0 && *te == '\0') {
156 last = res; /* truncate to no. of entries. */
157 } else {
158 /*@-moduncon@*/
159 res = (long)get_date (t, NULL);
160 /*@=moduncon@*/
161 /* XXX malformed date string silently ignored. */
162 if (res > 0) {
163 last = res; /* truncate to date. */
164 }
165 }
166 }
167 t = _free(t);
168 }
169
170 /* skip space */
171 mySKIPSPACE(s);
172
173 while (*s != '\0') {
174 if (*s != '*') {
175 rpmlog(RPMLOG_ERR,
176 _("%%changelog entries must start with *\n"));
177 return RPMRC_FAIL;
178 }
179
180 /* find end of line */
181 date = s;
182 while(*s && *s != '\n') s++;
183 if (! *s) {
184 rpmlog(RPMLOG_ERR, _("incomplete %%changelog entry\n"));
185 return RPMRC_FAIL;
186 }
187 /*@-modobserver@*/
188 *s = '\0';
189 /*@=modobserver@*/
190 text = s + 1;
191
192 /* 4 fields of date */
193 date++;
194 s = date;
195 for (i = 0; i < 4; i++) {
196 mySKIPSPACE(s);
197 mySKIPNONSPACE(s);
198 }
199 mySKIPSPACE(date);
200 if (dateToTimet(date, &time)) {
201 rpmlog(RPMLOG_ERR, _("bad date in %%changelog: %s\n"), date);
202 return RPMRC_FAIL;
203 }
204 if (lastTime && lastTime < time) {
205 rpmlog(RPMLOG_ERR,
206 _("%%changelog not in descending chronological order\n"));
207 return RPMRC_FAIL;
208 }
209 lastTime = time;
210
211 /* skip space to the name */
212 mySKIPSPACE(s);
213 if (! *s) {
214 rpmlog(RPMLOG_ERR, _("missing name in %%changelog\n"));
215 return RPMRC_FAIL;
216 }
217
218 /* name */
219 name = s;
220 while (*s != '\0') s++;
221 while (s > name && isspace(*s))
222 *s-- = '\0';
223
224 if (s == name) {
225 rpmlog(RPMLOG_ERR, _("missing name in %%changelog\n"));
226 return RPMRC_FAIL;
227 }
228
229 /* text */
230 mySKIPSPACE(text);
231 if (! *text) {
232 rpmlog(RPMLOG_ERR, _("no description in %%changelog\n"));
233 return RPMRC_FAIL;
234 }
235
236 /* find the next leading '*' (or eos) */
237 s = text;
238 do {
239 s++;
240 } while (*s && (*(s-1) != '\n' || *s != '*'));
241 se = s;
242 s--;
243
244 /* backup to end of description */
245 while ((s > text) && xisspace(*s))
246 *s-- = '\0';
247
248 /* Add entry if not truncated. */
249 nentries++;
250
251 if (last <= 0
252 || (last < 1000 && nentries < (int)last)
253 || (last > 1000 && time >= last))
254 addChangelogEntry(h, time, name, text);
255
256 s = se;
257
258 }
259
260 return 0;
261 }
262
263 int parseChangelog(Spec spec)
264 {
265 rpmParseState nextPart;
266 rpmiob iob = rpmiobNew(0);
267 rpmRC rc;
268
269 /* There are no options to %changelog */
270 if ((rc = readLine(spec, STRIP_COMMENTS)) > 0) {
271 iob = rpmiobFree(iob);
272 return PART_NONE;
273 }
274 if (rc != RPMRC_OK)
275 return rc;
276
277 while ((nextPart = isPart(spec)) == PART_NONE) {
278 const char * line;
279 line = xstrdup(spec->line);
280 line = xstrtolocale(line);
281 iob = rpmiobAppend(iob, spec->line, 0);
282 line = _free(line);
283 if ((rc = readLine(spec, STRIP_COMMENTS | STRIP_NOEXPAND)) > 0) {
284 nextPart = PART_NONE;
285 break;
286 }
287 if (rc != RPMRC_OK)
288 return rc;
289 }
290
291 rc = addChangelog(spec->packages->header, iob);
292 iob = rpmiobFree(iob);
293
294 return (rc != RPMRC_OK ? rc : (rpmRC)nextPart);
295 }