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