Vladimir D. Seleznev
d831a6ed9a
If all these environment variables are set, an entry based on their contents is added to the changelog of source and all binary packages.
269 lines
6.8 KiB
C
269 lines
6.8 KiB
C
/** \ingroup rpmbuild
|
|
* \file build/parseChangelog.c
|
|
* Parse %changelog section from spec file.
|
|
*/
|
|
|
|
#include "system.h"
|
|
|
|
#include "rpmbuild.h"
|
|
#include "debug.h"
|
|
|
|
void addChangelogEntry(Header h, time_t time, const char *name, const char *text)
|
|
{
|
|
int_32 mytime = time; /* XXX convert to header representation */
|
|
if (headerIsEntry(h, RPMTAG_CHANGELOGTIME)) {
|
|
(void) headerAppendEntry(h, RPMTAG_CHANGELOGTIME, RPM_INT32_TYPE,
|
|
&mytime, 1);
|
|
(void) headerAppendEntry(h, RPMTAG_CHANGELOGNAME, RPM_STRING_ARRAY_TYPE,
|
|
&name, 1);
|
|
(void) headerAppendEntry(h, RPMTAG_CHANGELOGTEXT, RPM_STRING_ARRAY_TYPE,
|
|
&text, 1);
|
|
} else {
|
|
(void) headerAddEntry(h, RPMTAG_CHANGELOGTIME, RPM_INT32_TYPE,
|
|
&mytime, 1);
|
|
(void) headerAddEntry(h, RPMTAG_CHANGELOGNAME, RPM_STRING_ARRAY_TYPE,
|
|
&name, 1);
|
|
(void) headerAddEntry(h, RPMTAG_CHANGELOGTEXT, RPM_STRING_ARRAY_TYPE,
|
|
&text, 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse date string to seconds.
|
|
* @param datestr date string (e.g. 'Wed Jan 1 1997')
|
|
* @retval secs secs since the unix epoch
|
|
* @return 0 on success, -1 on error
|
|
*/
|
|
static int dateToTimet(const char * datestr, /*@out@*/ time_t * secs)
|
|
/*@modifies *secs @*/
|
|
{
|
|
struct tm time;
|
|
char * p, * pe, * q, ** idx;
|
|
char * date = strcpy(alloca(strlen(datestr) + 1), datestr);
|
|
/*@observer@*/ static char * days[] =
|
|
{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL };
|
|
/*@observer@*/ static char * months[] =
|
|
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL };
|
|
/*@observer@*/ static char lengths[] =
|
|
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
|
|
|
memset(&time, 0, sizeof(time));
|
|
|
|
pe = date;
|
|
|
|
/* day of week */
|
|
p = pe; SKIPSPACE(p);
|
|
if (*p == '\0') return -1;
|
|
pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
|
|
for (idx = days; *idx && strcmp(*idx, p); idx++)
|
|
{};
|
|
if (*idx == NULL) return -1;
|
|
|
|
/* month */
|
|
p = pe; SKIPSPACE(p);
|
|
if (*p == '\0') return -1;
|
|
pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
|
|
for (idx = months; *idx && strcmp(*idx, p); idx++)
|
|
{};
|
|
if (*idx == NULL) return -1;
|
|
time.tm_mon = idx - months;
|
|
|
|
/* day */
|
|
p = pe; SKIPSPACE(p);
|
|
if (*p == '\0') return -1;
|
|
pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
|
|
|
|
/* make this noon so the day is always right (as we make this UTC) */
|
|
time.tm_hour = 12;
|
|
|
|
time.tm_mday = strtol(p, &q, 10);
|
|
if (!(q && *q == '\0')) return -1;
|
|
if (time.tm_mday < 0 || time.tm_mday > lengths[time.tm_mon]) return -1;
|
|
|
|
/* year */
|
|
p = pe; SKIPSPACE(p);
|
|
if (*p == '\0') return -1;
|
|
pe = p; SKIPNONSPACE(pe); if (*pe != '\0') *pe++ = '\0';
|
|
time.tm_year = strtol(p, &q, 10);
|
|
if (!(q && *q == '\0')) return -1;
|
|
if (time.tm_year < 1997 || time.tm_year >= 3000) return -1;
|
|
time.tm_year -= 1900;
|
|
|
|
*secs = mktime(&time);
|
|
if (*secs == -1) return -1;
|
|
|
|
/* adjust to GMT */
|
|
*secs += timezone;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Add %changelog section to header.
|
|
* @param h header
|
|
* @param sb changelog strings
|
|
* @return 0 on success
|
|
*/
|
|
static int addChangelog(Header h, StringBuf sb)
|
|
/*@modifies h @*/
|
|
{
|
|
char *s;
|
|
int i;
|
|
time_t time;
|
|
time_t lastTime = 0;
|
|
char *date, *name, *text, *next;
|
|
|
|
s = getStringBuf(sb);
|
|
|
|
/* skip space */
|
|
SKIPSPACE(s);
|
|
|
|
while (*s != '\0') {
|
|
if (*s != '*') {
|
|
rpmError(RPMERR_BADSPEC,
|
|
_("%%changelog entries must start with *\n"));
|
|
return RPMERR_BADSPEC;
|
|
}
|
|
|
|
/* find end of line */
|
|
date = s;
|
|
while(*s && *s != '\n') s++;
|
|
if (! *s) {
|
|
rpmError(RPMERR_BADSPEC, _("incomplete %%changelog entry\n"));
|
|
return RPMERR_BADSPEC;
|
|
}
|
|
/*@-modobserver@*/
|
|
*s = '\0';
|
|
/*@=modobserver@*/
|
|
text = s + 1;
|
|
|
|
/* 4 fields of date */
|
|
date++;
|
|
s = date;
|
|
for (i = 0; i < 4; i++) {
|
|
SKIPSPACE(s);
|
|
SKIPNONSPACE(s);
|
|
}
|
|
SKIPSPACE(date);
|
|
if (dateToTimet(date, &time)) {
|
|
rpmError(RPMERR_BADSPEC, _("bad date in %%changelog: %s\n"), date);
|
|
return RPMERR_BADSPEC;
|
|
}
|
|
if (lastTime && lastTime < time) {
|
|
rpmError(RPMERR_BADSPEC,
|
|
_("%%changelog not in descending chronological order\n"));
|
|
return RPMERR_BADSPEC;
|
|
}
|
|
lastTime = time;
|
|
|
|
/* skip space to the name */
|
|
SKIPSPACE(s);
|
|
if (! *s) {
|
|
rpmError(RPMERR_BADSPEC, _("missing name in %%changelog\n"));
|
|
return RPMERR_BADSPEC;
|
|
}
|
|
|
|
/* name */
|
|
name = s;
|
|
while (*s != '\0') s++;
|
|
while (s > name && xisspace(*s)) {
|
|
*s-- = '\0';
|
|
}
|
|
if (s == name) {
|
|
rpmError(RPMERR_BADSPEC, _("missing name in %%changelog\n"));
|
|
return RPMERR_BADSPEC;
|
|
}
|
|
|
|
/* text */
|
|
SKIPSPACE(text);
|
|
if (! *text) {
|
|
rpmError(RPMERR_BADSPEC, _("no description in %%changelog\n"));
|
|
return RPMERR_BADSPEC;
|
|
}
|
|
|
|
/* find the next leading '*' (or eos) */
|
|
s = text;
|
|
do {
|
|
s++;
|
|
} while (*s && (*(s-1) != '\n' || *s != '*'));
|
|
next = s;
|
|
s--;
|
|
|
|
/* backup to end of description */
|
|
while ((s > text) && xisspace(*s)) {
|
|
*s-- = '\0';
|
|
}
|
|
|
|
addChangelogEntry(h, time, name, text);
|
|
s = next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int parseChangelog(Spec spec)
|
|
{
|
|
int nextPart, res, rc;
|
|
char *fmt_name = NULL, *fmt_text = NULL, *fmt_time = NULL;
|
|
StringBuf sb = newStringBuf();
|
|
|
|
/* There are no options to %changelog */
|
|
if ((rc = readLine(spec, STRIP_COMMENTS)) == 1) {
|
|
sb = freeStringBuf(sb);
|
|
return PART_NONE;
|
|
}
|
|
if (rc)
|
|
return rc;
|
|
|
|
while (! (nextPart = isPart(spec->line))) {
|
|
appendStringBuf(sb, spec->line);
|
|
if ((rc = readLine(spec, STRIP_COMMENTS)) == 1) {
|
|
nextPart = PART_NONE;
|
|
break;
|
|
}
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
|
|
if ((fmt_name = getenv("RPM_ADD_CHANGELOG_NAME"))
|
|
&& (fmt_text = getenv("RPM_ADD_CHANGELOG_TEXT"))
|
|
&& (fmt_time = getenv("RPM_ADD_CHANGELOG_TIME"))) {
|
|
const char *errstr = NULL;
|
|
char *end = NULL;
|
|
time_t time;
|
|
long long ltime = strtoll(fmt_time, &end, 0);
|
|
|
|
const long long tmax = (1ULL << (sizeof(size_t) * 8 - 1)) - 1;
|
|
if (ltime > 0 && ltime < tmax && !*end) {
|
|
time = ltime;
|
|
} else {
|
|
rpmlog(RPMLOG_WARNING, "RPM_ADD_CHANGELOG_TIME cannot be converted from %s.\n", fmt_time);
|
|
time = 0;
|
|
}
|
|
|
|
char *name = headerSprintf(spec->packages->header, fmt_name, rpmTagTable, rpmHeaderFormats, &errstr);
|
|
if (!name)
|
|
rpmlog(RPMLOG_WARNING, "RPM_ADD_CHANGELOG_NAME: %s\n", errstr);
|
|
|
|
char *text = headerSprintf(spec->packages->header, fmt_text, rpmTagTable, rpmHeaderFormats, &errstr);
|
|
if (!text)
|
|
rpmlog(RPMLOG_WARNING, "RPM_ADD_CHANGELOG_TEXT: %s\n", errstr);
|
|
|
|
if (time != 0 && name && text)
|
|
addChangelogEntry(spec->packages->header, time, name, text);
|
|
|
|
free(name);
|
|
free(text);
|
|
} else if (fmt_name || fmt_text || fmt_time) {
|
|
rpmlog(RPMLOG_WARNING,
|
|
"Each of RPM_ADD_CHANGELOG_NAME, RPM_ADD_CHANGELOG_TEXT and RPM_ADD_CHANGELOG_TIME"
|
|
" environment variables should be set to add a changelog entry.\n");
|
|
}
|
|
|
|
res = addChangelog(spec->packages->header, sb);
|
|
sb = freeStringBuf(sb);
|
|
|
|
return (res) ? res : nextPart;
|
|
}
|