home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume24 / mkid2 / part05 / iidfun.c next >
Encoding:
C/C++ Source or Header  |  1991-10-09  |  16.7 KB  |  686 lines

  1. /* iidfun.c - This file holds the utility functions called from iid.y
  2.  */
  3.  
  4. #include "iiddef.h"
  5. #ifdef USE_ALLOCA
  6.  
  7. /* Apparently Sun's need the following to work correctly */
  8. #ifdef sun
  9. #include <alloca.h>
  10. #endif /* sun */
  11.  
  12. /* Define TEMP_ALLOC to call alloca, and TEMP_FREE to do nothing */
  13.  
  14. #define TEMP_ALLOC(s) alloca(s)
  15. #define TEMP_FREE(s)
  16.  
  17. #else
  18.  
  19. /* Not using alloca() (not everyone has it) - define TEMP_ALLOC to call
  20.  * malloc() and TEMP_FREE to call free().
  21.  */
  22. #define TEMP_ALLOC(s) malloc(s)
  23. #define TEMP_FREE(s)  free(s)
  24.  
  25. #endif /* USE_ALLOCA */
  26.  
  27. /* ArgListSize - count the size of an arg list so can alloca() enough
  28.  * space for the command.
  29.  */
  30. int
  31. ArgListSize(idlp)
  32.    id_list_type * idlp ;
  33. {
  34.    id_type *      idep ;
  35.    int            size = 0;
  36.    
  37.    idep = idlp->id_list ;
  38.    while (idep != NULL) {
  39.       size += 1 + strlen(idep->id);
  40.       idep = idep->next_id;
  41.    }
  42.    return size;
  43. }
  44.  
  45. /* SetListSize - count the size of a string build up from a set so we can
  46.  * alloca() enough space for args.
  47.  */
  48. int
  49. SetListSize(sp)
  50.    set_type * sp ;
  51. {
  52.    int            i ;
  53.    int            size = 0 ;
  54.    
  55.    for (i = 0; i < NextFileNum; ++i) {
  56.       if (FileList[i]->mask_word < sp->set_size) {
  57.          if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) {
  58.             size += 1 + strlen(FileList[i]->name);
  59.          }
  60.       }
  61.    }
  62.    return size;
  63. }
  64.  
  65. /* FlushFiles - clear out the TheFiles array for the start of a new
  66.  * query.
  67.  */
  68. void
  69. FlushFiles()
  70. {
  71.    int             i ;
  72.    
  73.    if (TheFiles != NULL) {
  74.       for (i = 0; i <= MaxCurFile; ++i) {
  75.          TheFiles[i] = 0 ;
  76.       }
  77.    }
  78.    MaxCurFile = 0 ;
  79. }
  80.  
  81. /* fatal - sometimes the only thing to do is die...
  82.  */
  83. void
  84. fatal(s)
  85. {
  86.    fprintf(stderr,"Fatal error: %s\n",s) ;
  87.    exit(1) ;
  88. }
  89.  
  90. /* CountBits - count the number of bits in a bit set. Actually fairly
  91.  * tricky since it needs to deal with sets having infinite tails
  92.  * as a result of a NOT operation.
  93.  */
  94. int
  95. CountBits(sp)
  96.    set_type * sp ;
  97. {
  98.    unsigned long      bit_mask ;
  99.    int                count = 0 ;
  100.    int                i ;
  101.    
  102.    i = 0;
  103.    for ( ; ; ) {
  104.       for (bit_mask = high_bit; bit_mask != 0; bit_mask >>= 1) {
  105.          if (bit_mask == NextMaskBit && i == NextMaskWord) {
  106.             return(count) ;
  107.          }
  108.          if (i < sp->set_size) {
  109.             if (sp->set_data[i] & bit_mask) {
  110.                ++count ;
  111.             }
  112.          } else {
  113.             if (sp->set_tail == 0) return count;
  114.             if (sp->set_tail & bit_mask) {
  115.                ++count;
  116.             }
  117.          }
  118.       }
  119.       ++i;
  120.    }
  121. }
  122.  
  123. /* OneDescription - Print a description of a set. This includes
  124.  * the set number, the number of files in the set, and the
  125.  * set description string.
  126.  */
  127. void
  128. OneDescription(sp)
  129.    set_type * sp ;
  130. {
  131.    int        elt_count ;
  132.    char       setnum[20] ;
  133.    
  134.    sprintf(setnum,"S%d",sp->set_num) ;
  135.    elt_count = CountBits(sp) ;
  136.    printf("%5s %6d  %s\n",setnum,elt_count,sp->set_desc) ;
  137. }
  138.  
  139. /* DescribeSets - Print description of all the sets.
  140.  */
  141. void
  142. DescribeSets()
  143. {
  144.    int            i ;
  145.  
  146.    if (NextSetNum > 0) {
  147.       for (i = 0; i < NextSetNum; ++i) {
  148.          OneDescription(TheSets[i]) ;
  149.       }
  150.    } else {
  151.       printf("No sets defined yet.\n") ;
  152.    }
  153. }
  154.  
  155. /* SetList - Go through the bit set and add the file names in
  156.  * it to an identifier list.
  157.  */
  158. id_list_type *
  159. SetList(idlp, sp)
  160.    id_list_type *  idlp ;
  161.    set_type *      sp ;
  162. {
  163.    int        i ;
  164.    id_type *  idep ;
  165.    
  166.    for (i = 0; i < NextFileNum; ++i) {
  167.       if (FileList[i]->mask_word < sp->set_size) {
  168.          if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) {
  169.             idep = (id_type *)malloc(sizeof(id_type) +
  170.                                      strlen(FileList[i]->name)) ;
  171.             if (idep == NULL) {
  172.                fatal("Out of memory in SetList") ;
  173.             }
  174.             idep->next_id = NULL ;
  175.             strcpy(idep->id, FileList[i]->name) ;
  176.             idlp = ExtendList(idlp, idep) ;
  177.          }
  178.       }
  179.    }
  180.    return(idlp) ;
  181. }
  182.  
  183. /* PrintSet - Go through the bit set and print the file names
  184.  * corresponding to all the set bits.
  185.  */
  186. void
  187. PrintSet(sp)
  188.    set_type * sp ;
  189. {
  190.    int        i ;
  191.    
  192.    for (i = 0; i < NextFileNum; ++i) {
  193.       if (FileList[i]->mask_word < sp->set_size) {
  194.          if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) {
  195.             printf("%s\n",FileList[i]->name) ;
  196.          }
  197.       }
  198.    }
  199. }
  200.  
  201. /* Free up all space used by current set of sets and reset all
  202.  * set numbers.
  203.  */
  204. void
  205. FlushSets()
  206. {
  207.    int         i ;
  208.    
  209.    for (i = 0; i < NextSetNum; ++i) {
  210.       free(TheSets[i]->set_desc) ;
  211.       free(TheSets[i]) ;
  212.    }
  213.    NextSetNum = 0 ;
  214. }
  215.  
  216. /* InitList - create an empty identifier list.
  217.  */
  218. id_list_type *
  219. InitList()
  220. {
  221.    id_list_type *   idlp ;
  222.    
  223.    idlp = (id_list_type *)malloc(sizeof(id_list_type)) ;
  224.    if (idlp == NULL) {
  225.       fatal("Out of memory in InitList") ;
  226.    }
  227.    idlp->id_count = 0 ;
  228.    idlp->end_ptr_ptr = & (idlp->id_list) ;
  229.    idlp->id_list = NULL ;
  230.    return(idlp) ;
  231. }
  232.  
  233. /* ExtendList - add one identifier to an ID list.
  234.  */
  235. id_list_type *
  236. ExtendList(idlp, idp)
  237.    id_list_type * idlp ;
  238.    id_type *      idp ;
  239. {
  240.    *(idlp->end_ptr_ptr) = idp ;
  241.    idlp->end_ptr_ptr = &(idp->next_id) ;
  242.    return(idlp) ;
  243. }
  244.  
  245. /* InitIid - do all initial processing for iid.
  246.  *   1) Determine the size of a unsigned long for bit set stuff.
  247.  *   2) Find out the name of the pager program to use.
  248.  *   3) Create the HelpSet (pointing to the help file).
  249.  *   4) Setup the prompt.
  250.  */
  251. void
  252. InitIid()
  253. {
  254.    unsigned long      bit_mask = 1 ;   /* find number of bits in long */
  255.    int                i ;
  256.    char *             page ;           /* pager program */
  257.    
  258.    do {
  259.       high_bit = bit_mask ;
  260.       bit_mask <<= 1 ;
  261.    } while (bit_mask != 0) ;
  262.    
  263.    NextMaskBit = high_bit ;
  264.    
  265.    page = getenv("PAGER") ;
  266.    if (page == NULL) {
  267.       page = PAGER ;
  268.    }
  269.    strcpy(Pager, page) ;
  270.    
  271.    FlushFiles() ;
  272.    InstallFile(HELPFILE) ;
  273.    HelpSet = (set_type *)
  274.       malloc(sizeof(set_type) + sizeof(unsigned long) * MaxCurFile) ;
  275.    if (HelpSet == NULL) {
  276.       fatal("No memory for set in InitIid") ;
  277.    }
  278.    HelpSet->set_tail = 0 ;
  279.    HelpSet->set_desc = NULL ;
  280.    HelpSet->set_size = MaxCurFile + 1 ;
  281.    for (i = 0; i <= MaxCurFile; ++i) {
  282.       HelpSet->set_data[i] = TheFiles[i] ;
  283.    }
  284.    
  285.    page = getenv("PS1") ;
  286.    if (page == NULL) {
  287.       page = PROMPT ;
  288.    }
  289.    strcpy(Prompt, page) ;
  290. }
  291.  
  292. /* InstallFile - install a file name in the symtab. Return the
  293.  * symbol table pointer of the file.
  294.  */
  295. symtab_type *
  296. InstallFile(fp)
  297.    char *      fp ;
  298. {
  299.    char             c ;
  300.    unsigned long    hash_code ;
  301.    int              i ;
  302.    char *           sp ;
  303.    symtab_type *    symp ;
  304.    
  305.    hash_code = 0 ;
  306.    sp = fp ;
  307.    while ((c = *sp++) != '\0') {
  308.       hash_code <<= 1 ;
  309.       hash_code ^= (unsigned long)(c) ;
  310.       if (hash_code & high_bit) {
  311.          hash_code &= ~ high_bit ;
  312.          hash_code ^= 1 ;
  313.       }
  314.    }
  315.    hash_code %= HASH_SIZE ;
  316.    symp = HashTable[hash_code] ;
  317.    while (symp != NULL && strcmp(symp->name, fp)) {
  318.       symp = symp->hash_link ;
  319.    }
  320.    if (symp == NULL) {
  321.       symp = (symtab_type *)malloc(sizeof(symtab_type) + strlen(fp)) ;
  322.       if (symp == NULL) {
  323.          fatal("No memory for symbol table entry in InstallFile") ;
  324.       }
  325.       strcpy(symp->name, fp) ;
  326.       symp->hash_link = HashTable[hash_code] ;
  327.       HashTable[hash_code] = symp ;
  328.       if (NextMaskWord >= FileSpace) {
  329.          FileSpace += 1000 ;
  330.          if (TheFiles != NULL) {
  331.             TheFiles = (unsigned long *)
  332.                realloc(TheFiles, sizeof(unsigned long) * FileSpace) ;
  333.          } else {
  334.             TheFiles = (unsigned long *)
  335.                malloc(sizeof(unsigned long) * FileSpace) ;
  336.          }
  337.          if (TheFiles == NULL) {
  338.             fatal("No memory for TheFiles in InstallFile") ;
  339.          }
  340.          for (i = NextMaskWord; i < FileSpace; ++i) {
  341.             TheFiles[i] = 0 ;
  342.          }
  343.       }
  344.       symp->mask_word = NextMaskWord ;
  345.       symp->mask_bit = NextMaskBit ;
  346.       NextMaskBit >>= 1 ;
  347.       if (NextMaskBit == 0) {
  348.          NextMaskBit = high_bit ;
  349.          ++NextMaskWord ;
  350.       }
  351.       if (NextFileNum >= ListSpace) {
  352.          ListSpace += 1000 ;
  353.          if (FileList == NULL) {
  354.             FileList = (symtab_type **)
  355.                malloc(sizeof(symtab_type *) * ListSpace) ;
  356.          } else {
  357.             FileList = (symtab_type **)
  358.                realloc(FileList, ListSpace * sizeof(symtab_type *)) ;
  359.          }
  360.          if (FileList == NULL) {
  361.             fatal("No memory for FileList in InstallFile") ;
  362.          }
  363.       }
  364.       FileList[NextFileNum++] = symp ;
  365.       /* put code here to sort the file list by name someday */
  366.    }
  367.    TheFiles[symp->mask_word] |= symp->mask_bit ;
  368.    if (symp->mask_word > MaxCurFile) {
  369.       MaxCurFile = symp->mask_word ;
  370.    }
  371.    return(symp) ;
  372. }
  373.  
  374. /* RunPager - run the users pager program on the list of files
  375.  * in the set.
  376.  */
  377. void
  378. RunPager(pp, sp)
  379.    char *     pp ;
  380.    set_type * sp ;
  381. {
  382.    char *         cmd ;
  383.    int            i ;
  384.    id_type *      idep ;
  385.    
  386.    cmd = (char *)TEMP_ALLOC(SetListSize(sp) + strlen(pp) + 2);
  387.    strcpy(cmd, pp) ;
  388.    for (i = 0; i < NextFileNum; ++i) {
  389.       if (FileList[i]->mask_word < sp->set_size) {
  390.          if (sp->set_data[FileList[i]->mask_word] & FileList[i]->mask_bit) {
  391.             strcat(cmd, " ") ;
  392.             strcat(cmd, FileList[i]->name) ;
  393.          }
  394.       }
  395.    }
  396.    system(cmd) ;
  397.    TEMP_FREE(cmd) ;
  398. }
  399.  
  400. /* AddSet - add a new set to the universal list of sets. Assign
  401.  * it the next set number.
  402.  */
  403. void
  404. AddSet(sp)
  405.    set_type *   sp ;
  406. {
  407.    if (NextSetNum >= SetSpace) {
  408.       SetSpace += 1000 ;
  409.       if (TheSets != NULL) {
  410.          TheSets = (set_type **)
  411.             realloc(TheSets, sizeof(set_type *) * SetSpace) ;
  412.       } else {
  413.          TheSets = (set_type **)
  414.             malloc(sizeof(set_type *) * SetSpace) ;
  415.       }
  416.       if (TheSets == NULL) {
  417.          fatal("No memory for TheSets in AddSet") ;
  418.       }
  419.    }
  420.    sp->set_num = NextSetNum ;
  421.    TheSets[NextSetNum++] = sp ;
  422. }
  423.  
  424. /* RunProg - run a program with arguments from id_list and
  425.  * accept list of file names back from the program which
  426.  * are installed in the symbol table and used to construct
  427.  * a new set.
  428.  */
  429. set_type *
  430. RunProg(pp, idlp)
  431.    char *         pp ;
  432.    id_list_type * idlp ;
  433. {
  434.    int            c ;
  435.    char *         cmd ;
  436.    char *         dp ;
  437.    char           file [ MAXCMD ] ;
  438.    int            i ;
  439.    id_type *      idep ;
  440.    id_type *      next_id ;
  441.    FILE *         prog ;
  442.    set_type *     sp ;
  443.    
  444.    cmd = (char *)TEMP_ALLOC(ArgListSize(idlp) + strlen(pp) + 2);
  445.    FlushFiles() ;
  446.    strcpy(cmd, pp) ;
  447.    idep = idlp->id_list ;
  448.    while (idep != NULL) {
  449.       strcat(cmd, " ") ;
  450.       strcat(cmd, idep->id) ;
  451.       next_id = idep->next_id ;
  452.       free(idep) ;
  453.       idep = next_id ;
  454.    }
  455.    free(idlp) ;
  456.    
  457.    /* run program with popen, reading the output. Assume each
  458.     * white space terminated string is a file name.
  459.     */
  460.    prog = popen(cmd, "r") ;
  461.    dp = file ;
  462.    while ((c = getc(prog)) != EOF) {
  463.       if (isspace(c)) {
  464.          if (dp != file) {
  465.             *dp++ = '\0' ;
  466.             InstallFile(file) ;
  467.             dp = file ;
  468.          }
  469.       } else {
  470.          *dp++ = c ;
  471.       }
  472.    }
  473.    if (dp != file) {
  474.       *dp++ = '\0' ;
  475.       InstallFile(file) ;
  476.    }
  477.    if (pclose(prog) != 0) {
  478.       /* if there was an error make an empty set, who knows what
  479.        * garbage the program printed.
  480.        */
  481.       FlushFiles() ;
  482.    }
  483.    
  484.    sp = (set_type *)
  485.       malloc(sizeof(set_type) + sizeof(unsigned long) * MaxCurFile) ;
  486.    if (sp == NULL) {
  487.       fatal("No memory for set in RunProg") ;
  488.    }
  489.    sp->set_tail = 0 ;
  490.    sp->set_desc = (char *)malloc(strlen(cmd) + 1) ;
  491.    if (sp->set_desc == NULL) {
  492.       fatal("No memory for set description in RunProg") ;
  493.    }
  494.    strcpy(sp->set_desc, cmd) ;
  495.    sp->set_size = MaxCurFile + 1 ;
  496.    for (i = 0; i <= MaxCurFile; ++i) {
  497.       sp->set_data[i] = TheFiles[i] ;
  498.    }
  499.    AddSet(sp) ;
  500.    TEMP_FREE(cmd);
  501.    return(sp) ;
  502. }
  503.  
  504. /* SetDirectory - change the working directory. This will
  505.  * determine which ID file is found by the subprograms.
  506.  */
  507. void
  508. SetDirectory(dir)
  509.    id_type *      dir ;
  510. {
  511.    if (chdir(dir->id) != 0) {
  512.       fprintf(stderr,"Directory %s not accessible.\n",dir) ;
  513.    }
  514.    free(dir) ;
  515. }
  516.  
  517. /* SetIntersect - construct a new set from the intersection
  518.  * of two others. Also construct a new description string.
  519.  */
  520. set_type *
  521. SetIntersect(sp1, sp2)
  522.    set_type * sp1 ;
  523.    set_type * sp2 ;
  524. {
  525.    char *     desc ;
  526.    int        i ;
  527.    int        len1 ;
  528.    int        len2 ;
  529.    set_type * new_set ;
  530.    int        new_size ;
  531.    
  532.    if (sp1->set_tail || sp2->set_tail) {
  533.       new_size = MAX(sp1->set_size, sp2->set_size) ;
  534.    } else {
  535.       new_size = MIN(sp1->set_size, sp2->set_size) ;
  536.    }
  537.    new_set = (set_type *)malloc(sizeof(set_type) +
  538.                                 (new_size - 1) * sizeof(unsigned long)) ;
  539.    if (new_set == NULL) {
  540.       fatal("No memory for set in SetIntersect") ;
  541.    }
  542.    len1 = strlen(sp1->set_desc) ;
  543.    len2 = strlen(sp2->set_desc) ;
  544.    desc = (char *)malloc(len1 + len2 + 10) ;
  545.    if (desc == NULL) {
  546.       fatal("No memory for set description in SetIntersect") ;
  547.    }
  548.    new_set->set_desc = desc ;
  549.    strcpy(desc,"(") ;
  550.    ++desc ;
  551.    strcpy(desc, sp1->set_desc) ;
  552.    desc += len1 ;
  553.    strcpy(desc, ") AND (") ;
  554.    desc += 7 ;
  555.    strcpy(desc, sp2->set_desc) ;
  556.    desc += len2 ;
  557.    strcpy(desc, ")") ;
  558.    AddSet(new_set) ;
  559.    new_set->set_size = new_size ;
  560.    for (i = 0; i < new_size; ++i) {
  561.       new_set->set_data[i] = 
  562.          ((i < sp1->set_size) ? sp1->set_data[i] : sp1->set_tail) & 
  563.          ((i < sp2->set_size) ? sp2->set_data[i] : sp2->set_tail) ;
  564.    }
  565.    new_set->set_tail = sp1->set_tail & sp2->set_tail ;
  566.    return(new_set) ;
  567. }
  568.  
  569. /* SetUnion - construct a new set from the union of two others.
  570.  * Also construct a new description string.
  571.  */
  572. set_type *
  573. SetUnion(sp1, sp2)
  574.    set_type * sp1 ;
  575.    set_type * sp2 ;
  576. {
  577.    char *     desc ;
  578.    int        i ;
  579.    int        len1 ;
  580.    int        len2 ;
  581.    set_type * new_set ;
  582.    int        new_size ;
  583.    
  584.    new_size = MAX(sp1->set_size, sp2->set_size) ;
  585.    new_set = (set_type *)malloc(sizeof(set_type) +
  586.                                 (new_size - 1) * sizeof(unsigned long)) ;
  587.    if (new_set == NULL) {
  588.       fatal("No memory for set in SetUnion") ;
  589.    }
  590.    len1 = strlen(sp1->set_desc) ;
  591.    len2 = strlen(sp2->set_desc) ;
  592.    desc = (char *)malloc(len1 + len2 + 9) ;
  593.    if (desc == NULL) {
  594.       fatal("No memory for set description in SetUnion") ;
  595.    }
  596.    new_set->set_desc = desc ;
  597.    strcpy(desc,"(") ;
  598.    ++desc ;
  599.    strcpy(desc, sp1->set_desc) ;
  600.    desc += len1 ;
  601.    strcpy(desc, ") OR (") ;
  602.    desc += 6 ;
  603.    strcpy(desc, sp2->set_desc) ;
  604.    desc += len2 ;
  605.    strcpy(desc, ")") ;
  606.    AddSet(new_set) ;
  607.    new_set->set_size = new_size ;
  608.    for (i = 0; i < new_size; ++i) {
  609.       new_set->set_data[i] =
  610.          ((i < sp1->set_size) ? (sp1->set_data[i]) : sp1->set_tail) |
  611.          ((i < sp2->set_size) ? (sp2->set_data[i]) : sp2->set_tail) ;
  612.    }
  613.    new_set->set_tail = sp1->set_tail | sp2->set_tail ;
  614.    return(new_set) ;
  615. }
  616.  
  617. /* SetInverse - construct a new set from the inverse of another.
  618.  * Also construct a new description string.
  619.  *
  620.  * This is kind of tricky. An inverse set in iid may grow during
  621.  * the course of a session. By NOTing the set_tail extension the
  622.  * inverse at any given time will be defined as the inverse against
  623.  * a universe that grows as additional queries are made and new files
  624.  * are added to the database.
  625.  *
  626.  * Several alternative definitions were possible (snapshot the
  627.  * universe at the time of the NOT, go read the ID file to
  628.  * determine the complete universe), but this one was the one
  629.  * I picked.
  630.  */
  631. set_type *
  632. SetInverse(sp)
  633.    set_type * sp ;
  634. {
  635.    char *     desc ;
  636.    int        i ;
  637.    set_type * new_set ;
  638.    
  639.    new_set = (set_type *)malloc(sizeof(set_type) +
  640.                                 (sp->set_size - 1) * sizeof(unsigned long)) ;
  641.    if (new_set == NULL) {
  642.       fatal("No memory for set in SetInverse") ;
  643.    }
  644.    desc = (char *)malloc(strlen(sp->set_desc) + 5) ;
  645.    if (desc == NULL) {
  646.       fatal("No memory for set description in SetInverse") ;
  647.    }
  648.    new_set->set_desc = desc ;
  649.    strcpy(desc,"NOT ") ;
  650.    desc += 4 ;
  651.    strcpy(desc, sp->set_desc) ;
  652.    AddSet(new_set) ;
  653.    new_set->set_size = sp->set_size ;
  654.    for (i = 0; i < sp->set_size; ++i) {
  655.       new_set->set_data[i] = ~ sp->set_data[i] ;
  656.    }
  657.    new_set->set_tail = ~ sp->set_tail ;
  658.    return(new_set) ;
  659. }
  660.  
  661. /* RunShell - run a program with arguments from id_list.
  662.  */
  663. void
  664. RunShell(pp, idlp)
  665.    char *         pp ;
  666.    id_list_type * idlp ;
  667. {
  668.    char *         cmd ;
  669.    id_type *      idep ;
  670.    id_type *      next_id ;
  671.    
  672.    cmd = (char *)TEMP_ALLOC(ArgListSize(idlp) + strlen(pp) + 2);
  673.    strcpy(cmd, pp) ;
  674.    idep = idlp->id_list ;
  675.    while (idep != NULL) {
  676.       strcat(cmd, " ") ;
  677.       strcat(cmd, idep->id) ;
  678.       next_id = idep->next_id ;
  679.       free(idep) ;
  680.       idep = next_id ;
  681.    }
  682.    free(idlp) ;
  683.    system(cmd) ;
  684.    TEMP_FREE(cmd);
  685. }
  686.