Realize the remaining bits of direct rpmdb interface are dead too
[platform/upstream/rpm.git] / build / parseChangelog.c
1 /** \ingroup rpmbuild
2  * \file build/parseChangelog.c
3  *  Parse %changelog section from spec file.
4  */
5
6 #include "system.h"
7
8 #include <rpm/header.h>
9 #include <rpm/rpmbuild.h>
10 #include <rpm/rpmlog.h>
11 #include "debug.h"
12
13 #define SKIPSPACE(s) { while (*(s) && risspace(*(s))) (s)++; }
14 #define SKIPNONSPACE(s) { while (*(s) && !risspace(*(s))) (s)++; }
15
16 void addChangelogEntry(Header h, time_t time, const char *name, const char *text)
17 {
18     rpm_time_t mytime = time;   /* XXX convert to header representation */
19                                 
20     headerPutUint32(h, RPMTAG_CHANGELOGTIME, &mytime, 1);
21     headerPutString(h, RPMTAG_CHANGELOGNAME, name);
22     headerPutString(h, RPMTAG_CHANGELOGTEXT, text);
23 }
24
25 /**
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
30  */
31 static int dateToTimet(const char * datestr, time_t * secs)
32 {
33     int rc = -1; /* assume failure */
34     struct tm time;
35     const char * const * idx;
36     char *p, *pe, *q, *date, *tz;
37     
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 };
45     
46     memset(&time, 0, sizeof(time));
47
48     date = xstrdup(datestr);
49     pe = date;
50
51     /* day of week */
52     p = pe; SKIPSPACE(p);
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++)
56         {};
57     if (*idx == NULL) goto exit;
58
59     /* month */
60     p = pe; SKIPSPACE(p);
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++)
64         {};
65     if (*idx == NULL) goto exit;
66     time.tm_mon = idx - months;
67
68     /* day */
69     p = pe; SKIPSPACE(p);
70     if (*p == '\0') goto exit;
71     pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
72
73     /* make this noon so the day is always right (as we make this UTC) */
74     time.tm_hour = 12;
75
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;
79
80     /* year */
81     p = pe; SKIPSPACE(p);
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;
87     time.tm_year -= 1900;
88
89     /* chnagelog date is always in UTC */
90     tz = getenv("TZ");
91     if (tz) tz = xstrdup(tz);
92     setenv("TZ", "UTC", 1);
93     *secs = mktime(&time);
94     unsetenv("TZ");
95     if (tz) {
96         setenv("TZ", tz, 1);
97         free(tz);
98     }
99     if (*secs == -1) goto exit;
100
101     rc = 0;
102
103 exit:
104     free(date);
105     return rc;
106 }
107
108 /**
109  * Add %changelog section to header.
110  * @param h             header
111  * @param sb            changelog strings
112  * @return              RPMRC_OK on success
113  */
114 static rpmRC addChangelog(Header h, StringBuf sb)
115 {
116     char *s;
117     int i;
118     time_t time;
119     time_t lastTime = 0;
120     time_t trimtime = rpmExpandNumeric("%{?_changelog_trimtime}");
121     char *date, *name, *text, *next;
122
123     s = getStringBuf(sb);
124
125     /* skip space */
126     SKIPSPACE(s);
127
128     while (*s != '\0') {
129         if (*s != '*') {
130             rpmlog(RPMLOG_ERR,
131                         _("%%changelog entries must start with *\n"));
132             return RPMRC_FAIL;
133         }
134
135         /* find end of line */
136         date = s;
137         while(*s && *s != '\n') s++;
138         if (! *s) {
139             rpmlog(RPMLOG_ERR, _("incomplete %%changelog entry\n"));
140             return RPMRC_FAIL;
141         }
142         *s = '\0';
143         text = s + 1;
144         
145         /* 4 fields of date */
146         date++;
147         s = date;
148         for (i = 0; i < 4; i++) {
149             SKIPSPACE(s);
150             SKIPNONSPACE(s);
151         }
152         SKIPSPACE(date);
153         if (dateToTimet(date, &time)) {
154             rpmlog(RPMLOG_ERR, _("bad date in %%changelog: %s\n"), date);
155             return RPMRC_FAIL;
156         }
157         if (lastTime && lastTime < time) {
158             rpmlog(RPMLOG_ERR,
159                      _("%%changelog not in descending chronological order\n"));
160             return RPMRC_FAIL;
161         }
162         lastTime = time;
163
164         /* skip space to the name */
165         SKIPSPACE(s);
166         if (! *s) {
167             rpmlog(RPMLOG_ERR, _("missing name in %%changelog\n"));
168             return RPMRC_FAIL;
169         }
170
171         /* name */
172         name = s;
173         while (*s != '\0') s++;
174         while (s > name && risspace(*s)) {
175             *s-- = '\0';
176         }
177         if (s == name) {
178             rpmlog(RPMLOG_ERR, _("missing name in %%changelog\n"));
179             return RPMRC_FAIL;
180         }
181
182         /* text */
183         SKIPSPACE(text);
184         if (! *text) {
185             rpmlog(RPMLOG_ERR, _("no description in %%changelog\n"));
186             return RPMRC_FAIL;
187         }
188             
189         /* find the next leading '*' (or eos) */
190         s = text;
191         do {
192            s++;
193         } while (*s && (*(s-1) != '\n' || *s != '*'));
194         next = s;
195         s--;
196
197         /* backup to end of description */
198         while ((s > text) && risspace(*s)) {
199             *s-- = '\0';
200         }
201         
202         if ( !trimtime || time >= trimtime ) {
203             addChangelogEntry(h, time, name, text);
204         } else break;
205         
206         s = next;
207     }
208
209     return RPMRC_OK;
210 }
211
212 int parseChangelog(rpmSpec spec)
213 {
214     int nextPart, rc, res = PART_ERROR;
215     StringBuf sb = newStringBuf();
216     
217     /* There are no options to %changelog */
218     if ((rc = readLine(spec, STRIP_COMMENTS)) > 0) {
219         res = PART_NONE;
220         goto exit;
221     } else if (rc < 0) {
222         goto exit;
223     }
224     
225     while (! (nextPart = isPart(spec->line))) {
226         appendStringBuf(sb, spec->line);
227         if ((rc = readLine(spec, STRIP_COMMENTS)) > 0) {
228             nextPart = PART_NONE;
229             break;
230         } else if (rc < 0) {
231             goto exit;
232         }
233     }
234
235     if (addChangelog(spec->packages->header, sb)) {
236         goto exit;
237     }
238     res = nextPart;
239
240 exit:
241     sb = freeStringBuf(sb);
242
243     return res;
244 }