distromatic/src/distromatic.c
Silvan Calarco e07f68435d distromatic: fixes to obsoletes detection:
- ignore obsoletes from obsoleted packages
- ignore obsoletes from updated or updating packages which apply to same {up/down}stream source build packages
2013-09-04 16:53:22 +02:00

1531 lines
60 KiB
C

/*
* distromatic - tool for RPM based repositories
*
* Copyright (C) 2004-2013 by Silvan Calarco <silvan.calarco@mambasoft.it>
* Copyright (C) 2006 by Davide Madrisan <davide.madrisan@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of version 2 of the GNU General Public License as published by the
* Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY, to the extent permitted by law; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <getopt.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <dirent.h>
#include <sys/stat.h>
#if HAVE_STRING_H
# if !STDC_HEADERS && HAVE_MEMORY_H
# include <memory.h>
# endif
/* Tell glibc's <string.h> to provide a prototype for strndup() */
# ifndef __USE_GNU
# define __USE_GNU
# endif
# include <string.h>
#endif
#if HAVE_STRINGS_H
# include <strings.h>
#endif
/* Tell glibc's <time.h> to provide a prototype for strptime() */
#ifndef __USE_XOPEN
# define __USE_XOPEN
#endif
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#if !HAVE_STRCHR
# define strchr index
#endif
#include "buildtools.h"
#include "changelog.h"
#include "distromatic.h"
#include "reports.h"
#include "headerlist.h"
#include "requirelist.h"
#include "functions.h"
#include "rpmfunctions.h"
#include "backend-sqlite3.h"
#define MODE_DATA_TABLES 1
#define MODE_FIND_DEPS 2
#define MODE_CHANGELOG 4
#define MODE_HTML 8
#define MODE_GENSRCPKGLIST 16
#define MODE_GENBUILDINFO 32
#define MODE_GENPKGLIST 64
#define MODE_SQLITE3 128
static void program_usage(int exit_code);
static void program_version(void);
static int resolveFirstLevelDependencies(
struct configTag *ct, int arch);
static int
resolveFirstLevelSourceDependencies(struct configTag *ct, int archidx);
static int clearRecursionFlag(struct headerList *headerlist);
static int resolveRecursiveDependencies(
struct headerList *headerlist);
struct configTag*
findRepositoryByTag(const char *tag);
static int read_configuration(const char *confFile, const char *tag);
int compareRequiredList(const void *ptr1, const void *ptr2);
int handleObsoletedPackages(struct configTag *ct, int arch);
static struct configDefaults configdefaults;
static struct configTag *firstconfigtag = NULL, *configtag = NULL;
static const unsigned int bufsize = 1024;
static const unsigned int maxlinelenght = 1024;
static const char *copyright[] = {
PROGRAMNAME " version " PROGRAMVERSION,
"Copyright (C) 2004-2013 by Silvan Calarco <silvan.calarco@mambasoft.it>",
"Copyright (C) 2006 by Davide Madrisan <davide.madrisan@gmail.com>",
(char *)0
};
unsigned int quietmode = 0;
unsigned int genheader_mode = GENHEADER_BASE + GENHEADER_REQUIREMENTS;
unsigned int mode = 0;
unsigned int recursive_mode = 0;
unsigned int obsolete_packages = 1;
/* *INDENT-OFF* */
static const char *freelicense[] = {
"This is free software; see the source for copying conditions. There is NO",
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.",
(char *)0
};
static const char *helpmsg[] = {
"Usage:",
"distromatic [options] [command [arguments] ]...",
"",
"Commands:",
" --gendatatables write dependencies, buildinfo and apt db files",
" --genbuildinfo generate build info files for all SRPMS packages",
" --genhtml generate HTML code for repository",
" --genpkglist generate binary packages list with version and size",
" --gensrcpkglist generate a source packages list with version",
" --gensqlite3 dump data to an SQLite3 database (EXPERIMENTAL)",
" --find-deps <package> find dependencies for given package name",
" --changelog <package> print changelog for given package name",
" --changelogsince <mmddyy> print changelog for all packages since given date",
"",
"Options:",
" -t, --tag use repository identified by tag in config file",
" -a, --arch specify a target architecture (default: " DEFAULT_ARCH ")",
" -c, --conf <file> specify configuration file (default: " DEFAULT_CONFIGFILE ")",
" -q, --quiet suppress all messages excluding warnings and errors",
" -d, --debug display debug level messages",
" -v, --version display version and exit",
"",
"Examples:",
" distromatic -t devel --genhtml --deps-table",
" distromatic -t stable/2.0 --changelogsince 010106",
"",
"Please report bugs to <"PACKAGE_BUGREPORT">.",
(char *)0
};
/* *INDENT-ON* */
static void
program_usage(int exit_code) {
int linenum;
for (linenum = 0; copyright[linenum]; linenum++)
printf("%s\n", copyright[linenum]);
printf("\n");
for (linenum = 0; helpmsg[linenum]; linenum++)
printf("%s\n", helpmsg[linenum]);
exit(exit_code);
}
static void
program_version(void) {
int linenum;
for (linenum = 0; copyright[linenum]; linenum++)
printf("%s\n", copyright[linenum]);
printf("\n");
for (linenum = 0; freelicense[linenum]; linenum++)
printf("%s\n", freelicense[linenum]);
}
/* Obtain a backtrace and print it to stdout. */
void backtraceHandler(int sig)
{
void *array[10];
size_t size;
char **strings;
size_t i;
size = backtrace (array, 10);
strings = (char**)backtrace_symbols (array, size);
fprintf(stderr, "Obtained %zd stack frames.\n", size);
for (i = 0; i < size; i++)
fprintf(stderr, "%s\n", strings[i]);
free (strings);
exit(1);
}
/*
* checks if given single requirement is met by given provides
*
* returns: 0 if check is unsuccessful
* 1 if successful
*
* FIXME: this just checks for names equality, not for version */
static int
checkRequireWithProvides(char *requirename,
struct headerList *provideheader)
{
int j;
/* assuming requirename is a library or a virtual package */
for (j = 0; j < provideheader->providecount; j++) {
if (!strcmp
(provideheader->provided[j]->name,requirename)) return 1;
}
return 0;
}
int
compareRequiredList(const void *ptr1, const void *ptr2)
{
#define require1 (*(struct Require**)ptr1)
#define require2 (*(struct Require**)ptr2)
if (!require1 && !require2) return 0;
if (!require1 || !require1->name) return 1;
if (!require2 || !require2->name) return -1;
/* if (require1->resolved->numproviders != require2->resolved->numproviders)
return require2->resolved->numproviders - require1->resolved->numproviders;
if (require1->resolved->numproviders == 1)
return strcmp(require1->resolved->provider[0]->name,require2->resolved->provider[0]->name);
else*/
return strcmp(require1->name,require2->name);
}
/*
* inspect multiple providing packages in providedList
* and remove providers that are obsoleted by others
*/
int
handleObsoletedPackages(struct configTag *ct, int archidx)
{
struct providedList *prov = ct->providedlist_idx[archidx][0];
int i,j,k;
int obs[256];
char buf[PATH_MAX];
struct headerList *currheader;
while (prov) {
if (prov->numproviders > 256) {
return 1;
}
if (prov->numproviders > 1) {
for (i = 0; i < prov->numproviders; i++) obs[i]=-1;
for (i = 0; i < prov->numproviders; i++)
for (j = 0; j < prov->provider[i]->obsoletecount; j++)
for (k = 0; k < prov->numproviders; k++)
if (!strcmp(prov->provider[i]->obsoletename[j],prov->provider[k]->name)
&& strcmp(prov->provider[i]->name,prov->provider[k]->name)) {
if (prov->provider[i]->obsoleteflags[j] & (RPMSENSE_EQUAL|RPMSENSE_GREATER|RPMSENSE_LESS)) {
snprintf(buf, PATH_MAX, "%s-%s",prov->provider[k]->version,prov->provider[k]->release);
if (!checkVersionWithFlags(
prov->provider[i]->obsoleteversion[j],
prov->provider[i]->obsoleteflags[j],
buf)) continue;
}
/* print 'obsoletes' warning for binary packages belonging to target repository,
and 'obsoleted by' for all obsoletions in upper level repositories */
if (prov->provider[k]->altrepository != ct->repository_level) {
currheader = prov->provider[k]->sourceheader->firstchild[archidx];
while (currheader) {
if (!strcmp(currheader->name, prov->provider[k]->name)) {
prov->provider[k]->obsoleted = 1;
snprintf(buf, PATH_MAX, "%s(%s,%s) obsoleted by %s(%s,%s)",
prov->provider[k]->name,
prov->provider[k]->arch,
ct->repository[prov->provider[k]->altrepository]->tag,
prov->provider[i]->name,
prov->provider[i]->arch,
ct->repository[prov->provider[i]->altrepository]->tag);
addWarning(prov->provider[k]->sourceheader, buf);
logmsg(LOG_WARNING,"%s", buf);
break;
}
currheader = currheader -> nextbrother;
}
} else {
prov->provider[k]->obsoleted = 1;
snprintf(buf, PATH_MAX, "%s(%s,%s) obsoletes %s(%s,%s)",
prov->provider[i]->name,
prov->provider[i]->arch,
ct->repository[prov->provider[i]->altrepository]->tag,
prov->provider[k]->name,
prov->provider[k]->arch,
ct->repository[prov->provider[k]->altrepository]->tag);
addWarning(prov->provider[i]->sourceheader, buf);
logmsg(LOG_WARNING,"%s", buf);
}
obs[k]=i;
}
// now delete found obsoleted providers
j = prov->numproviders;
for (i = 0; i < prov->numproviders; i++) {
if (obs[i] >=0) {
j--;
prov->provider[i]->obsoleted=1;
prov->provider[i]=NULL;
}
}
if (j < prov->numproviders) {
for (i = 0; i < j; i++) {
if (prov->provider[i] == NULL) {
k=i+1;
while (k < prov->numproviders && !prov->provider[k]) k++;
prov->provider[i]=prov->provider[k];
prov->provider[k]=NULL;
}
}
prov->numproviders = j;
}
}
prov=prov->next;
}
/* mark obsoleted elements which are provided by other packages */
currheader = ct->headerlist[archidx];
while (currheader) {
if (currheader->obsoleted == 0)
for (j = 0; j < currheader->obsoletecount; j++) {
prov=findOrCreateProvidedListEntry((struct providedList**) &ct->providedlist_idx[archidx],
currheader->obsoletename[j],0,archidx);
if (prov) {
for (i = 0; i < prov->numproviders; i++) {
if (strcmp(currheader->name,prov->provider[i]->name)) {
// if (currheader->sourceheader != prov->provider[i]->sourceheader) {
if (prov->provider[i]->sourceheader == currheader->sourceheader) {
snprintf(buf, PATH_MAX, "%s(%s,%s) obsoletes %s provided by %s(%s,%s) which comes from its own source; ignoring",
currheader->name,
currheader->arch,
ct->repository[currheader->altrepository]->tag,
prov->name,
prov->provider[i]->name,
prov->provider[i]->arch,
ct->repository[prov->provider[i]->altrepository]->tag);
} else if (!strcmp(prov->provider[i]->sourceheader->name,currheader->sourceheader->name)) {
snprintf(buf, PATH_MAX, "%s(%s,%s) obsoletes %s provided by %s(%s,%s) which comes from same source name; ignoring",
currheader->name,
currheader->arch,
ct->repository[currheader->altrepository]->tag,
prov->name,
prov->provider[i]->name,
prov->provider[i]->arch,
ct->repository[prov->provider[i]->altrepository]->tag);
} else {
prov->provider[i]->obsoleted = 1;
if (!strcmp(prov->name,prov->provider[i]->name)) {
snprintf(buf, PATH_MAX, "%s(%s,%s) obsoletes %s(%s,%s)",
currheader->name,
currheader->arch,
ct->repository[currheader->altrepository]->tag,
prov->provider[i]->name,
prov->provider[i]->arch,
ct->repository[prov->provider[i]->altrepository]->tag);
} else {
snprintf(buf, PATH_MAX, "%s(%s,%s) obsoletes %s provided by %s(%s,%s)",
currheader->name,
currheader->arch,
ct->repository[currheader->altrepository]->tag,
prov->name,
prov->provider[i]->name,
prov->provider[i]->arch,
ct->repository[prov->provider[i]->altrepository]->tag);
}
}
addWarning(currheader->sourceheader, buf);
logmsg(LOG_WARNING,"%s", buf);
}
}
}
}
currheader = currheader->next;
}
return 0;
}
/*
* resolve first level requires for given headerList
* note: this function doesn't free allocated memory so it should be called
* only once */
static int
resolveFirstLevelDependencies(struct configTag *ct, int archidx)
{
struct headerList *currheader, *scanheader, **newprovider;
struct providedList *provided;
struct fileTree *file;
int i,j,k,found;
char warning[PATH_MAX];
currheader = ct->headerlist[archidx];
logmsg(LOG_DEBUG,"resolveFirstLevelDependencies - binaries");
while (currheader) {
scanheader = ct->headerlist[archidx];
currheader->requirelist = NULL;
if ((!currheader->obsoleted) && (currheader->next) && (currheader->sourceheader->updatingparent)) {
// mark obsoleted any package with same name in upper level repositories
if (currheader->altrepository < currheader->sourceheader->updatingparent->altrepository) {
currheader->obsoleted = 1;
if (checkVersionWithFlags(
currheader->version,
RPMSENSE_GREATER & RPMSENSE_EQUAL,
currheader->next->version)) {
snprintf(warning,PATH_MAX,"%s(%s,%s): same or higher version than package with same name in %s (%s >= %s)",
currheader->name,
ct->arch[archidx],
ct->repository[currheader->altrepository]->tag,
ct->repository[currheader->next->altrepository]->tag,
currheader->version,
currheader->next->version);
fprintf(stderr,"Warning: %s\n",warning);
addWarning(currheader->sourceheader,warning);
}
} else if (currheader->altrepository >= currheader->sourceheader->updatingparent->altrepository) {
currheader->next->obsoleted = 1;
if (checkVersionWithFlags(
currheader->version,
RPMSENSE_LESS & RPMSENSE_EQUAL,
currheader->next->version)) {
snprintf(warning,PATH_MAX,"%s(%s,%s): same or higher version than package with same name in %s (%s >= %s)",
currheader->next->name,
ct->arch[archidx],
ct->repository[currheader->next->altrepository]->tag,
ct->repository[currheader->altrepository]->tag,
currheader->next->version,
currheader->version);
fprintf(stderr,"Warning: %s\n",warning);
addWarning(currheader->sourceheader,warning);
}
}
}
if (currheader->obsoleted || currheader->sourceheader->updatingparent) {
currheader = currheader->next;
continue;
}
// currheader->require.resolved = malloc(sizeof(struct providedList*)*currheader->requirecount);
for (i = 0; i < currheader->requirecount; i++) {
if (!strncmp("executable(",currheader->require[i]->name,11)) {
/* dynamic requirement for executable file */
/* fprintf(stderr,"Warning: skipping unhandled requirement %s for package %s\n",
currheader->require[i]->name,currheader->name);*/
currheader->require[i]->resolved=NULL;
} else if (strncmp("rpmlib(",currheader->require[i]->name,7) != 0) {
provided=findOrCreateProvidedListEntry((struct providedList**) &ct->providedlist_idx[archidx],
currheader->require[i]->name,1,archidx);
if (provided->numproviders == 0) {
// check if require[i]->name requirement is met
scanheader = ct->headerlist[archidx];
if ((currheader->require[i]->name)[0] == '/') {
/* requirement is a file, find who provides it */
file=findOrCreateFileTreeEntry(&ct->filetree[archidx],currheader->require[i]->name);
if (file->numproviders > 0) {
scanheader=file->provider[0];
provided->numproviders=file->numproviders;
provided->numbuildproviders=0;
provided->buildpriority=0;
provided->provider=file->provider;
provided->flags=0;
} else {
scanheader=NULL;
}
} else {
/* requirement is not a file, cross-check with provides */
while (scanheader &&
!checkRequireWithProvides(
currheader->require[i]->name,
scanheader)) scanheader = scanheader->next;
if (scanheader) {
provided->numproviders=1;
provided->numbuildproviders=0;
provided->buildpriority=0;
provided->provider=malloc(sizeof(struct headerList*));
provided->provider[0]=scanheader;
provided->flags=0;
}
}
if (!scanheader) {
snprintf(warning,PATH_MAX,"%s(%s,%s): missing provider for %s",
currheader->name,
ct->arch[archidx],
ct->repository[currheader->altrepository]->tag,
currheader->require[i]->name);
fprintf(stderr,"Warning: %s\n",warning);
addWarning(currheader->sourceheader,warning);
}
} else { /* provided->numproviders > 0 */
/* warn about provides only provided by older packages */
k=0;
for (j = 0; j < provided->numproviders; j++) {
if (provided->provider[j]->sourceheader->updatingparent &&
provided->provider[j]->sourceheader->updatingparent->firstchild[archidx] && /* skip if not built for arch */
!currheader->obsoleted) k++;
}
if (k == provided->numproviders) { // all provides are from older packages
snprintf(warning, PATH_MAX, "%s(%s,%s) requires %s which is only provided by older package(s):",
currheader->name,
currheader->arch,
ct->repository[currheader->altrepository]->tag,
provided->name);
for (j = 0; j < provided->numproviders; j++) {
snprintf(&warning[strlen(warning)], PATH_MAX - strlen(warning), " %s(%s,%s)",
provided->provider[j]->name,
provided->provider[j]->arch,
ct->repository[provided->provider[j]->altrepository]->tag);
}
fprintf(stderr,"Warning: %s\n",warning);
for (j = 0; j < provided->numproviders; j++) {
addWarning(provided->provider[j]->sourceheader,warning);
}
} else {
/* warn about provides provided by obsoleted packages */
k=0;
for (j = 0; j < provided->numproviders; j++) {
if (provided->provider[j]->obsoleted &&
!provided->provider[j]->sourceheader->updatingparent && // don't fall in case above
!currheader->obsoleted) k++;
}
if (k == provided->numproviders) { // all provides are obsoleted
snprintf(warning, PATH_MAX, "%s(%s,%s) requires %s which is only provided by obsoleted package(s):",
currheader->name,
currheader->arch,
ct->repository[currheader->altrepository]->tag,
provided->name);
for (j = 0; j < provided->numproviders; j++) {
snprintf(&warning[strlen(warning)], PATH_MAX - strlen(warning), " %s(%s,%s)",
provided->provider[j]->name,
provided->provider[j]->arch,
ct->repository[provided->provider[j]->altrepository]->tag);
}
fprintf(stderr,"Warning: %s\n",warning);
addWarning(currheader->sourceheader,warning);
for (j = 0; j < provided->numproviders; j++) {
addWarning(provided->provider[j]->sourceheader,warning);
}
}
}
if ((currheader->require[i]->name)[0] == '/') {
/* when there is a Requires: /file/requirement add provide from file tree as well */
file=findOrCreateFileTreeEntry(&ct->filetree[archidx],currheader->require[i]->name);
for (k = 0; k < file->numproviders; k++) {
for (j = 0, found = 0; j < provided->numproviders; j++) {
/* avoid duplicates */
if (file->provider[k] == provided->provider[j]) {
found = 1;
break;
}
}
if (! found) {
//printf("%s also provided by %s\n", currheader->require[i]->name, file->provider[k]->name);
provided->numproviders++;
newprovider=malloc(sizeof(struct headerList*)*provided->numproviders);
for (j = 0; j < provided->numproviders-1; j++) {
newprovider[j]=provided->provider[j];
}
newprovider[provided->numproviders-1] = file->provider[k];
free(provided->provider);
provided->provider=newprovider;
}
}
}
}
if (provided->numversions > 0) {
if (strcmp(currheader->require[i]->version,"") &&
(currheader->require[i]->flags & (RPMSENSE_LESS|RPMSENSE_GREATER|RPMSENSE_EQUAL))) {
found = 0;
for (j = 0; j < provided->numversions; j++) {
if (!strcmp(provided->version[j],"")) {
/* provider with no version; assume ok */
found = 1;
} else {
if (checkVersionWithFlags(
currheader->require[i]->version,
currheader->require[i]->flags,
provided->version[j])) found = 1;
}
} /* for */
if (!found) {
for (j = 0; j < provided->numversions; j++) {
snprintf(warning, PATH_MAX, "%s = %s fails to provide %s ",
provided->name,
provided->version[j],
provided->name);
if (currheader->require[i]->flags & RPMSENSE_LESS) snprintf(&warning[strlen(warning)], PATH_MAX,"<");
if (currheader->require[i]->flags & RPMSENSE_GREATER) snprintf(&warning[strlen(warning)], PATH_MAX, ">");
if (currheader->require[i]->flags & RPMSENSE_EQUAL) snprintf(&warning[strlen(warning)], PATH_MAX, "=");
snprintf(&warning[strlen(warning)], PATH_MAX, " %s to %s(%s,%s)",
currheader->require[i]->version,
currheader->name,
currheader->arch,
ct->repository[currheader->altrepository]->tag);
for (k = 0; k < provided->numproviders; k++) {
if (provided->provider[k]->sourceheader &&
(provided->provider[k]->altrepository == ct->repository_level)) {
fprintf(stderr,"Warning: %s\n", warning);
addWarning(provided->provider[k]->sourceheader, warning);
}
}
}
}
}
}
currheader->require[i]->resolved=provided;
} else {
currheader->require[i]->resolved=NULL;
}
}
// sort required list by first provider's name
qsort((void *) &currheader->require[0],
currheader->requirecount,
sizeof(struct Require *),
compareRequiredList);
currheader = currheader->next;
}
logmsg(LOG_DEBUG,"resolveFirstLevelDependencies - done");
return 0;
}
static int
resolveFirstLevelSourceDependencies(struct configTag *ct, int archidx)
{
struct headerSourceList *currsourceheader = ct->headersourcelist;
struct headerList *scanheader;
struct providedList *provided;
struct fileTree *file;
char warning[PATH_MAX];
int i;
int j,found;
logmsg(LOG_DEBUG,"resolveFirstLevelSourceDependencies - sources");
while (currsourceheader) {
if (!currsourceheader->firstchild[archidx]) { // ignore SRPMs with no builds
currsourceheader = currsourceheader->next;
continue;
}
scanheader = ct->headerlist[archidx];
currsourceheader->requirelist = NULL;
// currsourceheader->require.resolved = malloc(sizeof(struct providedList*)*currsourceheader->requirecount);
for (i = 0; i < currsourceheader->requirecount; i++) {
/* if (strncmp("a2ps",currsourceheader->name,4) == 0) {
fprintf(stderr,"a2ps:%s\n",currheader->require[i]->name);
}*/
if (strncmp("rpmlib(",currsourceheader->require[i]->name,7) != 0) {
provided=findOrCreateProvidedListEntry((struct providedList**) &ct->providedlist_idx[archidx],
currsourceheader->require[i]->name,1,archidx);
if (provided->numbuildproviders == 0) {
// check if require[i]->name requirement is met
if ((currsourceheader->require[i]->name)[0] == '/') {
/* requirement is a file, find who provides it */
file=findOrCreateFileTreeEntry(&ct->filetree[archidx],currsourceheader->require[i]->name);
if (file->numproviders > 0) {
provided->numbuildproviders=file->numproviders;
provided->buildpriority=0;
provided->buildprovider=file->provider;
provided->flags=0;
logmsg(LOG_DEBUG,"file %s is a build provider for %s",currsourceheader->require[i]->name,currsourceheader->name);
}
} else {
/* requirement is not a file, cross-check with provides */
scanheader = ct->headerlist[archidx];
while (scanheader) {
if (checkRequireWithProvides(
currsourceheader->require[i]->name,
scanheader)) provided->numbuildproviders+=1;
scanheader = scanheader->next;
}
if (provided->numbuildproviders > 0) {
provided->buildprovider = malloc(sizeof(struct headerList*) * provided->numbuildproviders);
provided->flags = 0;
scanheader = ct->headerlist[archidx];
j = 0;
while (scanheader) {
if (checkRequireWithProvides(
currsourceheader->require[i]->name,
scanheader)) provided->buildprovider[j++] = scanheader;
scanheader = scanheader->next;
}
logmsg(LOG_DEBUG,"%s is a build provider for %s",provided->name,currsourceheader->name);
}
}
if (provided->numbuildproviders == 0) {
snprintf(warning,PATH_MAX,"missing build provider for %s",currsourceheader->require[i]->name);
fprintf(stderr,"Warning: %s(source,%s): %s\n",
currsourceheader->name,
ct->repository[currsourceheader->altrepository]->tag,
warning);
addWarning(currsourceheader,warning);
}
//} else if (provided->numbuildproviders > 1) {
/*printf("Multiple providers for %s in package %s\n",currsourceheader->require[i]->name,currsourceheader->name);*/
}
if (provided->numbuildproviders > 0) {
if (strcmp(currsourceheader->require[i]->version,"") &&
(currsourceheader->require[i]->flags & (RPMSENSE_LESS+RPMSENSE_GREATER+RPMSENSE_EQUAL))) {
found = 0;
for (j = 0; j < provided->numversions; j++) {
if (!strcmp(provided->version[j],"")) {
/* provider with no version; assume ok */
found = 1;
} else {
if (checkVersionWithFlags(
currsourceheader->require[i]->version,
currsourceheader->require[i]->flags,
provided->version[j])) found = 1;
}
} /* for */
if (!found) {
if (currsourceheader->altrepository == ct->repository_level) {
fprintf(stderr,"Warning: %s(source): build requires %s ",currsourceheader->name,currsourceheader->require[i]->name);
if (currsourceheader->require[i]->flags & RPMSENSE_LESS) fprintf(stderr,"<");
if (currsourceheader->require[i]->flags & RPMSENSE_GREATER) fprintf(stderr,">");
if (currsourceheader->require[i]->flags & RPMSENSE_EQUAL) fprintf(stderr,"=");
fprintf(stderr," %s (failing build provider(s):", currsourceheader->require[i]->version);
for (j = 0; j < provided->numbuildproviders; j++) {
fprintf(stderr," %s#%s",
provided->provider[j]->name, provided->version[j]);
/* printrpmversion(buffer,PATH_MAX,
provided->provider[j]->epoch,
provided->provider[j]->version,
provided->provider[j]->release));*/
}
fprintf(stderr,")\n");
}
}
}
}
currsourceheader->require[i]->resolved=provided;
} else {
currsourceheader->require[i]->resolved=NULL;
}
}
// sort required list by first provider's name
qsort((void *) &currsourceheader->require[0],
currsourceheader->requirecount,
sizeof(struct Require *),
compareRequiredList);
currsourceheader = currsourceheader->next;
}
logmsg(LOG_DEBUG,"resolveFirstLevelSourceDependencies - done");
return 0;
}
static int
clearRecursionFlag(struct headerList *headerlist)
{
while (headerlist) {
if (headerlist->recursed == 1) {
headerlist->recursed = 0;
}
headerlist = headerlist->next;
}
return 0;
}
/*
* resolve recursive requirement dependencies
* (first level dependencies should have been resolved already) */
static int
resolveRecursiveDependencies(struct headerList *headerlist)
{
while (headerlist) {
clearRecursionFlag(headerlist);
headerlist->requirelist = recurseRequireList(headerlist);
headerlist->recursed = 2;
headerlist = headerlist->next;
}
return 0;
}
struct configTag*
findRepositoryByTag(const char *tag)
{
struct configTag *ct = firstconfigtag;
while ((ct) && (strncmp(ct->tag,tag,PATH_MAX))) ct = ct->next;
if (ct) return ct; else return NULL;
}
static int
read_configuration(const char *confFile, const char *tag)
{
FILE *fin;
char *vartok, *valuetok;
char input[maxlinelenght];
char buf[bufsize];
int i, j, curraltrep = 0;
struct configTag *newconfigtag, *currconfigtag = NULL;
struct Packager *currmaintainer;
if ((fin = fopen(confFile, "r")) == NULL) {
return 1;
}
unsigned int configsection = 0;
configdefaults.html_basedir = NULL;
configdefaults.arch[0] = DEFAULT_ARCH;
for (i = 1; i<ARCHS_MAX; i++)
configdefaults.arch[i] = NULL;
while (fgets(input, maxlinelenght, fin)) {
if (input[0] == '#') {
continue;
}
vartok = (char *) strtok(input, "=\n");
if (!vartok) { /* skip blank lines */
continue;
}
strip_separators(vartok, " \t\n");
valuetok = (char *) strtok(NULL, "\n");
strip_separators(valuetok, " \t\n");
if ((vartok[0]=='[') && (vartok[strlen(vartok)-1]==']')) {
/* found a `[defaults]' tag */
if (!strncmp(&vartok[1], "defaults", strlen(vartok)-2)) {
configsection = CONF_DEFAULTS_SECTION;
} else if (!strncmp(&vartok[1], "maintainers", strlen(vartok)-2)) {
configsection = CONF_MAINTAINERS_SECTION;
} else {
configsection = CONF_REP_SECTION;
newconfigtag = malloc(sizeof(struct configTag));
if (!newconfigtag) {
fprintf(stderr, "The system is out of memory\n");
return 1;
}
newconfigtag->tag =
(char *) strndup(&vartok[1], strlen(vartok) - 2);
newconfigtag->repository_dir = NULL;
newconfigtag->repository_source_dir = NULL;
curraltrep = 0;
for (i = 0; i<ALT_REPS_MAX; i++) newconfigtag->repository[i] = NULL;
newconfigtag->html_dir = NULL;
newconfigtag->download_prefix = NULL;
newconfigtag->download_dir = NULL;
newconfigtag->showfile_prefix = NULL;
for (i = 0; i<ARCHS_MAX; i++) {
newconfigtag->arch[i] = configdefaults.arch[i];
newconfigtag->headerlist[i] = NULL;
newconfigtag->filetree[i]=NULL;
for (j = 0; j<PROVIDEDLIST_IDX_SIZE; j++)
newconfigtag->providedlist_idx[i][j]=NULL;
}
newconfigtag->configdefaults = &configdefaults;
newconfigtag->headersourcelist = NULL;
if (configdefaults.html_basedir) {
strncpy(buf, configdefaults.html_basedir, bufsize);
strncat(buf, newconfigtag->tag, bufsize);
strncat(buf, "/", bufsize);
newconfigtag->html_dir = (char *) strdup(buf);
}
if ((!tag) || (!strcmp(newconfigtag->tag, tag))) {
configtag = newconfigtag;
}
if (!currconfigtag) {
firstconfigtag = newconfigtag;
} else {
currconfigtag->next = newconfigtag;
}
currconfigtag = newconfigtag;
}
} else if (configsection == CONF_REP_SECTION) {
/* we are reading inside a repository section... */
if (!strcmp(vartok, "REPOSITORY_DIR")) {
currconfigtag->repository_dir =
(char *) strdup(valuetok);
} else if (!strcmp(vartok, "PARENT")) {
currconfigtag->repository[curraltrep]=findRepositoryByTag(valuetok);
if (!currconfigtag->repository[curraltrep]) {
fprintf(stderr,"Error: repository %s requested in %s was not previously configured; aborting.\n",
valuetok, currconfigtag->tag);
exit(1);
}
curraltrep++;
} else if (!strcmp(vartok, "HTML_DIR")) {
currconfigtag->html_dir = (char *) strdup(valuetok);
} else if (!strcmp(vartok, "DOWNLOAD_PREFIX")) {
currconfigtag->download_prefix = (char *) strdup(valuetok);
} else if (!strcmp(vartok, "DOWNLOAD_DIR")) {
currconfigtag->download_dir = (char *) strdup(valuetok);
} else if (!strcmp(vartok, "SHOWFILE_PREFIX")) {
currconfigtag->showfile_prefix = (char *) strdup(valuetok);
} else if (!strcmp(vartok, "REPOSITORY_SOURCE_DIR")) {
currconfigtag->repository_source_dir =
(char *) strdup(valuetok);
} else if (!strcmp(vartok, "DESCRIPTION")) {
currconfigtag->description =
(char *) strdup(valuetok);
} else if (!strcmp(vartok, "ARCHS")) {
vartok = (char *) strtok(valuetok, " ");
i = 0;
while ((vartok) && (i < ARCHS_MAX)) {
currconfigtag->arch[i] = malloc(sizeof vartok);
if (!currconfigtag->arch[i]) {
fprintf(stderr, "The system is out of memory\n");
exit(1);
}
strcpy(currconfigtag->arch[i], vartok);
vartok = strtok(NULL, " ");
i++;
}
if (vartok) {
fprintf(stderr,
"Error: exceeding number of archs defined; maximum is %d.",ARCHS_MAX);
return 1;
} else {
currconfigtag->arch[i] = NULL;
}
} else {
fprintf(stderr, "Undefined token: %s\n", vartok);
return 1;
}
} else if (configsection == CONF_DEFAULTS_SECTION) {
/* we are in the default section */
if (!strcmp(vartok, "HTML_BASEDIR")) {
configdefaults.html_basedir =
(char *) strdup(valuetok);
} else if (!strcmp(vartok, "DISTRIBUTION_NAME")) {
configdefaults.distribution_name =
(char *) strdup(valuetok);
} else if (!strcmp(vartok, "URL_ADDRESS")) {
configdefaults.url_address =
(char *) strdup(valuetok);
} else if (!strcmp(vartok, "URL_PREFIX")) {
configdefaults.url_prefix =
(char *) strdup(valuetok);
} else if (!strcmp(vartok, "URL_DIR")) {
configdefaults.url_dir =
(char *) strdup(valuetok);
} else if (!strcmp(vartok, "ARCHS")) {
vartok = (char *) strtok(valuetok, " ");
i = 0;
while ((vartok) && (i < ARCHS_MAX)) {
configdefaults.arch[i] = malloc(sizeof vartok);
if (!configdefaults.arch[i]) {
fprintf(stderr, "The system is out of memory\n");
exit(1);
}
strcpy(configdefaults.arch[i], vartok);
vartok = strtok(NULL, " ");
i++;
}
if (vartok) {
fprintf(stderr,
"Error: exceeding number of archs defined; maximum is %d.",ARCHS_MAX);
return 1;
}
} else {
fprintf(stderr,
"Invalid token for `defaults' tag: %s\n",
vartok);
return 1;
}
} else if (configsection == CONF_MAINTAINERS_SECTION) {
/* find or create maintainer */
currmaintainer = getPackagerByName(vartok, 1);
if (!currmaintainer) {
fprintf(stderr,
"Error: cannot create maintainers; aborting.");
return 1;
}
currmaintainer->role |= PACKAGER_ROLE_MAINTAINER;
vartok = (char *) strtok(valuetok, "\"");
i = 0;
while ((currmaintainer->alias[i]) && (i < PACKAGER_MAXALIASES)) i++;
while ((vartok) && (i < PACKAGER_MAXALIASES)) {
currmaintainer->alias[i]=strdup(vartok);
vartok = strtok(NULL, "\"");
vartok = strtok(NULL, "\"");
i++;
}
if (vartok) {
fprintf(stderr,
"Error: exceeding number of aliases defined for maintainer %s.",currmaintainer->name);
return 1;
}
}
}
/* check for errors and complete previous tag infos */
for(currconfigtag = firstconfigtag; currconfigtag;
currconfigtag = currconfigtag->next) {
if (!currconfigtag->repository_dir) {
fprintf(stderr,
"REPOSITORY_DIR not given for tag %s\n",
currconfigtag->tag);
return 1;
}
if (!currconfigtag->repository_source_dir) {
strncpy(buf, currconfigtag->repository_dir, bufsize);
strncat(buf, "/SRPMS.base/", bufsize);
currconfigtag->repository_source_dir =
(char *) strdup(buf);
}
currconfigtag->repository_level=0;
for (i = 0; i < ALT_REPS_MAX; i++) {
if (currconfigtag->repository[i]) {
currconfigtag->repository_level=i+1;
}
}
currconfigtag->repository[currconfigtag->repository_level]=currconfigtag;
if (!currconfigtag->description)
currconfigtag->description=currconfigtag->tag;
}
fclose(fin);
return 0;
}
void *threadArchScan(void* arg) {
int arch = *(int *)arg;
if (generateHeaderList(configtag,arch)) {
fprintf(stderr,
"Fatal error: could not generate header list\n");
exit(1);
}
if (genheader_mode & GENHEADER_REQUIREMENTS) {
if (obsolete_packages) {
if (!quietmode) fprintf(stdout, "%s: handling obsoleted packages...\n",configtag->arch[arch]);
if (handleObsoletedPackages(configtag,arch)) {
fprintf(stderr,
"Fatal error: maximum number of multiple providing packages reached. Aborting.");
exit(1);
}
}
if (!quietmode) fprintf(stdout, "%s: resolving dependencies...\n",configtag->arch[arch]);
if (resolveFirstLevelDependencies(configtag, arch)) {
fprintf(stderr,
"Fatal error: could not generate first level requires table\n");
exit(1);
}
if (arch == 0) /* fixme? */ {
if (!quietmode) fprintf(stdout, "%s: resolving source dependencies\n",configtag->arch[arch]);
if (resolveFirstLevelSourceDependencies(configtag, arch)) {
fprintf(stderr,
"Fatal error: could not generate first level requires table\n");
exit(1);
}
}
if (recursive_mode) {
if (!quietmode) fprintf(stdout, "%s: resolving recursive dependencies...\n",configtag->arch[arch]);
if (resolveRecursiveDependencies
(configtag->headerlist[arch])) {
fprintf(stderr,"Fatal error: could not resolve recursive dependencies\n");
exit(1);
}
}
}
if (mode & MODE_DATA_TABLES) {
if (!quietmode) fprintf(stdout,"%s: writing dependencies table...\n",configtag->arch[arch]);
print_datatables(configtag,arch);
}
if (genheader_mode & GENHEADER_STATS) {
if (!quietmode) fprintf(stdout,"%s: generating statistics...\n",configtag->arch[arch]);
logmsg(LOG_DEBUG,"%s: generateHeaderStats - start",configtag->arch[arch]);
if (generateHeaderStats(configtag, arch)) {
fprintf(stderr,"Fatal error: could not generate statistic for headers\n");
exit(1);
}
logmsg(LOG_DEBUG,"%s: generateHeaderStats - done",configtag->arch[arch]);
}
if (mode & MODE_HTML) {
// printf("Generating HTML files for binary RPMs...\n");
logmsg(LOG_DEBUG,"%s: generateHTMLFiles - start",configtag->arch[arch]);
generateHTMLFiles(configtag, arch);
logmsg(LOG_DEBUG,"%s: generateHTMLFiles - done",configtag->arch[arch]);
}
if (mode & MODE_GENPKGLIST) {
/* currheaderlist = headerlist; */
generatePkgList(configtag, arch);
}
}
int
main(int argc, char *argv[])
{
char *name = NULL, *date = NULL,
*repository_dir = NULL, *repository_tag = NULL,
*configfile = NULL, *passed_arch = NULL;
struct headerList *currheaderlist;
struct headerSourceList *currheadersourcelist = NULL;
int i,hasbuilds[ARCHS_MAX],ptharg[ARCHS_MAX];
pthread_t pth[ARCHS_MAX];
char warning[PATH_MAX];
time_t start_time, stop_time;
/* options args for 'getopt_long()' */
static const char *optstring = "+a:c:t:qhvd";
static struct option longopts[] = {
{ "deps-table", no_argument, 0, 0 },
{ "data-tables", no_argument, 0, 0 },
{ "gendatatables", no_argument, 0, 0 },
{ "find-deps", required_argument, 0, 0 },
{ "changelog", required_argument, 0, 0 },
{ "changelogsince", required_argument, 0, 0 },
{ "genbuildinfo", no_argument, 0, 0 },
{ "genhtml", no_argument, 0, 0 },
{ "genpkglist", no_argument, 0, 0 },
{ "gensrcpkglist", no_argument, 0, 0 },
{ "gensqlite3", no_argument, 0, 0 },
{ "arch", required_argument, 0, 'a' },
{ "conf", required_argument, 0, 'c' },
{ "help", no_argument, 0, 'h' },
{ "quiet", no_argument, 0, 'q' },
{ "tag", required_argument, 0, 't' },
{ "version", no_argument, 0, 'v' },
{ "debug", no_argument, 0, 'd' },
{ 0, 0, 0, 0 }
};
// install backtrace handler
signal(SIGSEGV, backtraceHandler);
start_time=time(NULL);
opterr = 1;
unsigned int syntaxerr = 0;
while (1) {
int longindex = 0;
int argval = getopt_long(argc, argv, optstring, longopts, &longindex);
if (argval == -1) {
break;
}
switch (argval) {
case 0:
if (!strcmp(longopts[longindex].name, "gendatatables")) {
mode |= MODE_DATA_TABLES;
} else if (!strcmp(longopts[longindex].name, "deps-table") ||
!strcmp(longopts[longindex].name, "data-tables")) {
fprintf(stderr,"Warning: use of --deps-table and --data-tables is obsolete; use --gendatatables instead.\n");
mode |= MODE_DATA_TABLES;
} else if (!strcmp(longopts[longindex].name, "find-deps")) {
mode |= MODE_FIND_DEPS;
name = optarg;
} else if (!strcmp(longopts[longindex].name, "changelog")) {
mode |= MODE_CHANGELOG;
genheader_mode |= GENHEADER_CHANGELOG | GENHEADER_STATS;
name = optarg;
} else if (!strcmp(longopts[longindex].name, "changelogsince")) {
mode |= MODE_CHANGELOG;
genheader_mode |= GENHEADER_CHANGELOG | GENHEADER_STATS;
date = optarg;
name = NULL;
} else if (!strcmp(longopts[longindex].name, "genhtml")) {
mode |= MODE_HTML;
genheader_mode |= GENHEADER_CHANGELOG | GENHEADER_STATS;
recursive_mode = 1;
name = NULL;
} else if (!strcmp(longopts[longindex].name, "gensqlite3")) {
mode |= MODE_SQLITE3;
genheader_mode |= GENHEADER_CHANGELOG | GENHEADER_STATS;
recursive_mode = 1;
name = NULL;
} else if (!strcmp(longopts[longindex].name, "gensrcpkglist")) {
mode |= MODE_GENSRCPKGLIST;
name = NULL;
} else if (!strcmp(longopts[longindex].name, "genpkglist")) {
mode |= MODE_GENPKGLIST;
name = NULL;
} else if (!strcmp(longopts[longindex].name, "genbuildinfo")) {
mode |= MODE_GENBUILDINFO;
genheader_mode |= GENHEADER_BASE;
name = NULL;
}
break;
case 'a':
passed_arch = optarg;
break;
case 'c':
configfile = optarg;
break;
case 't':
repository_tag = optarg;
break;
case 'h':
program_usage(0);
break;
case 'q':
quietmode=1;
break;
case 'v':
program_version();
exit(0);
break;
case '?':
/* program_usage(1); */
exit(1);
break;
case 'd':
log_debug_set(1);
logmsg(LOG_DEBUG,"debug logging enabled");
break;
default:
fprintf(stderr,
"Fatal error while parsing command line arguments\n");
exit(1);
break;
}
}
if (optind < argc) {
fprintf(stderr, "Non-option elements found: ");
while (optind < argc)
fprintf(stderr, "`%s' ", argv[optind++]);
fprintf(stderr, "\n");
exit(1);
} else if (syntaxerr || !mode || !repository_tag ) {
program_usage(1);
}
if (!configfile) {
configfile = malloc(sizeof DEFAULT_CONFIGFILE);
if (!configfile) {
fprintf(stderr, "The system is out of memory\n");
exit(1);
}
strcpy(configfile, DEFAULT_CONFIGFILE);
}
if (read_configuration(configfile, repository_tag)) {
fprintf(stderr,
"Fatal error while parsing config file %s; aborting.\n",configfile);
exit(1);
}
if (!configtag) {
fprintf(stderr,
"Fatal error: configuration missing for given tag\n");
exit(1);
}
if (passed_arch) {
configtag->arch[0] = malloc(sizeof passed_arch);
if (!configtag->arch[0]) {
fprintf(stderr, "The system is out of memory\n");
exit(1);
}
strcpy(configtag->arch[0], passed_arch);
configtag->arch[1] = NULL;
}
rpminit();
if (!quietmode)
fprintf(stdout, "Starting operations on '%s' repository\n", configtag->tag);
if (!repository_dir)
repository_dir = configtag->repository_dir;
if (!quietmode)
fprintf(stdout, "Scanning source packages...\n");
else
logmsg(LOG_MARK,"Source packages check for %s:", configtag->tag);
if (generateSourceHeaderList(configtag, genheader_mode)) {
fprintf(stderr,
"Fatal error: could not generate source header list\n");
exit(1);
}
if (genheader_mode) {
if (mode & MODE_HTML) {
logmsg(LOG_DEBUG,"cleanHTMLPackagesFiles - start");
cleanHTMLPackagesFiles(configtag);
logmsg(LOG_DEBUG,"cleanHTMLPackagesFiles - done");
}
warning[0]=0;
for (i = 0; i < ARCHS_MAX && configtag->arch[i]; i++) {
snprintf(&warning[strlen(warning)],PATH_MAX-strlen(warning)," %s", configtag->arch[i]);
}
if (!quietmode)
fprintf(stdout, "Scanning binary packages for archs:%s...\n",warning);
else
logmsg(LOG_MARK, "Binary packages check for archs:%s", warning);
for (i = 0; i < ARCHS_MAX && configtag->arch[i]; i++) {
ptharg[i]=i;
pthread_create(&pth[i],NULL,threadArchScan,&ptharg[i]);
} // archs threads loop
for (i = 0; i < ARCHS_MAX && configtag->arch[i]; i++) {
pthread_join(pth[i], NULL);
}
if (!passed_arch) { // can't do missing builds and ports check in single arch mode
if (!quietmode)
fprintf(stdout, "Checking for SRPMS with no builds and missing ports...\n");
else
logmsg(LOG_MARK, "Missing ports and SRPMS with no builds check");
currheadersourcelist = configtag->headersourcelist;
while (currheadersourcelist != NULL) {
for (i = 0; i < ARCHS_MAX && configtag->arch[i]; i++)
hasbuilds[i] = 0;
for (i = 0; i < ARCHS_MAX && configtag->arch[i]; i++) {
if (currheadersourcelist->firstchild[i])
hasbuilds[i] = 1;
else if (currheadersourcelist->old && currheadersourcelist->old->firstchild[i])
hasbuilds[i] = -1;
}
warning[0] = '\0';
for (i = 0; i < ARCHS_MAX && configtag->arch[i]; i++) {
if (hasbuilds[i] == 1) {
strncat(warning," ",PATH_MAX);
strncat(warning,configtag->arch[i],PATH_MAX);
}
}
if (warning[0] == '\0') {
snprintf(warning, PATH_MAX, "SRPM does not have any valid RPM build");
logmsg(LOG_WARNING, "%s: %s",
currheadersourcelist->name,
warning);
addWarning(currheadersourcelist, warning);
}
warning[0] = '\0';
for (i = 0; i < ARCHS_MAX && configtag->arch[i]; i++) {
if ((hasbuilds[i] == -1) &&
(currheadersourcelist->altrepository == configtag->repository_level)) {
if (warning[0] == '\0')
strncat(warning, "requires port to arch(s):", PATH_MAX);
strncat(warning," ",PATH_MAX);
strncat(warning,configtag->arch[i],PATH_MAX);
}
}
if (warning[0] != '\0') {
logmsg(LOG_WARNING, "%s: %s",
currheadersourcelist->name,
warning);
addWarning(currheadersourcelist, warning);
}
currheadersourcelist = currheadersourcelist->next;
}
}
} // if (genheader_mode)
if (mode & MODE_SQLITE3) {
printf("Generating sqlite db...\n");
logmsg(LOG_DEBUG,"generateSQLite - start");
generateSQLite(configtag);
logmsg(LOG_DEBUG,"generateSQLite - done");
}
if (genheader_mode & GENHEADER_STATS) {
if (!quietmode)
fprintf(stdout, " - generating source statistics...\n");
logmsg(LOG_DEBUG,"generateHeaderSourceStats - start");
if (generateHeaderSourceStats(configtag)) {
fprintf(stderr,
"Fatal error: could not generate statistic for headers\n");
exit(1);
}
logmsg(LOG_DEBUG,"generateHeaderSourceStats - done");
}
if (mode & MODE_FIND_DEPS) {
currheaderlist = findPackageByName(configtag->headerlist[i], name);
if (currheaderlist) {
printf("%s: ", currheaderlist->name);
printRequireList(stdout,currheaderlist->requirelist);
/* currrequirelist = currheaderlist->requirelist; */
printf("\n");
} else {
fprintf(stderr, "Error: package %s not found\n", name);
}
}
if (mode & MODE_CHANGELOG) {
if (name) {
currheadersourcelist =
findSourcePackage(configtag->headersourcelist, name,
NULL, NULL,-1);
if (currheadersourcelist) {
printChangelog(currheadersourcelist->changelog);
} else {
fprintf(stderr, "Error: package %s not found\n", name);
}
} else {
struct tm tmdate;
tmdate.tm_min = 0;
tmdate.tm_hour = 0;
tmdate.tm_sec = 0;
const char *cp;
cp = (char *) strptime((const char *) date, "%m%d%y", &tmdate);
if (!cp) {
fprintf(stderr, "Error: date \"%s\" is invalid!\n", date);
exit(1);
}
printChangelogSince(stdout,configtag, &tmdate, 0);
}
}
if (mode & MODE_HTML) {
logmsg(LOG_DEBUG,"cleanHTMLFiles - start");
cleanHTMLFiles(configtag);
logmsg(LOG_DEBUG,"cleanHTMLFiles - done");
if (!quietmode)
fprintf(stdout, "Generating HTML reports...\n");
generateHTMLMainIndex(firstconfigtag);
// printf("Generating HTML files for source RPMs...\n");
generateHTML_SRPMSFiles(configtag);
// printf("Generating Maintainers pages...\n");
generateMaintainersPages(configtag);
}
// NOTE: generateStats must be called after generateHTML_SRPMSFiles for warnings to appear
if (mode & MODE_HTML) {
logmsg(LOG_DEBUG,"generateStats - start");
generateStats(configtag,i);
logmsg(LOG_DEBUG,"generateStats - done");
}
if (mode & MODE_GENSRCPKGLIST) {
/* currheaderlist = headerlist; */
generateSrcPkgList(configtag);
}
if (mode & MODE_GENBUILDINFO) {
/* currheaderlist = headerlist; */
printf("Generating build information files...\n");
generateBuildInfo(configtag,0);
}
stop_time=time(NULL);
if (!quietmode)
fprintf(stdout, "Execution time: %ld.%02ld minutes\n",(stop_time-start_time)/60,(stop_time-start_time)%60);
exit(0);
}