"Fossies" - the Fresh Open Source Software Archive

Member "rpm2html-1.11.2/rpmdata.c" (13 Nov 2008, 34132 Bytes) of package /linux/privat/rpm2html-1.11.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "rpmdata.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * rpmdata.c : handle the data in the RPM database.
    3  *
    4  * See Copyright for the status of this software.
    5  *
    6  * $Id: rpmdata.c,v 1.54 2008/11/13 23:56:21 hany Exp $
    7  */
    8 
    9 #include <config.h>
   10 #include <sys/types.h>
   11 #include <sys/stat.h>
   12 #ifdef HAVE_FCNTL_H
   13 #include <fcntl.h>
   14 #endif
   15 #include <stdio.h>
   16 #include <stdlib.h>
   17 #include <string.h>
   18 #ifdef HAVE_UNISTD_H
   19 #include <unistd.h>
   20 #endif
   21 #include <ctype.h>
   22 #include <errno.h>
   23 
   24 #include <rpm/rpmlib.h>
   25 
   26 #include "rpm2html.h"
   27 #include "rpmdata.h"
   28 
   29 int rpmNameCmp(const rpmData *a, const rpmData *b);
   30 
   31 /*
   32  * An hash table for the RPM list
   33  */
   34 
   35 #define HASH_SIZE 4096
   36 
   37 rpmDataPtr rpmHashTable[HASH_SIZE];
   38 
   39 /*
   40  * An hash table for the Resources list
   41  */
   42 
   43 rpmRessPtr rpmRessHashTable[HASH_SIZE];
   44 
   45 /*
   46  * global variables shared by rpm2htm code
   47  */
   48 int rpm2htmlVerbose = 1;
   49 int force = 0;
   50 time_t currentTime;
   51 
   52 /*
   53  * the lists of RPM and resources collected so far.
   54  */
   55 rpmDataPtr rpmSoftwareList = NULL;
   56 rpmRessPtr ressList = NULL;
   57 rpmRessPtr ressInstalledList = NULL;
   58 rpmArchPtr archList = NULL;
   59 rpmDirPtr dirList = NULL;
   60 rpmSubdirPtr dirTree = NULL;
   61 rpmRealDirPtr treeRoot = NULL;
   62 
   63 /*
   64  * Hash table initialization.
   65  */
   66 
   67 static int rpmHashInitialized = 0;
   68 
   69 void rpmHashInitialize(void) {
   70     int i;
   71 
   72     for (i = 0; i < HASH_SIZE; i++) {
   73         rpmHashTable[i] = NULL;
   74         rpmRessHashTable[i] = NULL;
   75     }
   76     rpmHashInitialized = 1;
   77 }
   78 
   79 /*
   80  * Compute an RPM hash key
   81  */
   82 
   83 int rpmGetHash(const char *name, const char *version,
   84            const char *release) {
   85     unsigned short res = 0;
   86 
   87     while (*name != 0) res += *(name++);
   88     while (*version != 0) res += *(version++);
   89     while (*release != 0) res += *(release++);
   90 
   91     return((int) (res % HASH_SIZE));
   92 }
   93 
   94 /*
   95  * Compute a Resource hash key
   96  */
   97 
   98 int rpmGetRessHash(const char *name) {
   99     unsigned short res = 0;
  100 
  101     while (*name != 0) res += *(name++);
  102 
  103     return((int) (res % HASH_SIZE));
  104 }
  105 
  106 /*
  107  * Search wether an RPM is available.
  108  */
  109 
  110 rpmDataPtr rpmSearchSoftware(const char *name, const char *version,
  111            const char *release, const char *arch) {
  112     rpmDataPtr cur;
  113     int hash;
  114 
  115     if (!rpmHashInitialized) rpmHashInitialize();
  116 
  117     hash = rpmGetHash(name, version, release);
  118     cur = rpmHashTable[hash];
  119 
  120     while (cur != NULL) {
  121         if (!strcmp(name, cur->name) &&
  122         !strcmp(version, cur->version) &&
  123         !strcmp(release, cur->release)) {
  124         /*
  125          * this is already in the sofware list, link in as
  126          * a new arch support.
  127          */
  128         while (cur != NULL) {
  129             if (!strcmp(arch, cur->arch))
  130             return(cur);
  131         cur = cur->nextArch;
  132         }
  133         return(NULL);
  134     }    
  135         cur = cur->nextHash;
  136     }
  137     return(NULL);
  138 }
  139 
  140 /*
  141  * Insert a new RPM, managing the list of various software.
  142  */
  143 
  144 void rpmAddSoftware(rpmDataPtr rpm) {
  145     rpmDataPtr cur;
  146     int hash;
  147 
  148     if (!rpmHashInitialized) rpmHashInitialize();
  149 
  150     hash = rpmGetHash(rpm->name, rpm->version, rpm->release);
  151     cur = rpmHashTable[hash];
  152 
  153     while (cur != NULL) {
  154         if (!strcmp(rpm->name, cur->name) &&
  155         !strcmp(rpm->version, cur->version) &&
  156         !strcmp(rpm->release, cur->release)) {
  157         /*
  158          * this is already in the sofware list, link in as
  159          * a new arch support.
  160          */
  161 #ifdef DEBUG_HASH
  162 fprintf(stderr, "clash : (%d) %s-%s-%s.%s and %s-%s-%s.%s\n", hash,
  163         rpm->name, rpm->version, rpm->release, rpm->arch,
  164         cur->name, cur->version, cur->release, cur->arch);
  165 #endif
  166 
  167         rpm->nextArch = cur->nextArch;
  168         cur->nextArch = rpm;
  169         rpm->nextSoft = NULL; /* this one is not in the software list */
  170         return;
  171     }    
  172         cur = cur->nextHash;
  173     }
  174 
  175     /*
  176      * this wasn't found in the software list !
  177      */
  178 #ifdef DEBUG_HASH
  179 fprintf(stderr, "new : (%d) %s-%s-%s.%s\n", hash,
  180         rpm->name, rpm->version, rpm->release, rpm->arch);
  181 #endif
  182 
  183     rpm->nextHash = rpmHashTable[hash];
  184     rpmHashTable[hash] = rpm;
  185     rpm->nextArch = NULL;
  186     rpm->nextSoft = rpmSoftwareList;
  187     rpmSoftwareList = rpm;
  188 }
  189 
  190 /*
  191  * Add a resource to the list if it doesn't exists and add the
  192  * corresponding RPM as a provider.
  193  */
  194 
  195 rpmRessPtr rpmRessAdd(char *ress, rpmDataPtr rpm, int installed) {
  196     rpmRessPtr cur;
  197     int hash;
  198     
  199     if (!rpmHashInitialized) rpmHashInitialize();
  200 
  201     hash = rpmGetRessHash(ress);
  202     cur = rpmRessHashTable[hash];
  203 
  204     /* search for the resource */
  205     while (cur != NULL) {
  206         if (!strcmp(ress, cur->name)) goto found;
  207     cur = cur->nextHash;
  208     }
  209 
  210     /* not found allocate a new resource block and fill it */
  211     cur = (rpmRessPtr) xmlMalloc(sizeof(rpmRess));
  212     if (cur == NULL) {
  213          fprintf(stderr, "cannot allocate %d bytes: %s\n", sizeof(rpmRess),
  214              strerror(errno));
  215          return(NULL);
  216     }
  217     memset(cur, 0, sizeof(rpmRess));
  218     cur->max_provider = 8;
  219     cur->provider = (rpmDataPtr *) xmlMalloc(cur->max_provider *
  220                                           sizeof(rpmDataPtr));
  221     if (cur->provider == NULL) {
  222          fprintf(stderr, "cannot allocate %d bytes: %s\n", 
  223              cur->max_provider * sizeof(rpmDataPtr),
  224              strerror(errno));
  225          return(NULL);
  226     }
  227     cur->name = xmlStrdup(ress);
  228     cur->nb_provider = 0;
  229     cur->stamp = rpm->extra->stamp;
  230     if (installed) {
  231     cur->next = ressInstalledList;
  232     ressInstalledList = cur;
  233     } else {
  234     cur->next = ressList;
  235     ressList = cur;
  236     }
  237     cur->nextHash = rpmRessHashTable[hash];
  238     rpmRessHashTable[hash] = cur;
  239 
  240 found:
  241     /* add the provider to the array */
  242     if (cur->nb_provider >= cur->max_provider) {
  243         cur->max_provider *= 2;
  244     cur->provider = (rpmDataPtr *) xmlRealloc(cur->provider,
  245                  cur->max_provider * sizeof(rpmDataPtr));
  246         if (cur->provider == NULL) {
  247             fprintf(stderr, "rpmRessAdd : ran out of memory !\n");
  248         exit(1);
  249     }
  250     }
  251     cur->provider[cur->nb_provider++] = rpm;
  252     if (rpm->extra->stamp > cur->stamp) 
  253         cur->stamp = rpm->extra->stamp;
  254     return(cur);
  255 }
  256 
  257 /*
  258  * Add a resource to the list if it doesn't exists and add the
  259  * corresponding RPM as a requester.
  260  */
  261 
  262 rpmRessPtr rpmRequAdd(char *requ, char * requv, rpm_dep_flag requf, rpmDataPtr rpm, int installed) { 
  263     rpmRessPtr cur;
  264     int hash;
  265     
  266     if (!rpmHashInitialized) rpmHashInitialize();
  267 
  268     hash = rpmGetRessHash(requ);
  269     cur = rpmRessHashTable[hash];
  270     
  271     /* search for the resource */
  272     while (cur != NULL) {
  273         if (!strcmp(requ, cur->name)){
  274         goto found;
  275     }
  276     cur = cur->nextHash;
  277     }
  278 
  279     /* not found allocate a new resource block and fill it */
  280     cur = (rpmRessPtr) xmlMalloc(sizeof(rpmRess));
  281     if (cur == NULL) {
  282          fprintf(stderr, "cannot allocate %d bytes: %s\n", sizeof(rpmRess),
  283              strerror(errno));
  284          return(NULL);
  285     }
  286     memset(cur, 0, sizeof(rpmRess));
  287     cur->max_provider = 8;
  288     cur->provider = (rpmDataPtr *) xmlMalloc(cur->max_provider *
  289                                           sizeof(rpmDataPtr));
  290     if (cur->provider == NULL) {
  291          fprintf(stderr, "cannot allocate %d bytes: %s\n", 
  292              cur->max_provider * sizeof(rpmDataPtr),
  293              strerror(errno));
  294          return(NULL);
  295     }
  296     cur->name = xmlStrdup(requ);
  297     if (requv != NULL) 
  298     cur->version = xmlStrdup(requv);
  299     cur->flag = requf;
  300     cur->nb_provider = 0;
  301     cur->stamp = rpm->extra->stamp;
  302     if (installed) {
  303     cur->next = ressInstalledList;
  304     ressInstalledList = cur;
  305     } else {
  306     cur->next = ressList;
  307     ressList = cur;
  308     }
  309     cur->nextHash = rpmRessHashTable[hash];
  310     rpmRessHashTable[hash] = cur;
  311 
  312 found:
  313     if (rpm->extra->stamp > cur->stamp)
  314         cur->stamp = rpm->extra->stamp;
  315     return(cur);
  316 }
  317 
  318 /*
  319  * print an RPM data block
  320  */
  321 void rpmDataPrint(rpmDataPtr rpm) {
  322     int i;
  323 
  324     printf("%s\n", rpm->filename);
  325     printf("%s-%s-%s\n", rpm->name, rpm->version, rpm->release);
  326     if (rpm->summary)
  327     printf("   %s\n", rpm->summary);
  328     if (rpm->extra) {
  329     for (i = 0; i < rpm->extra->nb_resources ;i++)
  330         printf("-> %s\n", rpm->extra->resources[i]->name);
  331     }
  332 }
  333 
  334 /*
  335  * Add two RPM lists
  336  */
  337 
  338 rpmDataPtr rpmAddList(rpmDataPtr ret, rpmDataPtr cur) {
  339     rpmDataPtr tmp;
  340 
  341     if (ret == NULL) return (cur);
  342     if (cur == NULL) return (ret);
  343     if (cur->next == NULL) {
  344         cur->next = ret;
  345     return(cur);
  346     }
  347     if (ret->next == NULL) {
  348         ret->next = cur;
  349     return(ret);
  350     }
  351     tmp = ret;
  352     while (tmp->next != NULL) tmp = tmp->next;
  353     tmp->next = cur;
  354     return(ret);
  355 }
  356 
  357 /*
  358  * A generic sort function for an RPM list.
  359  * NOTE : since doing a fast sort on a linked list is a pain,
  360  *        we allocate an array, copy the rpmDataPtr in the array
  361  *        and use the standard qsort() for decent performances on
  362  *        large databases.
  363  */
  364 
  365 typedef int (*rpmCmpFunc)(const rpmData *a, const rpmData *b);
  366 
  367 typedef int (*qsortCmpFunc)(const void * a, const void * b);
  368 
  369 static rpmCmpFunc currentCmpFunc = NULL;
  370 
  371 int rpmPtrCmpFunc(const rpmData **a, const rpmData **b) {
  372     return(currentCmpFunc(*a, *b));
  373 }
  374 
  375 void rpmGenericSort(rpmCmpFunc rpmCmp, rpmDataPtr *base, int installed) {
  376     int i, nb_elems = 0;
  377     rpmDataPtr list;
  378     rpmDataPtr *array = NULL;
  379     int max_elems = 1024;
  380 
  381     if (base == NULL)
  382     return;
  383     if (*base == NULL)
  384     return;
  385     list = *base;
  386 
  387     array = (rpmDataPtr *) xmlMalloc(max_elems * sizeof(rpmDataPtr));
  388     if (array == NULL) {
  389     fprintf(stderr, "rpmGenericSort : array alloc error\n");
  390     exit(1);
  391     }
  392 
  393     /*
  394      * Step 1 : copy the list in the Array
  395      */
  396     while (list != NULL) {
  397         if (nb_elems == max_elems) {
  398         max_elems *= 2;
  399         array = (rpmDataPtr *)
  400                  xmlRealloc(array, max_elems * sizeof(rpmDataPtr));
  401             if (array == NULL) {
  402             fprintf(stderr, "rpmGenericSort : array alloc error\n");
  403         exit(1);
  404         }
  405     }
  406     array[nb_elems++] = list;
  407     list = list->next;
  408     }
  409 
  410     /*
  411      * Step 2 : qsort() the Array
  412      */
  413     currentCmpFunc = rpmCmp;
  414     qsort(array, nb_elems, sizeof(rpmDataPtr), (qsortCmpFunc) rpmPtrCmpFunc);
  415 
  416     /*
  417      * Step 3 : rebuild the list.
  418      */
  419     for (i = 0;i < nb_elems - 1; i++)
  420         array[i]->next = array[i + 1];
  421     array[nb_elems - 1]->next = NULL;
  422 
  423     list = array[0];
  424     *base = list;
  425     xmlFree(array);
  426 }
  427 
  428 /*
  429  * A generic sort function for the software list.
  430  */
  431 
  432 void rpmGenericSortSoftware(rpmCmpFunc rpmCmp, int installed) {
  433     rpmDataPtr list;
  434     int i, nb_elems = 0;
  435     rpmDataPtr *array = NULL;
  436     int max_elems = 1024;
  437 
  438     list = rpmSoftwareList;
  439 
  440     if (list == NULL) return;
  441 
  442     array = (rpmDataPtr *) xmlMalloc(max_elems * sizeof(rpmDataPtr));
  443     if (array == NULL) {
  444     fprintf(stderr, "rpmGenericSort : array alloc error\n");
  445     exit(1);
  446     }
  447 
  448     /*
  449      * Step 1 : copy the list in the Array
  450      */
  451     while (list != NULL) {
  452         if (nb_elems == max_elems) {
  453         max_elems *= 2;
  454         array = (rpmDataPtr *)
  455                  xmlRealloc(array, max_elems * sizeof(rpmDataPtr));
  456             if (array == NULL) {
  457             fprintf(stderr, "rpmGenericSort : array alloc error\n");
  458         exit(1);
  459         }
  460     }
  461     array[nb_elems++] = list;
  462     list = list->nextSoft;
  463     }
  464 
  465     /*
  466      * Step 2 : qsort() the Array
  467      */
  468     currentCmpFunc = rpmCmp;
  469     qsort(array, nb_elems, sizeof(rpmDataPtr), (qsortCmpFunc) rpmPtrCmpFunc);
  470 
  471     /*
  472      * Step 3 : rebuild the list.
  473      */
  474     for (i = 0;i < nb_elems - 1; i++)
  475         array[i]->nextSoft = array[i + 1];
  476     array[nb_elems - 1]->nextSoft = NULL;
  477 
  478     rpmSoftwareList = array[0];
  479     xmlFree(array);
  480 }
  481 
  482 /*
  483  * rpmGroupCmp : comparison of groupe values, if equal, compare
  484  *               the names.
  485  */
  486 
  487 int rpmGroupCmp(const rpmData *a, const rpmData *b) {
  488     int res = strcmp(a->group, b->group);
  489 
  490     if (res) return(res);
  491     res = rpmNameCmp(a, b);
  492     return(res);
  493 }
  494 
  495 /*
  496  * sort all the RPMs by Group.
  497  */
  498 void rpmGroupSort(rpmDataPtr *list, int installed) {
  499     rpmGenericSort(rpmGroupCmp, list, installed);
  500     rpmGenericSortSoftware(rpmGroupCmp, installed);
  501 }
  502 
  503 /*
  504  * rpmDistribCmp : comparison of distributions values, if equal, compare
  505  *               the names.
  506  */
  507 
  508 int rpmDistribCmp(const rpmData *a, const rpmData *b) {
  509     int res = strcmp(a->distribution, b->distribution);
  510 
  511     if (res) return(res);
  512     res = rpmNameCmp(a, b);
  513     return(res);
  514 }
  515 
  516 /*
  517  * sort all the RPMs by Distribution.
  518  */
  519 void rpmDistribSort(rpmDataPtr *list, int installed) {
  520     rpmGenericSort(rpmDistribCmp, list, installed);
  521 }
  522 
  523 /*
  524  * rpmVendorCmp : comparison of vendor values, if equal, compare
  525  *               distributions, then compares the names.
  526  */
  527 
  528 int rpmVendorCmp(const rpmData *a, const rpmData *b) {
  529     int res = strcmp(a->vendor, b->vendor);
  530     if (res) return(res);
  531     res = strcmp(a->distribution, b->distribution);
  532     if (res) return(res);
  533     res = rpmNameCmp(a, b);
  534     return(res);
  535 }
  536 
  537 /*
  538  * sort all the RPMs by Vendor.
  539  */
  540 void rpmVendorSort(rpmDataPtr *list, int installed) {
  541     rpmGenericSort(rpmVendorCmp, list, installed);
  542 }
  543 
  544 /*
  545  * rpmDateCmp : comparison of date values, if equal, compare
  546  *              distributions, then compares the names.
  547  */
  548 
  549 int rpmDateCmp(const rpmData *a, const rpmData *b) {
  550     int res = (b->date - a->date) / (60 * 60 * 12);
  551 
  552     if (res) return(res);
  553     res = rpmNameCmp(a, b);
  554     return(res);
  555 }
  556 
  557 /*
  558  * sort all the RPMs by Date.
  559  */
  560 void rpmDateSort(rpmDataPtr *list, int installed) {
  561     rpmGenericSort(rpmDateCmp, list, installed);
  562 }
  563 
  564 /* Compare two version or release numbers.  If there are leading
  565    digits convert them to an integer and compare those.  If they
  566    are equal and a "." follows, skip the "." and repeat.  Compare
  567    any remaining non-digits using strcasecmp.   Note that the
  568    numerical sense of this comparison is reversed, so that higher
  569    version numbers (and thus more recent packages) appear first. */
  570 
  571 static int vercmp(const char *a, const char *b)
  572 {
  573   while (1) {
  574     /* If we're at the end of both they are equal */
  575     if (!*a && !*b) return 0;
  576     /* If we're at the end of a then b is more recent */
  577     else if (!*a) return -1;
  578     /* If we're at the end of b then a is more recent */
  579     else if (!*b) return 1;
  580     /* If we see non-digits compare lexically */
  581     else if (!isdigit(*a) || !isdigit(*b))
  582       return strcasecmp(a, b);
  583     else {
  584       /* compare the integers at the beginning.  The larger number is
  585          more recent. */
  586       int diff = atoi(a) - atoi(b);
  587       if (diff != 0) return diff;
  588       /* If they are equal, skip past them */
  589       else {
  590     while (isdigit(*a)) a++;
  591     while (isdigit(*b)) b++;
  592       }
  593     }
  594     /* If we don't see any version number separators compare lexically */
  595     if (*a != '.' && *b != '.') return strcasecmp(a, b);
  596     /* If a's version number ended and b continues, b is more recent. */
  597     else if (*a != '.') return -1;
  598     /* Conversely, here a is more recent */
  599     else if (*b != '.') return 1;
  600     /* Skip past the periods and comparison the next version element */
  601     else {a += 1; b += 1;}
  602   }
  603 }
  604 
  605 /*
  606  * rpmNameCmp : comparison of names.
  607  */
  608 
  609 int rpmNameCmp(const rpmData *a, const rpmData *b) {
  610     int res;
  611     const char *ap = a->name;
  612     const char *bp = b->name;
  613     /* This algorithm for locating the subpackage name is flawed, as
  614        it is possible for either the main package name or the sub
  615        package name to contain a dash. */
  616 #if 1
  617     /* Here we scan from the right, assuming that the sub-package name
  618        contains no dashes. */
  619     const char *asp = &ap[strlen(ap)-1];
  620     const char *bsp = &bp[strlen(bp)-1];
  621     while (asp > ap && *asp != '-') asp -= 1;   /* scan to subpackage name */
  622     while (bsp > bp && *bsp != '-') bsp -= 1;   /* (if any.) */
  623     if (asp == ap) asp = &ap[strlen(ap)];   /* No subpackage, go to nul */
  624     if (bsp == bp) bsp = &bp[strlen(bp)];   /*   at end of string */
  625 #else    
  626     /* Here we scan from the left, assuming that the main package name
  627        contains no dashes. */
  628     const char *asp = ap;
  629     const char *bsp = bp;
  630     while (*asp && *asp != '-') asp += 1;   /* scan to subpackage name */
  631     while (*bsp && *bsp != '-') bsp += 1;   /* (if any.) */
  632 #endif
  633 
  634     /* Primary sort key is the package name, then the version number,
  635        then the release number, and only then the subpackage name. */
  636 #define min(a,b) (((a)<(b))?(a):(b))
  637     res = strncasecmp(ap, bp, min(asp - ap, bsp - bp));
  638     /* A shorter name is lexically less. */
  639     if (res == 0) res = (asp - ap) - (bsp - bp);
  640     /* Later version numbers should appear first. */
  641     if (res == 0) res = vercmp(b->version, a->version);
  642     /* Same for release numbers. */
  643     if (res == 0) res = vercmp(b->release, a->release);
  644     /* Sort on sub-package name */
  645     if (res == 0) res = strcasecmp(asp, bsp);
  646     return(res);
  647 }
  648 
  649 /*
  650  * sort all the RPMs by Name.
  651  */
  652 void rpmNameSort(rpmDataPtr *list, int installed) {
  653     rpmGenericSort(rpmNameCmp, list, installed);
  654     rpmGenericSortSoftware(rpmNameCmp, installed);
  655 }
  656 
  657 /*
  658  * rpmVersionCmp : comparison of version
  659  */
  660 
  661 int rpmVersionCmp(const rpmDataPtr *a, const rpmDataPtr *b) {
  662     int res = strcmp((*a)->name, (*b)->name);
  663     if (res) return(res);
  664     res = strcmp((*b)->version, (*a)->version);
  665     if (res) return(res);
  666     res = strcmp((*b)->release, (*a)->release);
  667     return(res);
  668 }
  669 
  670 /*
  671  * sort all RPM providing a resource by version number
  672  */
  673 void rpmRessSort(rpmRessPtr ress) {
  674     if (ress == NULL) return;
  675     if (ress->provider == NULL) return;
  676     if (ress->nb_provider <= 1) return;
  677     qsort(ress->provider, ress->nb_provider, sizeof(rpmDataPtr),
  678           (qsortCmpFunc) rpmVersionCmp);
  679 }
  680 
  681 /*
  682  * Create a subdirectory of a given directory
  683  */
  684 
  685 rpmSubdirPtr rpmNewSubdir(rpmSubdirPtr dir, const char *name,
  686       const char *htmlpath, const char *rpmpath, const char *color,
  687       int html) {
  688     rpmSubdirPtr res;
  689 
  690     res  = (rpmSubdirPtr) xmlMalloc(sizeof(rpmSubdir));
  691     if (res == NULL) {
  692         fprintf(stderr, "rpmNewSubdir : running out of memory !\n");
  693     exit(1);
  694     }
  695     res->max_subdirs = 8;
  696     res->subdirs = (rpmSubdirPtr *) 
  697             xmlMalloc(res->max_subdirs * sizeof(rpmSubdirPtr));
  698     if (res->subdirs == NULL) {
  699         fprintf(stderr, "rpmNewSubdir : running out of memory !\n");
  700     exit(1);
  701     }
  702     res->name = xmlStrdup(name);
  703     res->nb_subdirs = 0;
  704     if (color)
  705         res->color = xmlStrdup(color);
  706     else
  707         res->color = NULL;
  708     res->html = html;
  709 
  710     memset(res->subdirs, 0, sizeof(res->subdirs));
  711     if (dir == NULL) {
  712         res->htmlpath = xmlStrdup(name);
  713     res->parent = NULL;
  714     } else {
  715         res->parent = dir;
  716     if (dir->parent != NULL) /* top level dir has alway html = 1 */
  717         res->html = dir->html;
  718     if (htmlpath == NULL) {
  719         if (dir->htmlpath[0] != '\0') {
  720         char buf[500];
  721         snprintf(buf, sizeof(buf), "%s/%s", dir->htmlpath, name);
  722         res->htmlpath = xmlStrdup(buf);
  723         } else
  724         res->htmlpath = xmlStrdup(name);
  725     } else
  726         res->htmlpath = xmlStrdup(htmlpath);
  727     if (rpmpath == NULL) {
  728         if (dir->rpmpath[0] != '\0') {
  729         char buf[500];
  730         snprintf(buf, sizeof(buf), "%s/%s", dir->rpmpath, name);
  731         res->rpmpath = xmlStrdup(buf);
  732         } else
  733         res->rpmpath = xmlStrdup(name);
  734     } else
  735         res->rpmpath = xmlStrdup(rpmpath);
  736     if (dir->nb_subdirs >= dir->max_subdirs) {
  737         dir->max_subdirs *= 2;
  738         dir->subdirs = (rpmSubdirPtr *) xmlRealloc(dir->subdirs,
  739                      dir->max_subdirs * sizeof(rpmSubdirPtr));
  740         if (dir->subdirs == NULL) {
  741         fprintf(stderr, "rpmNewSubdir : running out of memory !\n");
  742         exit(1);
  743         }
  744     }
  745     dir->subdirs[dir->nb_subdirs++] = res;
  746     }
  747     return(res);
  748 }
  749 
  750 /*
  751  * Remove one subdirectory from it's parent.
  752  */
  753 
  754 void rpmRemoveSubdir(rpmSubdirPtr tree) {
  755     int i;
  756     rpmSubdirPtr cur = tree->parent;
  757 
  758     if (cur == NULL) return;
  759 
  760     /*
  761      * search the child in the parent list.
  762      */
  763     for (i = 0;i < cur->nb_subdirs;i++)
  764         if (cur->subdirs[i] == tree) break;
  765     if (i >= cur->nb_subdirs) {
  766         fprintf(stderr, "rpmRemoveSubdir : child not referenced by parent\n");
  767     return;
  768     }
  769 
  770     /*
  771      * Decrement the parent count, and shift the end of the array.
  772      */
  773     cur->nb_subdirs--;
  774     for (;i < cur->nb_subdirs;i++)
  775         cur->subdirs[i] = cur->subdirs[i + 1];
  776     cur->subdirs[i] = NULL;
  777 }
  778 
  779 /*
  780  * Free a full directory tree.
  781  */
  782 
  783 void rpmFreeSubdir(rpmSubdirPtr tree) {
  784     int i;
  785 
  786     for (i = 0;i < tree->nb_subdirs; i++)
  787          rpmFreeSubdir(tree->subdirs[i]);
  788     if (tree->name != NULL) xmlFree(tree->name);
  789     if (tree->htmlpath != NULL) xmlFree(tree->htmlpath);
  790     if (tree->rpmpath != NULL) xmlFree(tree->rpmpath);
  791     if (tree->color != NULL) xmlFree(tree->color);
  792     tree->parent = NULL;
  793     xmlFree(tree->subdirs);
  794     xmlFree(tree);
  795 }
  796 
  797 /*
  798  * print all RPM data blocks
  799  */
  800 void rpmDataPrintAll(void) {
  801     rpmDataPtr cur = rpmSoftwareList;
  802 
  803     while (cur != NULL) {
  804         rpmDataPrint(cur);
  805     cur = cur->next;
  806     }
  807 }
  808 
  809 /*
  810  * Try to extract an E-mail address from a string.
  811  * The string is truncated to the beginning of the E-Mail.
  812  * e.g. "Joe Average <joe@average.org>" should be truncated
  813  *      to "Joe Average"
  814  */
  815 
  816 char *extractEMail(char *string) {
  817     static char buf[200];
  818     char *start;
  819     char *end;
  820 
  821     start = strchr(string, '@');
  822     if (start == NULL) return(NULL);
  823     end = start;
  824     do {
  825     start--;
  826     if ((!isprint(*start)) ||
  827         (*start == ' ') || (*start == '\t') ||
  828         (*start == '\n') || (*start == '\r') ||
  829         (*start == '@') || (*start == '<') ||
  830         (*start == '>') || (*start == '(') || (*start == ')') ||
  831         (*start == '[') || (*start == ']') ||
  832         (*start == ';') || (*start == ',') || (*start == ',')) {
  833         start++;
  834         break;
  835     }
  836     } while (start != string);
  837     do {
  838     end++;
  839     if ((!isprint(*end)) ||
  840         (*start == ' ') || (*start == '\t') ||
  841         (*start == '\n') || (*start == '\r') ||
  842         (*end == '@') || (*end == '<') ||
  843         (*end == '>') || (*end == '(') || (*end == ')') ||
  844         (*end == '[') || (*end == ']') ||
  845         (*end == ';') || (*end == ',') || (*end == ',')) {
  846         break;
  847     }
  848     } while (*end != '\0');
  849     strncpy(buf, start, end - start);
  850     buf[end - start] = '\0';
  851     return(buf);
  852 }
  853 
  854 /*
  855  * Try to extract an URL address from a string.
  856  * The string is truncated to the beginning of the URL
  857  * e.g. "Nifty Project http://www.nifty.org/project/"
  858  *      to "Nifty Project"
  859  */
  860 
  861 char *extractURL(char *string) {
  862     static char buf[500];
  863     char *start;
  864     char *end;
  865 
  866     start = strstr(string, "http://");
  867     if (start == NULL) start = strstr(string, "ftp://");
  868     if (start == NULL) return(NULL);
  869     end = start;
  870     do {
  871     start--;
  872     if ((!isprint(*start)) ||
  873         (*start == ' ') || (*start == '\t') ||
  874         (*start == '\n') || (*start == '\r') ||
  875         (*start == '@') || (*start == '<') ||
  876         (*start == '>') || (*start == '(') || (*start == ')') ||
  877         (*start == '[') || (*start == ']') ||
  878         (*start == ';') || (*start == ',') || (*start == ',')) {
  879         start++;
  880         break;
  881     }
  882     } while (start != string);
  883     do {
  884     end++;
  885     if ((!isprint(*end)) ||
  886         (*start == ' ') || (*start == '\t') ||
  887         (*start == '\n') || (*start == '\r') ||
  888         (*end == '@') || (*end == '<') ||
  889         (*end == '>') || (*end == '(') || (*end == ')') ||
  890         (*end == '[') || (*end == ']') ||
  891         (*end == ';') || (*end == ',') || (*end == ',')) {
  892         break;
  893     }
  894     } while (*end != '\0');
  895     strncpy(buf, start, end - start);
  896     buf[end - start] = '\0';
  897     return(buf);
  898 }
  899 
  900 /*
  901  * Try to protect an e-mail address from a spam bot.
  902  * The "<someone@domain.tld>" is replaced
  903  *      with "<someone(at)domain.tld>".
  904  */
  905 
  906 static unsigned char *buffer = NULL;
  907 static int buffer_size = 2000;
  908 static char *at = "(at)";
  909 char *protectEmail(char *string) {
  910     if (rpm2html_protect_emails == 0 || string == NULL)
  911         return string;
  912 
  913     unsigned char *cur, *end;
  914     unsigned char c;
  915 
  916     if (buffer == NULL) {
  917         buffer = (char *) xmlMalloc(buffer_size * sizeof(char));
  918     if (buffer == NULL) {
  919         perror("xmlMalloc failed");
  920         exit(1);
  921     }
  922     }
  923     cur = &buffer[0];
  924     end = &buffer[buffer_size - 20];
  925 
  926     while (*string != '\0') {
  927         if (cur >= end) {
  928         int delta = cur - buffer;
  929 
  930         buffer_size *= 2;
  931         buffer = (char *) xmlRealloc(buffer, buffer_size * sizeof(char));
  932         if (buffer == NULL) {
  933         perror("xmlMalloc failed");
  934         exit(1);
  935         }
  936         end = &buffer[buffer_size - 20];
  937         cur = &buffer[delta];
  938     }
  939         c = (unsigned char) *(string++);
  940     if (c == '@') {
  941         strcpy(cur, at);
  942         cur += strlen(at);
  943     } else {
  944         *(cur++) = c;
  945     }
  946     }
  947     *cur = '\0';
  948     return(buffer);
  949 }
  950 
  951 /*
  952  * Create a root directory for the installation space, i.e. the
  953  * filesystem that would be created if the packages were installed
  954  * on a real system
  955  * Basic array allocation is 16 items.
  956  */
  957 
  958 rpmRealDirPtr rpmCreateRealRoot(void) {
  959     rpmRealDirPtr res;
  960 
  961     res = (rpmRealDirPtr) xmlMalloc(sizeof(rpmRealDir));
  962     if (res == NULL) {
  963         fprintf(stderr, "rpmCreateRealRoot : out of memory !\n");
  964     exit(1);
  965     }
  966     res->parent = NULL;
  967     res->name = xmlStrdup("/");
  968     res->nb_elem = 0;
  969     res->elems = (void *) xmlMalloc(16 * sizeof(void *));
  970     res->names = (void *) xmlMalloc(16 * sizeof(char *));
  971     res->flags = (char *) xmlMalloc(16 * sizeof(char));
  972     if ((res->elems == NULL) || (res->flags == NULL) || (res->names == NULL)) {
  973         fprintf(stderr, "rpmCreateRealRoot : out of memory !\n");
  974     exit(1);
  975     }
  976     res->max_elem = 16;
  977     return(res);
  978 }
  979 
  980 /*
  981  * Add a given file to a directory.
  982  */
  983 
  984 int rpmGetRealFile(rpmRealDirPtr parent, const char *file, void *elem) {
  985     int i, min, max;
  986     int res;
  987 
  988     /*
  989      * Search the index for the new file.
  990      */
  991     min = i = 0;
  992     if (parent->nb_elem == 0) goto add_first_elem;
  993     max = parent->nb_elem - 1;
  994 
  995     while (min < max) {
  996         i = min + max;
  997     i /= 2;
  998 
  999         res = strcmp(file, parent->names[i]);
 1000     if (res < 0) max = i - 1;
 1001     else if (res > 0) min = i + 1;
 1002     else {
 1003         return(i);
 1004     }
 1005     }
 1006     i = min;
 1007     res = strcmp(file, parent->names[i]);
 1008     if (res == 0) {
 1009         return(i);
 1010     } else if (res > 0) i++;
 1011 
 1012     /*
 1013      * If more space is needed grow the arrays.
 1014      */
 1015     if (parent->nb_elem >= parent->max_elem) {
 1016         parent->max_elem *= 2;
 1017         parent->elems = (void *)
 1018             xmlRealloc(parent->elems, parent->max_elem * sizeof(void *));
 1019         parent->names = (void *)
 1020             xmlRealloc(parent->names, parent->max_elem * sizeof(char *));
 1021         parent->flags = (void *)
 1022             xmlRealloc(parent->flags, parent->max_elem * sizeof(char));
 1023     if ((parent->elems == NULL) || (parent->flags == NULL) ||
 1024         (parent->names == NULL)) {
 1025         fprintf(stderr, "rpmAddRealFile : out of memory !\n");
 1026         exit(1);
 1027     }
 1028     }
 1029 
 1030     /*
 1031      * Shift the end of the arrays.
 1032      */
 1033 
 1034     if (i < parent->nb_elem) {
 1035         memmove(&parent->elems[i + 1], &parent->elems[i], 
 1036             (parent->nb_elem - i) * sizeof(void *));
 1037         memmove(&parent->names[i + 1], &parent->names[i], 
 1038             (parent->nb_elem - i) * sizeof(char *));
 1039         memmove(&parent->flags[i + 1], &parent->flags[i], 
 1040             (parent->nb_elem - i) * sizeof(char));
 1041     }
 1042 add_first_elem:
 1043     parent->nb_elem++;
 1044 
 1045     /*
 1046      * Create the new entry.
 1047      */
 1048     parent->elems[i] = elem;
 1049     parent->names[i] = xmlStrdup(file);
 1050     parent->flags[i] = 0;
 1051     return(i);
 1052 }
 1053 
 1054 /*
 1055  * Get a given subdirectory of a directory. If it doesn't exist,
 1056  * the subdir is added. The filename may have been added as a file
 1057  * in that case turn it into a directory.
 1058  */
 1059 
 1060 rpmRealDirPtr rpmGetRealSubdir(rpmRealDirPtr parent, const char *subdir) {
 1061     rpmRealDirPtr res;
 1062     int i = rpmGetRealFile(parent, subdir, NULL);
 1063 
 1064     /*
 1065      * Check if such a directory already exists.
 1066      */
 1067     if (parent->flags[i] & RPM_ELEM_DIRECTORY) {
 1068         return((rpmRealDirPtr) parent->elems[i]);
 1069     }
 1070 
 1071     /*
 1072      * Allocate a new rpmRealDir block and link it in.
 1073      */
 1074 
 1075     res = (rpmRealDirPtr) xmlMalloc(sizeof(rpmRealDir));
 1076     if (res == NULL) {
 1077         fprintf(stderr, "rpmCreateRealRoot : out of memory !\n");
 1078     exit(1);
 1079     }
 1080     res->parent = parent;
 1081     res->name = xmlStrdup(subdir);
 1082     res->nb_elem = 0;
 1083     res->elems = (void *) xmlMalloc(16 * sizeof(void *));
 1084     res->names = (void *) xmlMalloc(16 * sizeof(char *));
 1085     res->flags = (char *) xmlMalloc(16 * sizeof(char));
 1086     if ((res->elems == NULL) || (res->flags == NULL) || (res->names == NULL)) {
 1087         fprintf(stderr, "rpmCreateRealRoot : out of memory !\n");
 1088     exit(1);
 1089     }
 1090     res->max_elem = 16;
 1091 
 1092     parent->elems[i] = res;
 1093     parent->flags[i] = RPM_ELEM_DIRECTORY;
 1094     return(res);
 1095 }
 1096 
 1097 /*
 1098  * Add a file to the real filesystem tree, and all memorize which
 1099  * RPM provided it
 1100  */
 1101 
 1102 void rpmAddRealFile(rpmRealDirPtr root, const char *file, rpmDataPtr rpm) {
 1103     static char path[2000];
 1104     char *filename;
 1105     char *cur, save;
 1106     rpmRealDirPtr curDir = root;
 1107 
 1108     if (root == NULL) return;
 1109     if (file == NULL) return;
 1110     if (rpm == NULL) return;
 1111     strncpy(path, file, sizeof(path));
 1112     filename = &path[0];
 1113     if (*filename != '/') {
 1114         if (rpm2htmlVerbose > 1) 
 1115         fprintf(stderr, "rpmAddRealFile : %s invalid\n", file);
 1116     return;
 1117     }
 1118     cur = ++filename;
 1119     while (*cur != '\0') {
 1120         if (*cur == '/') {
 1121         *cur = '\0';
 1122         curDir = rpmGetRealSubdir(curDir, filename);
 1123         *cur = '/';
 1124         cur++;
 1125         while (*cur == '/') cur++;
 1126         filename = cur;
 1127     } else if ((*cur == ' ') || (*cur == '\t') ||
 1128                (*cur == '\n') || (*cur == '\r')) {
 1129         break;
 1130     } else
 1131         cur++;
 1132     }
 1133     if (*filename == '\0') return;
 1134     save = *cur;
 1135     *cur = '\0';
 1136     rpmGetRealFile(curDir, filename, rpm);
 1137     *cur = save;
 1138 }
 1139 
 1140 /*
 1141  * Merge two real tree, this is somewhat messy since
 1142  * we keep references to only one source package even if
 1143  * two different distributions carries the same file (which
 1144  * have a high probability).
 1145  *
 1146  * NOTE : the old trees are destoyed !!!
 1147  */
 1148 
 1149 rpmRealDirPtr rpmMergeRealRoots(rpmRealDirPtr root1, rpmRealDirPtr root2) {
 1150     rpmRealDirPtr base, add;
 1151     int j;
 1152 
 1153     if (root1 == NULL) return(root2);
 1154     if (root2 == NULL) return(root1);
 1155 
 1156     /*
 1157      * We keep the bigger one as-is and add-in the smaller one.
 1158      */
 1159     if (root1->nb_elem > root2->nb_elem) {
 1160         base = root1;
 1161     add = root2;
 1162     } else {
 1163         base = root2;
 1164     add = root1;
 1165     }
 1166 
 1167     /*
 1168      * For every element in add, search for the same entry in base.
 1169      * If both exists, keep only the directories, if both are directories,
 1170      * recurse !
 1171      */
 1172     for (j = 0;j < add->nb_elem;j++) {
 1173     int i, min, max;
 1174     int res;
 1175 
 1176     /*
 1177      * Search the index for the new file.
 1178      */
 1179     min = i = 0;
 1180     max = base->nb_elem - 1;
 1181 
 1182     while (min < max) {
 1183         i = min + max;
 1184         i /= 2;
 1185 
 1186         res = strcmp(add->names[j], base->names[i]);
 1187         if (res < 0)
 1188             max = i - 1;
 1189         else if (res > 0)
 1190             min = i + 1;
 1191         else 
 1192             goto entry_found;
 1193     }
 1194     i = min;
 1195     res = strcmp(add->names[j], base->names[i]);
 1196     if (res == 0) {
 1197         goto entry_found;
 1198     } else if (res > 0) i++;
 1199 
 1200     /*
 1201      * The name was not found in base, add it !
 1202      * If more space is needed grow the arrays.
 1203      */
 1204     if (base->nb_elem >= base->max_elem) {
 1205         base->max_elem *= 2;
 1206         base->elems = (void *)
 1207             xmlRealloc(base->elems, base->max_elem * sizeof(void *));
 1208         base->names = (void *)
 1209             xmlRealloc(base->names, base->max_elem * sizeof(char *));
 1210         base->flags = (void *)
 1211             xmlRealloc(base->flags, base->max_elem * sizeof(char));
 1212         if ((base->elems == NULL) || (base->flags == NULL) ||
 1213         (base->names == NULL)) {
 1214         fprintf(stderr, "rpmMergeRealRoots : out of memory !\n");
 1215         exit(1);
 1216         }
 1217     }
 1218 
 1219     /*
 1220      * Shift the end of the arrays.
 1221      */
 1222 
 1223     if (i < base->nb_elem) {
 1224         memmove(&base->elems[i + 1], &base->elems[i], 
 1225             (base->nb_elem - i) * sizeof(void *));
 1226         memmove(&base->names[i + 1], &base->names[i], 
 1227             (base->nb_elem - i) * sizeof(char *));
 1228         memmove(&base->flags[i + 1], &base->flags[i], 
 1229             (base->nb_elem - i) * sizeof(char));
 1230     }
 1231     base->nb_elem++;
 1232 
 1233     /*
 1234      * Create the new entry.
 1235      */
 1236     base->elems[i] = add->elems[j];
 1237     base->names[i] = add->names[j];
 1238     base->flags[i] = add->flags[j];
 1239     continue;
 1240 
 1241 entry_found:
 1242         /*
 1243      * the name is present in both directories.
 1244      */
 1245     if ((base->flags[i] & RPM_ELEM_DIRECTORY) &&
 1246         (add->flags[j] & RPM_ELEM_DIRECTORY)) {
 1247         /* Two subdirectories, merge them */
 1248         base->elems[i] = rpmMergeRealRoots(base->elems[i], add->elems[j]);
 1249     /* --------------
 1250     } else if (base->flags[i] & RPM_ELEM_DIRECTORY) {
 1251          Keep base directory 
 1252        -------------- */
 1253     } else if (add->flags[j] & RPM_ELEM_DIRECTORY) {
 1254         void *ptr;
 1255         int val;
 1256 
 1257         /* Keep add directory */
 1258         ptr = base->names[i] ;
 1259         base->names[i] = add->names[j];
 1260         add->names[j] = ptr;
 1261         ptr = base->elems[i];
 1262         base->elems[i] = add->elems[j];
 1263         add->elems[j] = ptr; 
 1264         val = base->flags[i];
 1265         base->flags[i] = add->flags[j];
 1266         add->flags[j] = val;
 1267     /* --------------
 1268     } else {
 1269          Two files, arbitrarily keep base one, this is simpler ! 
 1270        -------------- */
 1271     }
 1272     }
 1273     rpmDestroyRealRoot(add);
 1274     return(base);
 1275 }
 1276 
 1277 /*
 1278  * Destroy a real tree root.
 1279  */
 1280 
 1281 void rpmDestroyRealRoot(rpmRealDirPtr dir) {
 1282     int i;
 1283 
 1284     for (i = 0;i < dir->nb_elem;i++) {
 1285         if (dir->flags[i] & RPM_ELEM_DIRECTORY) {
 1286         rpmDestroyRealRoot(dir->elems[i]);
 1287     }
 1288     xmlFree(dir->names[i]);
 1289     }
 1290     if (dir->elems) xmlFree(dir->elems);
 1291     if (dir->names) xmlFree(dir->names);
 1292     if (dir->flags) xmlFree(dir->flags);
 1293     if (dir->name) xmlFree(dir->name);
 1294     xmlFree(dir);
 1295 }
 1296 
 1297 /*
 1298  * Cleanup of the packages lists
 1299  */
 1300 void rpmlistCleanup(void) {
 1301     int i;
 1302 
 1303 fprintf(stderr, "rpmlistCleanup\n");
 1304 
 1305     rpmDataListFree(&rpmSoftwareList);
 1306     rpmRessListFree(&ressList);
 1307     rpmRessListFree(&ressInstalledList);
 1308     for (i = 0; i < HASH_SIZE;i++) {
 1309         rpmHashTable[i] = NULL;
 1310         rpmRessHashTable[i] = NULL;
 1311     }
 1312     rpmHashInitialize();
 1313 }
 1314 
 1315 /*
 1316  * Cleanup of this module global varibales.
 1317  */
 1318 void rpmdataCleanup(void) {
 1319     int i;
 1320 
 1321     rpmDirListFree(&dirList);
 1322     rpmDataListFree(&rpmSoftwareList);
 1323     rpmRessListFree(&ressList);
 1324     rpmRessListFree(&ressInstalledList);
 1325     for (i = 0; i < HASH_SIZE;i++) {
 1326         rpmHashTable[i] = NULL;
 1327         rpmRessHashTable[i] = NULL;
 1328     }
 1329     dirList = NULL;
 1330     dirTree = NULL;
 1331     treeRoot = NULL;
 1332     rpmHashInitialize();
 1333 }
 1334