rpm-build/build/parseChangelog.c
Vladimir D. Seleznev d831a6ed9a Add support for $RPM_ADD_CHANGELOG_{NAME,TEXT,TIME}
If all these environment variables are set, an entry based on their
contents is added to the changelog of source and all binary packages.
2018-09-24 18:07:32 +03:00

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;
}