- Main Page
- Related Pages
- Modules
- Data Structures
- Files

# build/parseChangelog.c

Go to the documentation of this file.

    00001
    00006 #include "system.h"
    00007
    00008 #include <rpmio.h>
    00009 #include "rpmbuild.h"
    00010 #include "debug.h"
    00011
    00012 #define mySKIPSPACE(s) { while (*(s) && isspace(*(s))) (s)++; }
    00013 #define mySKIPNONSPACE(s) { while (*(s) && !isspace(*(s))) (s)++; }
    00014
    00015 void addChangelogEntry(Header h, time_t time, const char *name, const char *text)
    00016 {
    00017     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
    00018     uint32_t mytime = time;     /* XXX convert to uint32_t for header */
    00019     int xx;
    00020
    00021     he->tag = RPMTAG_CHANGELOGTIME;
    00022     he->t = RPM_UINT32_TYPE;
    00023     he->p.ui32p = &mytime;
    00024     he->c = 1;
    00025     he->append = 1;
    00026     xx = headerPut(h, he, 0);
    00027     he->append = 0;
    00028
    00029     he->tag = RPMTAG_CHANGELOGNAME;
    00030     he->t = RPM_STRING_ARRAY_TYPE;
    00031     he->p.argv = &name;
    00032     he->c = 1;
    00033     he->append = 1;
    00034     xx = headerPut(h, he, 0);
    00035     he->append = 0;
    00036
    00037     he->tag = RPMTAG_CHANGELOGTEXT;
    00038     he->t = RPM_STRING_ARRAY_TYPE;
    00039     he->p.argv = &text;
    00040     he->c = 1;
    00041     he->append = 1;
    00042     xx = headerPut(h, he, 0);
    00043     he->append = 0;
    00044 }
    00045
    00052 static int dateToTimet(const char * datestr, /*@out@*/ time_t * secs)
    00053         /*@modifies *secs @*/
    00054 {
    00055     struct tm time;
    00056     time_t timezone_offset;
    00057     char * p, * pe, * q, ** idx;
    00058     char * date = strcpy(alloca(strlen(datestr) + 1), datestr);
    00059 /*@observer@*/ static char * days[] =
    00060         { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL };
    00061 /*@observer@*/ static char * months[] =
    00062         { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    00063           "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL };
    00064 /*@observer@*/ static char lengths[] =
    00065         { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    00066
    00067     memset(&time, 0, sizeof(time));
    00068
    00069     pe = date;
    00070
    00071     /* day of week */
    00072     p = pe; mySKIPSPACE(p);
    00073     if (*p == '\0') return -1;
    00074     pe = p; mySKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
    00075     for (idx = days; *idx && strcmp(*idx, p); idx++)
    00076         {};
    00077     if (*idx == NULL) return -1;
    00078
    00079     /* month */
    00080     p = pe; mySKIPSPACE(p);
    00081     if (*p == '\0') return -1;
    00082     pe = p; mySKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
    00083     for (idx = months; *idx && strcmp(*idx, p); idx++)
    00084         {};
    00085     if (*idx == NULL) return -1;
    00086     time.tm_mon = idx - months;
    00087
    00088     /* day */
    00089     p = pe; mySKIPSPACE(p);
    00090     if (*p == '\0') return -1;
    00091     pe = p; mySKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
    00092
    00093     /* make this noon so the day is always right (as we make this UTC) */
    00094     time.tm_hour = 12;
    00095
    00096     time.tm_mday = strtol(p, &q, 10);
    00097     if (!(q && *q == '\0')) return -1;
    00098     if (time.tm_mday < 0 || time.tm_mday > lengths[time.tm_mon]) return -1;
    00099
    00100     /* year */
    00101     p = pe; mySKIPSPACE(p);
    00102     if (*p == '\0') return -1;
    00103     pe = p; mySKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
    00104     time.tm_year = strtol(p, &q, 10);
    00105     if (!(q && *q == '\0')) return -1;
    00106     if (time.tm_year < 1990 || time.tm_year >= 3000) return -1;
    00107     time.tm_year -= 1900;
    00108
    00109     *secs = mktime(&time);
    00110     if (*secs == -1) return -1;
    00111
    00112     /* determine timezone offset */
    00113 /*@-nullpass@*/         /* gmtime(3) unlikely to return NULL soon. */
    00114     timezone_offset = mktime(gmtime(secs)) - *secs;
    00115 /*@=nullpass@*/
    00116
    00117     /* adjust to UTC */
    00118     *secs += timezone_offset;
    00119
    00120     return 0;
    00121 }
    00122
    00123 /*@-redecl@*/
    00124 extern time_t get_date(const char * p, void * now);     /* XXX expedient lies */
    00125 /*@=redecl@*/
    00126
    00133 static rpmRC addChangelog(Header h, StringBuf sb)
    00134         /*@globals rpmGlobalMacroContext, h_errno @*/
    00135         /*@modifies h, rpmGlobalMacroContext @*/
    00136 {
    00137     char * s = getStringBuf(sb);
    00138     char * se;
    00139     char *date, *name, *text;
    00140     int i;
    00141     time_t time;
    00142     time_t lastTime = 0;
    00143     int nentries = 0;
    00144     static time_t last = 0;
    00145     static int oneshot = 0;
    00146
    00147     /* Determine changelog truncation criteria. */
    00148     if (!oneshot++) {
    00149         char * t = rpmExpand("%{?_changelog_truncate}", NULL);
    00150         char *te = NULL;
    00151         if (t && *t) {
    00152             long res = strtol(t, &te, 0);
    00153             if (res >= 0 && *te == '\0') {
    00154                 last = res;             /* truncate to no. of entries. */
    00155             } else {
    00156 /*@-moduncon@*/
    00157                 res = get_date (t, NULL);
    00158 /*@=moduncon@*/
    00159                 /* XXX malformed date string silently ignored. */
    00160                 if (res > 0) {
    00161                     last = res;         /* truncate to date. */
    00162                 }
    00163             }
    00164         }
    00165         t = _free(t);
    00166     }
    00167
    00168     /* skip space */
    00169     mySKIPSPACE(s);
    00170
    00171     while (*s != '\0') {
    00172         if (*s != '*') {
    00173             rpmlog(RPMLOG_ERR,
    00174                         _("%%changelog entries must start with *\n"));
    00175             return RPMRC_FAIL;
    00176         }
    00177
    00178         /* find end of line */
    00179         date = s;
    00180         while(*s && *s != '\n') s++;
    00181         if (! *s) {
    00182             rpmlog(RPMLOG_ERR, _("incomplete %%changelog entry\n"));
    00183             return RPMRC_FAIL;
    00184         }
    00185 /*@-modobserver@*/
    00186         *s = '\0';
    00187 /*@=modobserver@*/
    00188         text = s + 1;
    00189
    00190         /* 4 fields of date */
    00191         date++;
    00192         s = date;
    00193         for (i = 0; i < 4; i++) {
    00194             mySKIPSPACE(s);
    00195             mySKIPNONSPACE(s);
    00196         }
    00197         mySKIPSPACE(date);
    00198         if (dateToTimet(date, &time)) {
    00199             rpmlog(RPMLOG_ERR, _("bad date in %%changelog: %s\n"), date);
    00200             return RPMRC_FAIL;
    00201         }
    00202         if (lastTime && lastTime < time) {
    00203             rpmlog(RPMLOG_ERR,
    00204                      _("%%changelog not in descending chronological order\n"));
    00205             return RPMRC_FAIL;
    00206         }
    00207         lastTime = time;
    00208
    00209         /* skip space to the name */
    00210         mySKIPSPACE(s);
    00211         if (! *s) {
    00212             rpmlog(RPMLOG_ERR, _("missing name in %%changelog\n"));
    00213             return RPMRC_FAIL;
    00214         }
    00215
    00216         /* name */
    00217         name = s;
    00218         while (*s != '\0') s++;
    00219         while (s > name && isspace(*s))
    00220             *s-- = '\0';
    00221
    00222         if (s == name) {
    00223             rpmlog(RPMLOG_ERR, _("missing name in %%changelog\n"));
    00224             return RPMRC_FAIL;
    00225         }
    00226
    00227         /* text */
    00228         mySKIPSPACE(text);
    00229         if (! *text) {
    00230             rpmlog(RPMLOG_ERR, _("no description in %%changelog\n"));
    00231             return RPMRC_FAIL;
    00232         }
    00233
    00234         /* find the next leading '*' (or eos) */
    00235         s = text;
    00236         do {
    00237            s++;
    00238         } while (*s && (*(s-1) != '\n' || *s != '*'));
    00239         se = s;
    00240         s--;
    00241
    00242         /* backup to end of description */
    00243         while ((s > text) && xisspace(*s))
    00244             *s-- = '\0';
    00245
    00246         /* Add entry if not truncated. */
    00247         nentries++;
    00248
    00249         if (last <= 0
    00250          || (last < 1000 && nentries < last)
    00251          || (last > 1000 && time >= last))
    00252             addChangelogEntry(h, time, name, text);
    00253
    00254         s = se;
    00255
    00256     }
    00257
    00258     return 0;
    00259 }
    00260
    00261 int parseChangelog(Spec spec)
    00262 {
    00263     rpmParseState nextPart;
    00264     StringBuf sb = newStringBuf();
    00265     rpmRC rc;
    00266
    00267     /* There are no options to %changelog */
    00268     if ((rc = readLine(spec, STRIP_COMMENTS)) > 0) {
    00269         sb = freeStringBuf(sb);
    00270         return PART_NONE;
    00271     }
    00272     if (rc != RPMRC_OK)
    00273         return rc;
    00274
    00275     while ((nextPart = isPart(spec)) == PART_NONE) {
    00276         const char * line;
    00277         line = xstrdup(spec->line);
    00278         line = xstrtolocale(line);
    00279         appendStringBuf(sb, spec->line);
    00280         line = _free(line);
    00281         if ((rc = readLine(spec, STRIP_COMMENTS)) > 0) {
    00282             nextPart = PART_NONE;
    00283             break;
    00284         }
    00285         if (rc != RPMRC_OK)
    00286             return rc;
    00287     }
    00288
    00289     rc = addChangelog(spec->packages->header, sb);
    00290     sb = freeStringBuf(sb);
    00291
    00292     return (rc != RPMRC_OK ? rc : nextPart);
    00293 }