/* * distromatic - tool for RPM based repositories * * Copyright (C) 2004-2010 by Silvan Calarco * Copyright (C) 2006 by Davide Madrisan * * 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 #include #include #include #if HAVE_UNISTD_H # include #endif #include #include #if HAVE_STRING_H # if !STDC_HEADERS && HAVE_MEMORY_H # include # endif /* Tell glibc's to provide a prototype for strndup() */ # ifndef __USE_GNU # define __USE_GNU # endif # include #endif #if HAVE_STRINGS_H # include #endif /* Tell glibc's to provide a prototype for strptime() */ #ifndef __USE_XOPEN # define __USE_XOPEN #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # 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" #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 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 char* arch =NULL; static const unsigned int bufsize = 1024; static const unsigned int maxlinelenght = 1024; static const char *copyright[] = { PROGRAMNAME " version " PROGRAMVERSION, "Copyright (C) 2004-2010 by Silvan Calarco ", "Copyright (C) 2006 by Davide Madrisan ", (char *)0 }; /* *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", " --find-deps find dependencies for given package name", " --changelog print changelog for given package name", " --changelogsince 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 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]); } /* * 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 those 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)) { 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)) { 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 { 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; } /* report obsoleted elements which are provided by other packages */ currheader = ct->headerlist[archidx]; while (currheader) { for (j = 0; j < currheader->obsoletecount; j++) { prov=findOrCreateProvidedListEntry((struct providedList**) &ct->providedlist_idx[archidx], currheader->obsoletename[j],0); if (prov) { for (i = 0; i < prov->numproviders; i++) { if (prov->provider[i] != currheader) { 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 requireList *currrequire; 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]; currrequire = NULL; currheader->requirelist = NULL; if ((!currheader->obsoleted) && (currheader->next) && (!strcmp(currheader->name,currheader->next->name))) { // mark obsoleted any package with same name in upper level repositories if (currheader->altrepository < currheader->next->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->next->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 = currheader->next; continue; } // currheader->require.resolved = malloc(sizeof(struct providedList*)*currheader->requirecount); for (i = 0; i < currheader->requirecount; i++) { /* if (strncmp("a2ps",currheader->name,4) == 0) { fprintf(stderr,"a2ps:%s\n",currheader->require[i]->name); }*/ 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); 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 */ 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 requireList *currrequire; struct providedList *provided; struct fileTree *file; char warning[PATH_MAX]; int i; int j,found; logmsg(LOG_DEBUG,"resolveFirstLevelSourceDependencies - sources"); while (currsourceheader) { scanheader = ct->headerlist[archidx]; currrequire = NULL; 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); if (provided->numbuildproviders == 0) { // check if require[i]->name requirement is met scanheader = ct->headerlist[archidx]; 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 { scanheader=NULL; } } else { /* requirement is not a file, cross-check with provides */ while (scanheader && !checkRequireWithProvides( currsourceheader->require[i]->name, scanheader)) scanheader = scanheader->next; if (scanheader) { provided->numbuildproviders=1; provided->buildprovider=malloc(sizeof(struct headerList*)); provided->buildprovider[0]=scanheader; provided->flags=0; } logmsg(LOG_DEBUG,"%s is a build provider for %s",provided->name,currsourceheader->name); } if (!scanheader) { 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); } /* if (scanheader) { provided->numbuildproviders=1; provided->buildprovider=malloc(sizeof(struct headerList*)); provided->buildprovider[0]=scanheader; provided->flags=0; } else { snprintf(warning, PATH_MAX, "missing build provider for %s", currsourceheader->require[i]->name); fprintf(stderr,"Warning: %s: %s\n",currsourceheader->name, 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->numbuildproviders; 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; itag = (char *) strndup(&vartok[1], strlen(vartok) - 2); newconfigtag->repository_dir = NULL; newconfigtag->repository_source_dir = NULL; curraltrep = 0; for (i = 0; irepository[i] = NULL; newconfigtag->html_dir = NULL; newconfigtag->download_prefix = NULL; newconfigtag->download_dir = NULL; newconfigtag->showfile_prefix = NULL; for (i = 0; iarch[i] = configdefaults.arch[i]; newconfigtag->headerlist[i] = NULL; newconfigtag->filetree[i]=NULL; for (j = 0; jprovidedlist_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; } int main(int argc, char *argv[]) { char *name = NULL, *date = NULL, *repository_dir = NULL, *repository_tag = NULL, *configfile = NULL; struct headerList *currheaderlist; struct headerSourceList *currheadersourcelist = NULL; unsigned int mode = 0; unsigned int recursive_mode = 0; unsigned int genheader_mode = GENHEADER_BASE + GENHEADER_REQUIREMENTS; unsigned int quietmode = 0; unsigned int obsolete_packages = 1; int i; 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 }, { "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 } }; 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, "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': 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 (arch) { configtag->arch[0] = malloc(sizeof arch); if (!configtag->arch[0]) { fprintf(stderr, "The system is out of memory\n"); exit(1); } strcpy(configtag->arch[0], 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"); } for (i = 0; i < ARCHS_MAX && configtag->arch[i]; i++) { if (!quietmode) fprintf(stdout, "Scanning binary packages for %s arch ...\n",configtag->arch[i]); else logmsg(LOG_MARK, "%s binary packages check for %s:", configtag->arch[i],configtag->tag); if (generateHeaderList(configtag,i)) { fprintf(stderr, "Fatal error: could not generate header list\n"); exit(1); } if (!quietmode) fprintf(stdout, "Checking for SRPMS with no RPMS...\n"); currheadersourcelist = configtag->headersourcelist; while (currheadersourcelist != NULL) { if (!currheadersourcelist->firstchild) { snprintf(warning, PATH_MAX, "SRPM does not have any valid RPM build"); logmsg(LOG_WARNING, "%s: %s", currheadersourcelist->name, warning); addWarning(currheadersourcelist, warning); } currheadersourcelist = currheadersourcelist->next; } if (genheader_mode & GENHEADER_REQUIREMENTS) { if (obsolete_packages) { if (!quietmode) fprintf(stdout, " - handling obsoleted packages...\n"); if (handleObsoletedPackages(configtag,i)) { fprintf(stderr, "Fatal error: maximum number of multiple providing packages reached. Aborting."); exit(1); } } if (!quietmode) fprintf(stdout, " - resolving dependencies...\n"); if (resolveFirstLevelDependencies(configtag, i)) { fprintf(stderr, "Fatal error: could not generate first level requires table\n"); exit(1); } if (i == 0) /* fixme? */ { if (!quietmode) fprintf(stdout, " - resolving source dependencies\n"); if (resolveFirstLevelSourceDependencies(configtag, i)) { fprintf(stderr, "Fatal error: could not generate first level requires table\n"); exit(1); } } if (recursive_mode) { if (!quietmode) fprintf(stdout, " - resolving recursive dependencies...\n"); if (resolveRecursiveDependencies (configtag->headerlist[i])) { fprintf(stderr, "Fatal error: could not resolve recursive dependencies\n"); exit(1); } } } if (mode & MODE_DATA_TABLES) { if (!quietmode) fprintf(stdout, " - writing dependencies table...\n"); print_datatables(configtag,i); } if (genheader_mode & GENHEADER_STATS) { if (!quietmode) fprintf(stdout, " - generating statistics...\n"); logmsg(LOG_DEBUG,"generateHeaderStats - start"); if (generateHeaderStats(configtag, i)) { fprintf(stderr, "Fatal error: could not generate statistic for headers\n"); exit(1); } logmsg(LOG_DEBUG,"generateHeaderStats - done"); } if (mode & MODE_HTML) { // printf("Generating HTML files for binary RPMs...\n"); logmsg(LOG_DEBUG,"generateHTMLFiles - start"); generateHTMLFiles(configtag, i); logmsg(LOG_DEBUG,"generateHTMLFiles - done"); } if (mode & MODE_GENPKGLIST) { /* currheaderlist = headerlist; */ generatePkgList(configtag, i); } } // archs loop } // if (genheader_mode) 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); }