rpm  5.4.15
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 #define CVS_RCSID "$""Log: "
18 #define CVS_REVISION "Revision "
19 
20 void addChangelogEntry(Header h, time_t time, const char *name, const char *text)
21 {
22  HE_t he = (HE_t) memset(alloca(sizeof(*he)), 0, sizeof(*he));
23  rpmuint32_t mytime = (rpmuint32_t)time; /* XXX convert to rpmuint32_t for header */
24  int xx;
25 
27  he->t = RPM_UINT32_TYPE;
28  he->p.ui32p = &mytime;
29  he->c = 1;
30  he->append = 1;
31  xx = headerPut(h, he, 0);
32  he->append = 0;
33 
36  he->p.argv = &name;
37  he->c = 1;
38  he->append = 1;
39  xx = headerPut(h, he, 0);
40  he->append = 0;
41 
44  he->p.argv = &text;
45  he->c = 1;
46  he->append = 1;
47  xx = headerPut(h, he, 0);
48  he->append = 0;
49 }
50 
57 static int dateToTimet(const char * datestr, /*@out@*/ time_t * secs)
58  /*@modifies *secs @*/
59 {
60  struct tm time;
61  time_t timezone_offset;
62  char * p, * pe, * q, ** idx;
63  char * date = strcpy(alloca(strlen(datestr) + 1), datestr);
64 /*@observer@*/ static char * days[] =
65  { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL };
66 /*@observer@*/ static char * months[] =
67  { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
68  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL };
69 /*@observer@*/ static char lengths[] =
70  { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
71 
72  memset(&time, 0, sizeof(time));
73 
74  pe = date;
75 
76  /* day of week */
77  p = pe; mySKIPSPACE(p);
78  if (*p == '\0') return -1;
79  pe = p; mySKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
80  for (idx = days; *idx && strcmp(*idx, p); idx++)
81  {};
82  if (*idx == NULL) return -1;
83 
84  /* month */
85  p = pe; mySKIPSPACE(p);
86  if (*p == '\0') return -1;
87  pe = p; mySKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
88  for (idx = months; *idx && strcmp(*idx, p); idx++)
89  {};
90  if (*idx == NULL) return -1;
91  time.tm_mon = idx - months;
92 
93  /* day */
94  p = pe; mySKIPSPACE(p);
95  if (*p == '\0') return -1;
96  pe = p; mySKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
97 
98  /* make this noon so the day is always right (as we make this UTC) */
99  time.tm_hour = 12;
100 
101  time.tm_mday = strtol(p, &q, 10);
102  if (!(q && *q == '\0')) return -1;
103  if (time.tm_mday < 0 || time.tm_mday > lengths[time.tm_mon]) return -1;
104 
105  /* year */
106  p = pe; mySKIPSPACE(p);
107  if (*p == '\0') return -1;
108  pe = p; mySKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
109  time.tm_year = strtol(p, &q, 10);
110  if (!(q && *q == '\0')) return -1;
111  if (time.tm_year < 1990 || time.tm_year >= 3000) return -1;
112  time.tm_year -= 1900;
113 
114  *secs = mktime(&time);
115  if (*secs == -1) return -1;
116 
117  /* determine timezone offset */
118 /*@-nullpass@*/ /* gmtime(3) unlikely to return NULL soon. */
119  timezone_offset = mktime(gmtime(secs)) - *secs;
120 /*@=nullpass@*/
121 
122  /* adjust to UTC */
123  *secs += timezone_offset;
124 
125  return 0;
126 }
127 
128 /*@-redecl@*/
129 extern time_t get_date(const char * p, void * now); /* XXX expedient lies */
130 /*@=redecl@*/
131 
139  /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
140  /*@modifies h, rpmGlobalMacroContext, internalState @*/
141 {
142  char * s = rpmiobStr(iob);
143  char * se;
144  char *date, *name, *text;
145  int i;
146  time_t time;
147  time_t lastTime = 0;
148  int nentries = 0;
149  static time_t last = 0;
150  static int oneshot = 0;
151  int numchangelog = rpmExpandNumeric("%{?_buildchangelogtruncate}");
152 
153  /* Determine changelog truncation criteria. */
154  if (!oneshot++) {
155  char * t = rpmExpand("%{?_changelog_truncate}", NULL);
156  char *te = NULL;
157  if (t && *t) {
158  long res = strtol(t, &te, 0);
159  if (res >= 0 && *te == '\0') {
160  last = res; /* truncate to no. of entries. */
161  } else {
162 /*@-moduncon@*/
163  res = (long)get_date (t, NULL);
164 /*@=moduncon@*/
165  /* XXX malformed date string silently ignored. */
166  if (res > 0) {
167  last = res; /* truncate to date. */
168  }
169  }
170  }
171  t = _free(t);
172  }
173 
174  /* skip space */
175  mySKIPSPACE(s);
176 
177  while (*s != '\0') {
178  if (*s != '*') {
180  _("%%changelog entries must start with *\n"));
181  return RPMRC_FAIL;
182  }
183 
184  /* find end of line */
185  date = s;
186  while(*s && *s != '\n') s++;
187  if (! *s) {
188  rpmlog(RPMLOG_ERR, _("incomplete %%changelog entry\n"));
189  return RPMRC_FAIL;
190  }
191 /*@-modobserver@*/
192  *s = '\0';
193 /*@=modobserver@*/
194  text = s + 1;
195 
196  /* 4 fields of date */
197  date++;
198  s = date;
199  for (i = 0; i < 4; i++) {
200  mySKIPSPACE(s);
201  mySKIPNONSPACE(s);
202  }
203  mySKIPSPACE(date);
204  if (dateToTimet(date, &time)) {
205  rpmlog(RPMLOG_ERR, _("bad date in %%changelog: %s\n"), date);
206  return RPMRC_FAIL;
207  }
208  if (lastTime && lastTime < time) {
210  _("%%changelog not in descending chronological order\n"));
211  }
212  lastTime = time;
213 
214  /* skip space to the name */
215  mySKIPSPACE(s);
216  if (! *s) {
217  rpmlog(RPMLOG_ERR, _("missing name in %%changelog\n"));
218  return RPMRC_FAIL;
219  }
220 
221  /* name */
222  name = s;
223  while (*s != '\0') s++;
224  while (s > name && isspace(*s))
225  *s-- = '\0';
226 
227  if (s == name) {
228  rpmlog(RPMLOG_ERR, _("missing name in %%changelog\n"));
229  return RPMRC_FAIL;
230  }
231 
232  /* text */
233  mySKIPSPACE(text);
234  if (! *text) {
235  rpmlog(RPMLOG_ERR, _("no description in %%changelog\n"));
236  return RPMRC_FAIL;
237  }
238 
239  /* find the next leading '*' (or eos) */
240  s = text;
241  do {
242  s++;
243  } while (*s && (*(s-1) != '\n' || *s != '*'));
244  se = s;
245  s--;
246 
247  /* backup to end of description */
248  while ((s > text) && xisspace(*s))
249  *s-- = '\0';
250 
251  if (numchangelog && (s = strstr(text, CVS_RCSID))) {
252  /* find end of line */
253  while(*s && *s != '\n') s++;
254  if (!*s) {
255  goto out;
256  }
257  s++;
258  if (!*s) {
259  goto out;
260  }
261 
262  /* we reached place where first Revisions should be */
263  i = 0;
264  while (1) {
265  if (strncmp(s, CVS_REVISION, sizeof(CVS_REVISION) - 1) == 0) {
266  if (i++ == numchangelog) {
267  break;
268  }
269  }
270  while(*s && *s != '\n') s++;
271  if (!*s) {
272  break;
273  }
274  s++;
275  }
276 
277  if (*s) {
278  s--;
279  /* backup to the beginning of line */
280  while ((s > text) && (*s == '\n' || xisspace(*s))) {
281  *s-- = '\0';
282  }
283  }
284  }
285 out:
286 
287  /* Add entry if not truncated. */
288  nentries++;
289 
290  if (last <= 0
291  || (last < 1000 && nentries < (int)last)
292  || (last > 1000 && time >= last))
293  addChangelogEntry(h, time, name, text);
294 
295  s = se;
296 
297  }
298 
299  return 0;
300 }
301 
303 {
304  rpmParseState nextPart;
305  rpmiob iob = rpmiobNew(0);
306  rpmRC rc;
307 
308  /* There are no options to %changelog */
309  if ((rc = readLine(spec, STRIP_COMMENTS)) > 0) {
310  iob = rpmiobFree(iob);
311 #if defined(RPM_VENDOR_MANDRIVA)
312  return (spec->clean == NULL) ? PART_CLEAN : PART_NONE;
313 #else
314  return PART_NONE;
315 #endif
316  }
317  if (rc != RPMRC_OK)
318  return rc;
319 
320  while ((nextPart = isPart(spec)) == PART_NONE) {
321  const char * line;
322  line = xstrdup(spec->line);
323  line = xstrtolocale(line);
324  iob = rpmiobAppend(iob, spec->line, 0);
325  line = _free(line);
326  if ((rc = readLine(spec, STRIP_COMMENTS | STRIP_NOEXPAND)) > 0) {
327 #if defined(RPM_VENDOR_MANDRIVA)
328  nextPart = (spec->clean == NULL) ? PART_CLEAN : PART_NONE;
329 #else
330  nextPart = PART_NONE;
331 #endif
332  break;
333  }
334  if (rc != RPMRC_OK)
335  return rc;
336  }
337 
338  rc = addChangelog(spec->packages->header, iob);
339  iob = rpmiobFree(iob);
340 
341  return (rc != RPMRC_OK ? rc : (rpmRC)nextPart);
342 }
rpmTagType t
Definition: rpmtag.h:504
rpmTag tag
Definition: rpmtag.h:503
const char ** argv
Definition: rpmtag.h:75
rpmParseState isPart(Spec spec)
Check line for section separator, return next parser state.
Definition: parseSpec.c:64
rpmiob clean
Definition: rpmspec.h:194
const char * xstrtolocale(const char *str)
Force encoding of string.
Definition: strtolocale.c:15
char * xstrdup(const char *str)
Definition: rpmmalloc.c:321
rpmuint32_t * ui32p
Definition: rpmtag.h:70
int headerPut(Header h, HE_t he, unsigned int flags)
Add or append tag container to header.
Definition: header.c:2294
const char int time
Definition: bson.h:1005
The Header data structure.
rpmiob rpmiobFree(rpmiob iob)
Destroy a I/O buffer instance.
static void rpmlog(int code, const char *fmt,...)
Definition: rpmlog.h:299
rpmiob rpmiobAppend(rpmiob iob, const char *s, size_t nl)
Append string to I/O buffer.
Definition: rpmiob.c:77
#define mySKIPSPACE(s)
int readLine(Spec spec, rpmStripFlags strip)
Read next line from spec file.
Definition: parseSpec.c:351
char * alloca()
Yet Another syslog(3) API clone.
Header header
Definition: rpmspec.h:217
unsigned int rpmuint32_t
Definition: rpmiotypes.h:28
struct _HE_s * HE_t
Definition: rpmtag.h:59
#define CVS_RCSID
char * line
Definition: rpmspec.h:138
time_t get_date(const char *p, void *now)
rpmTagData p
Definition: rpmtag.h:506
static int xisspace(int c)
Definition: rpmiotypes.h:555
rpmTagCount c
Definition: rpmtag.h:507
The structure used to store values parsed from a spec file.
Definition: rpmspec.h:113
const char time_t secs
Definition: bson.h:1028
char * rpmExpand(const char *arg,...)
Return (malloc'ed) concatenated macro expansion(s).
Definition: macro.c:3238
rpmiob rpmiobNew(size_t len)
Create an I/O buffer.
Definition: rpmiob.c:44
void addChangelogEntry(Header h, time_t time, const char *name, const char *text)
Add changelog entry to header.
static int dateToTimet(const char *datestr, time_t *secs)
Parse date string to seconds.
enum rpmRC_e rpmRC
RPM return codes.
char * rpmiobStr(rpmiob iob)
Return I/O buffer (as string).
Definition: rpmiob.c:112
Package packages
Definition: rpmspec.h:204
Definition: rpmtag.h:502
const char const int i
Definition: bson.h:778
This is the only module users of librpmbuild should need to include.
const char const bson const bson bson * out
Definition: mongo.h:678
#define mySKIPNONSPACE(s)
static void * _free(const void *p)
Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
Definition: rpmiotypes.h:756
struct rpmiob_s * rpmiob
Definition: rpmiotypes.h:60
int parseChangelog(Spec spec)
Parse %changelog section of a spec file.
enum rpmParseState_e rpmParseState
static rpmRC addChangelog(Header h, rpmiob iob)
Add changelog section to header.
#define CVS_REVISION
static const char * name
#define _(Text)
Definition: system.h:29
int rpmExpandNumeric(const char *arg)
Return macro expansion as a numeric value.
Definition: macro.c:3312
unsigned int append
Definition: rpmtag.h:511