2 * \file build/parseChangelog.c
3 * Parse %changelog section from spec file.
8 #include <rpm/header.h>
9 #include <rpm/rpmbuild.h>
10 #include <rpm/rpmlog.h>
13 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
14 #define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; }
16 void addChangelogEntry(Header h, time_t time, const char *name, const char *text)
18 rpm_time_t mytime = time; /* XXX convert to header representation */
20 headerPutUint32(h, RPMTAG_CHANGELOGTIME, &mytime, 1);
21 headerPutString(h, RPMTAG_CHANGELOGNAME, name);
22 headerPutString(h, RPMTAG_CHANGELOGTEXT, text);
26 * Parse date string to seconds.
27 * @param datestr date string (e.g. 'Wed Jan 1 1997')
28 * @retval secs secs since the unix epoch
29 * @return 0 on success, -1 on error
31 static int dateToTimet(const char * datestr, time_t * secs)
33 int rc = -1; /* assume failure */
35 const char * const * idx;
36 char *p, *pe, *q, *date, *tz;
38 static const char * const days[] =
39 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL };
40 static const char * const months[] =
41 { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
42 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL };
43 static const char const lengths[] =
44 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
46 memset(&time, 0, sizeof(time));
48 date = xstrdup(datestr);
53 if (*p == '\0') goto exit;
54 pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
55 for (idx = days; *idx && !rstreq(*idx, p); idx++)
57 if (*idx == NULL) goto exit;
61 if (*p == '\0') goto exit;
62 pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
63 for (idx = months; *idx && !rstreq(*idx, p); idx++)
65 if (*idx == NULL) goto exit;
66 time.tm_mon = idx - months;
70 if (*p == '\0') goto exit;
71 pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
73 /* make this noon so the day is always right (as we make this UTC) */
76 time.tm_mday = strtol(p, &q, 10);
77 if (!(q && *q == '\0')) goto exit;
78 if (time.tm_mday < 0 || time.tm_mday > lengths[time.tm_mon]) goto exit;
82 if (*p == '\0') goto exit;
83 pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
84 time.tm_year = strtol(p, &q, 10);
85 if (!(q && *q == '\0')) goto exit;
86 if (time.tm_year < 1990 || time.tm_year >= 3000) goto exit;
89 /* chnagelog date is always in UTC */
91 if (tz) tz = xstrdup(tz);
92 setenv("TZ", "UTC", 1);
93 *secs = mktime(&time);
99 if (*secs == -1) goto exit;
109 * Add %changelog section to header.
111 * @param sb changelog strings
112 * @return RPMRC_OK on success
114 static rpmRC addChangelog(Header h, StringBuf sb)
120 time_t trimtime = rpmExpandNumeric("%{?_changelog_trimtime}");
121 char *date, *name, *text, *next;
123 s = getStringBuf(sb);
131 _("%%changelog entries must start with *\n"));
135 /* find end of line */
137 while(*s && *s != '\n') s++;
139 rpmlog(RPMLOG_ERR, _("incomplete %%changelog entry\n"));
145 /* 4 fields of date */
148 for (i = 0; i < 4; i++) {
153 if (dateToTimet(date, &time)) {
154 rpmlog(RPMLOG_ERR, _("bad date in %%changelog: %s\n"), date);
157 if (lastTime && lastTime < time) {
159 _("%%changelog not in descending chronological order\n"));
164 /* skip space to the name */
167 rpmlog(RPMLOG_ERR, _("missing name in %%changelog\n"));
173 while (*s != '\0') s++;
174 while (s > name && risspace(*s)) {
178 rpmlog(RPMLOG_ERR, _("missing name in %%changelog\n"));
185 rpmlog(RPMLOG_ERR, _("no description in %%changelog\n"));
189 /* find the next leading '*' (or eos) */
193 } while (*s && (*(s-1) != '\n' || *s != '*'));
197 /* backup to end of description */
198 while ((s > text) && risspace(*s)) {
202 if ( !trimtime || time >= trimtime ) {
203 addChangelogEntry(h, time, name, text);
212 int parseChangelog(rpmSpec spec)
214 int nextPart, rc, res = PART_ERROR;
215 StringBuf sb = newStringBuf();
217 /* There are no options to %changelog */
218 if ((rc = readLine(spec, STRIP_COMMENTS)) > 0) {
225 while (! (nextPart = isPart(spec->line))) {
226 appendStringBuf(sb, spec->line);
227 if ((rc = readLine(spec, STRIP_COMMENTS)) > 0) {
228 nextPart = PART_NONE;
235 if (addChangelog(spec->packages->header, sb)) {
241 sb = freeStringBuf(sb);