rpm  5.4.10
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 = (HE_t) 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 
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 
33  he->p.argv = &name;
34  he->c = 1;
35  he->append = 1;
36  xx = headerPut(h, he, 0);
37  he->append = 0;
38 
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 
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 != '*') {
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) {
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 
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 #if defined(RPM_VENDOR_MANDRIVA)
273  return (spec->clean == NULL) ? PART_CLEAN : PART_NONE;
274 #else
275  return PART_NONE;
276 #endif
277  }
278  if (rc != RPMRC_OK)
279  return rc;
280 
281  while ((nextPart = isPart(spec)) == PART_NONE) {
282  const char * line;
283  line = xstrdup(spec->line);
284  line = xstrtolocale(line);
285  iob = rpmiobAppend(iob, spec->line, 0);
286  line = _free(line);
287  if ((rc = readLine(spec, STRIP_COMMENTS | STRIP_NOEXPAND)) > 0) {
288 #if defined(RPM_VENDOR_MANDRIVA)
289  nextPart = (spec->clean == NULL) ? PART_CLEAN : PART_NONE;
290 #else
291  nextPart = PART_NONE;
292 #endif
293  break;
294  }
295  if (rc != RPMRC_OK)
296  return rc;
297  }
298 
299  rc = addChangelog(spec->packages->header, iob);
300  iob = rpmiobFree(iob);
301 
302  return (rc != RPMRC_OK ? rc : (rpmRC)nextPart);
303 }