home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / file / conflict.0 / conflict / conflict-6.0 / conflict.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-18  |  17.7 KB  |  813 lines

  1. /******************************************************************************
  2.  * Copyright 1995 by Thomas E. Dickey.  All Rights Reserved.                  *
  3.  *                                                                            *
  4.  * You may freely copy or redistribute this software, so long as there is no  *
  5.  * profit made from its use, sale trade or reproduction. You may not change   *
  6.  * this copyright notice, and it must be included in any copy made.           *
  7.  ******************************************************************************/
  8. #ifndef    NO_IDENT
  9. static    char    Id[] = "$Header: /home/tom/src/conflict/RCS/conflict.c,v 6.0 1995/03/18 14:05:04 dickey Rel $";
  10. #endif
  11.  
  12. /*
  13.  * Title:    conflict.c (path-conflict display)
  14.  * Author:    T.E.Dickey
  15.  * Created:    15 Apr 1988
  16.  *
  17.  * Function:    Reports pathname conflicts by making a list of the directories
  18.  *        which are listed in the environment variable PATH, and then
  19.  *        scanning these directories for executable files.  If arguments
  20.  *        given to this program, the test for executable files is limited
  21.  *        to the given names (after stripping directory-prefix).
  22.  *
  23.  *        The report is sent to standard output and always shows the
  24.  *        list of directories.  Conflicts are reported on succeeding
  25.  *        lines.
  26.  */
  27.  
  28. #include "conflict.h"
  29.  
  30. #define    CHUNK    0xff        /* (2**n) - 1 chunk for reducing realloc's */
  31.  
  32. static    INPATH    *inpath;
  33. static    DIRS    *dirs;
  34. static    char    *env_name;    /* e.g., "PATH" */
  35. static    char    *pathlist;    /* e.g., ".:/bin:/usr/bin" */
  36. static    char    **FileTypes;
  37. static    char    *dot;
  38. static    size_t    total,
  39.         path_len,    /* maximum number of items in path */
  40.         num_types;    /* number of file-types to scan */
  41. static    int    acc_mask,    /* permissions we're looking for */
  42.         p_opt,        /* print pathnames (not a table) */
  43.         v_opt,        /* verbose (repeat for different levels) */
  44.         do_blips;
  45. static    char    *w_opt    = "",    /* pads listing */
  46.         *w_opt_text = "--------";
  47.  
  48. #if SYS_MSDOS || SYS_OS2
  49. #define DOS_upper(s) strupr(s)
  50. #else
  51. #define DOS_upper(s) s
  52. #endif
  53.  
  54. #if SYS_MSDOS
  55. #define TYPES_PATH ".COM.EXE.BAT.PIF"
  56. #endif
  57.  
  58. #if SYS_OS2
  59. #define TYPES_PATH ".EXE.CMD.bat.com.sys"
  60. #endif
  61.  
  62. static    char *    TypesOf ARGS((size_t len, INPATH *ip));
  63. static    int    LookupDirs ARGS((char *name, int n));
  64. static    int    SameTypeless ARGS((char *a, char *b));
  65. static    int    cmp_INPATH ARGS((const void *p1, const void *p2));
  66. static    int    had_conflict ARGS((INPATH *ip));
  67. static    type_t    LookupType ARGS((char *name));
  68. static    void    AddToPath ARGS((char *path));
  69. static    void    ScanConflicts ARGS((char *path, int inx, int argc, char **argv));
  70. static    void    ShowConflicts ARGS((int len, INPATH *ip));
  71. static    void    ShowPathnames ARGS((INPATH *ip));
  72. static    void    compress_list ARGS((void));
  73. static    void    usage ARGS((void));
  74.  
  75. #if USE_INODE
  76. static    void    node_found ARGS((INPATH *ip, int n, type_t f, struct stat *sb));
  77. #else
  78. static    void    node_found ARGS((INPATH *ip, int n, type_t flags));
  79. #endif
  80.  
  81. /*
  82.  * comparison procedure used for sorting list of names for display
  83.  */
  84. static
  85. int    cmp_INPATH(p1, p2)
  86.     const    void    *p1;
  87.     const    void    *p2;
  88. {
  89.     return strcmp(((INPATH *)(p1))->name, ((INPATH *)(p2))->name);
  90. }
  91.  
  92. /*
  93.  * save information from the current stat-block so we can test for repeated
  94.  * instances of the file.
  95.  */
  96. #if USE_INODE
  97. static
  98. void    node_found(ip, n, flags, sb)
  99.     INPATH *    ip;
  100.     int        n;
  101.     type_t        flags;
  102.     struct stat *    sb;
  103. {
  104.     NODEFLAGS(n)      |= flags;
  105.     ip->node[n].device = sb->st_dev;
  106.     ip->node[n].inode  = sb->st_ino;
  107. }
  108. #define    FoundNode(ip,n) node_found(ip,n,ok,&sb)
  109.  
  110. #else
  111. static
  112. void    node_found(INPATH *ip, int n, type_t flags)
  113. {
  114.     NODEFLAGS(n) |= flags;
  115. }
  116. #define    FoundNode(ip,n) node_found(ip,n,ok)
  117. #endif    /* USE_INODE */
  118.  
  119. /*
  120.  * Look up the given name and fill-in the n'th entry of the 'dirs[]' array.
  121.  */
  122. static
  123. int    LookupDirs(name, n)
  124.     char        *name;
  125.     int        n;
  126. {
  127.     int    found    = FALSE;
  128. #if USE_INODE
  129.     struct    stat    sb;
  130.  
  131.     if (stat(name, &sb) >= 0) {
  132.         dirs[n].device = sb.st_dev;
  133.         dirs[n].inode  = sb.st_ino;
  134.         found = TRUE;
  135.     }
  136. #else
  137.     char    resolved[MAXPATHLEN];
  138.     if (realpath(name, resolved) != 0) {
  139.         dirs[n].actual = MakeString(resolved);
  140.         found = TRUE;
  141.     }
  142. #endif
  143.     return found;
  144. }
  145.  
  146. /*
  147.  * Display the conflicting items
  148.  */
  149. static
  150. void    ShowConflicts(len, ip)
  151.     int        len;
  152.     INPATH *    ip;
  153. {
  154.     register int j;
  155.     int    k = -1;
  156.     char flags[BUFSIZ];
  157.  
  158.     for (j = 0; j < len; j++) {
  159.         if (FileTypes == 0) {
  160.             register int d = '-';
  161.             if (ip != 0) {
  162.                 if (IS_A_NODE(j)) {
  163.                     d = '*';
  164.                     if (k < 0) {
  165.                         k = j;    /* latch first index */
  166.                     } else {
  167.                         if (!SAME_NODE(j,k))
  168.                             d = '+';
  169.                     }
  170.                 }
  171.             }
  172.             (void)printf("%s%c", w_opt, d);
  173.             continue;
  174.         }
  175.  
  176.         for (k = 0; k < num_types+2; k++)
  177.             flags[k] = '-';
  178.         flags[k-1] = EOS;
  179.         flags[0] = ':';
  180.         if (ip != 0 && IS_A_NODE(j)) {
  181.             for (k = 0; k < num_types; k++) {
  182.                 if ((1 << k) & NODEFLAGS(j)) {
  183.                     char *s = FileTypes[k];
  184.                     int c = *s++;
  185.                     if (c == EOS || *s == EOS)
  186.                         c = '.';
  187.                     else
  188.                         c = *s;
  189.                     flags[k+1] = c;
  190.                 }
  191.             }
  192.         }
  193.         (void)printf("%s%s", w_opt, flags);
  194.     }
  195. }
  196.  
  197. static
  198. void    ShowPathnames (ip)
  199.     INPATH    *ip;
  200. {
  201.     int    j, k;
  202.     char    bfr[MAXPATHLEN];
  203.  
  204.     if (num_types != 0) {
  205.         (void)strcpy(bfr, ip->name);
  206.         *ftype(bfr) = EOS;
  207.     }
  208.  
  209.     if (p_opt) {
  210.         for (j = 0; j < path_len; j++) {
  211.             if (NODEFLAGS(j) != 0) {
  212.                 if (num_types != 0) {
  213.                     for (k = 0; k < num_types; k++) {
  214.                         if ((1<<k) & NODEFLAGS(j)) {
  215.                             (void)printf("%s%c%s%s\n",
  216.                                 dirs[j].name,
  217.                                 PATHNAME_SEP,
  218.                                 bfr,
  219.                                 FileTypes[k]);
  220.                         }
  221.                     }
  222.                 } else {
  223.                     (void)printf("%s%c%s\n",
  224.                         dirs[j].name,
  225.                         PATHNAME_SEP,
  226.                         ip->name);
  227.                 }
  228.             }
  229.         }
  230.     } else if (num_types != 0) {
  231.         (void)printf(": %s (%s)\n", bfr, TypesOf(path_len, ip));
  232.     } else {
  233.         (void)printf(": %s\n", ip->name);
  234.     }
  235. }
  236.  
  237. static
  238. char *    TypesOf(len, ip)
  239.     size_t        len;
  240.     INPATH *    ip;
  241. {
  242.     register int j, mask, n, k;
  243. #if NO_LEAKS
  244.     static    char    result[BUFSIZ];
  245. #else
  246.     static    char    *result;
  247.  
  248.     if (result == 0)
  249.         result = malloc((num_types*4) + 1);
  250. #endif
  251.  
  252.     for (j = mask = 0; j < len; j++)
  253.         mask |= NODEFLAGS(j);
  254.  
  255.     result[0] = EOS;
  256.     for (n = 0, k = 1; n < num_types; n++, k <<= 1) {
  257.         if (k & mask) {
  258.             if (result[0] != EOS)
  259.                 (void)strcat(result, ",");
  260.             (void)strcat(result, FileTypes[n]);
  261.         }
  262.     }
  263.     return result;
  264. }
  265.  
  266. /*
  267.  * Compress the list of paths, removing those which had no conflicts.
  268.  */
  269. static
  270. void    compress_list()
  271. {
  272.     register int    j, k, jj;
  273.     int    compress;
  274.     type_t    flags;
  275.  
  276.     for (j = 0; j < path_len; j++) {
  277.         for (k = 0, compress = TRUE; compress && (k < total); k++) {
  278.             register INPATH *ip = inpath + k;
  279.             if ((flags = NODEFLAGS(j)) != 0) {
  280.                 /* If there's more than one suffix found for
  281.                  * the current entry, we cannot compress it.
  282.                  */
  283.                 while (flags != 0) {
  284.                     if ((flags & 1) != 0)
  285.                         break;
  286.                     flags >>= 1;
  287.                 }
  288.                 if (flags == 1) {
  289.                     for (jj = 0; jj < path_len; jj++) {
  290.                         if (jj == j)
  291.                             continue;
  292.                         if (IS_A_NODE(jj)) {
  293.                             compress = FALSE;
  294.                             break;
  295.                         }
  296.                     }
  297.                 } else {
  298.                     compress = FALSE;
  299.                 }
  300.             }
  301.         }
  302.         if (compress) {
  303.             if (v_opt) {
  304.                 (void)fprintf(stderr, "no conflict:%s\n", dirs[j].name);
  305.                 (void)fflush(stderr);
  306.             }
  307.             FreeString(dirs[j].name);
  308.             for (jj = j+1; jj < path_len; jj++)
  309.                 dirs[jj-1] = dirs[jj];
  310.             for (k = 0; k < total; k++) {
  311.                 register INPATH *ip = inpath + k;
  312.                 for (jj = j+1; jj < path_len; jj++)
  313.                     ip->node[jj-1] = ip->node[jj];
  314.             }
  315.             j--;
  316.             path_len--;
  317.         }
  318.     }
  319. }
  320.  
  321. /*
  322.  * returns true iff we have two instances of the same name
  323.  */
  324. static
  325. int    had_conflict(ip)
  326.     INPATH *    ip;
  327. {
  328.     register int    j, k;
  329.     int    first    = TRUE;
  330.     type_t    mask;
  331.  
  332.     for (j = 0; j < path_len; j++) {
  333.         if ((mask = NODEFLAGS(j)) != 0) {
  334.  
  335.             if (FileTypes == 0) {
  336.                 if (!first)
  337.                     return TRUE;
  338.                 first = FALSE;
  339.                 continue;
  340.             }
  341.  
  342.             for (k = 0; k < num_types; k++) {
  343.                 if (mask & (1<<k)) {
  344.                     if (!first)
  345.                         return TRUE;
  346.                     first = FALSE;
  347.                 }
  348.             }
  349.         }
  350.     }
  351.     return (FALSE);
  352. }
  353.  
  354. /* Returns nonzero if the given filename has an executable-suffix */
  355. static
  356. type_t    LookupType(name)
  357.     char *        name;
  358. {
  359.     register int k;
  360.     char    temp[MAXPATHLEN];
  361.     char    *type = DOS_upper(strcpy(temp, ftype(name)));
  362.  
  363.     for (k = 0; k < num_types; k++) {
  364.         if (!strcmp(type, FileTypes[k]))
  365.             return (1<<k);
  366.     }
  367.     return 0;
  368. }
  369.  
  370. /* Compare two leaf-names, ignoring their suffix. */
  371. static
  372. int    SameTypeless(a, b)
  373.     char *        a;
  374.     char *        b;
  375. {
  376.     char    *type_a = ftype(a);
  377.     char    *type_b = ftype(b);
  378.     if (type_a - a == type_b - b) {
  379.         if (!strncmp(a, b, (size_t)(type_a - a)))
  380.             return TRUE;
  381.     }
  382.     return FALSE;
  383. }
  384. #define SameName(a,b) ((FileTypes == 0) ? SameString(a,b) : SameTypeless(a,b))
  385.  
  386. static
  387. void    ScanConflicts(path, inx, argc, argv)
  388.     char *    path;
  389.     int    inx;
  390.     int    argc;
  391.     char **    argv;
  392. {
  393.     DIR        *dp;
  394.     struct dirent    *de;
  395.     struct stat    sb;
  396.     register int    j;
  397. #if SYS_MSDOS || SYS_OS2
  398.     char    save_wd[MAXPATHLEN];
  399. #endif
  400.  
  401.     /*
  402.      * When scanning a directory, we first chdir to it, mostly to make
  403.      * the scan+stat work faster, but also because some systems don't
  404.      * scan properly otherwise.
  405.      *
  406.      * MSDOS and OS/2 are a little more complicated, because each drive
  407.      * has its own current directory.
  408.      */
  409. #if SYS_MSDOS || SYS_OS2
  410.     (void) strcpy(save_wd, dot);
  411.     if (!strcmp(".", path)) {
  412.         path = dot;
  413.     } else if (!same_drive(dot, path)) {
  414.         if (!set_drive(path))
  415.             return;
  416.         getwd(save_wd);
  417.     }
  418. #endif
  419.     if (set_directory(path)
  420.      && (dp = opendir(path)) != NULL) {
  421.  
  422.         while ((de = readdir(dp)) != NULL) {
  423.         register
  424.         type_t    ok    = 0;
  425.         int    found    = FALSE;
  426.         char    buffer[MAXPATHLEN];
  427.         char    *the_name;
  428.  
  429.             if (do_blips)
  430.                 blip('.');
  431.  
  432.             (void)sprintf(buffer, "%.*s", (int)NAMLEN(de), de->d_name);
  433.             the_name = MakeString(DOS_upper(buffer));
  434.  
  435.             /* If arguments are given, restrict search to them */
  436.             if (argc > optind) {
  437.                 for (j = optind; j < argc; j++) {
  438.                     if (SameName(argv[j], the_name)) {
  439.                         found = TRUE;
  440.                         break;
  441.                     }
  442.                 }
  443.                 if (!found)
  444.                     continue;
  445.             }
  446.  
  447.             /* Verify that the name is a file, and executable */
  448.             if (stat(the_name, &sb) < 0)
  449.                 continue;
  450.             if ((sb.st_mode & S_IFMT) != S_IFREG)
  451.                 continue;
  452.  
  453. #if SYS_UNIX || SYS_OS2
  454.             if (access(the_name, acc_mask) < 0)
  455.                 continue;
  456.             ok = 1;
  457. #endif
  458.             if (FileTypes != 0) {
  459.                 if ((ok = LookupType(the_name)) == 0)
  460.                     continue;
  461.             }
  462.  
  463.             /* Find the name in our array of all names */
  464.             found    = FALSE;
  465.             for (j = 0; j < total; j++) {
  466.                 if (SameName(inpath[j].name, the_name)) {
  467.                     FoundNode(&inpath[j], inx);
  468.                     found = TRUE;
  469.                     break;
  470.                 }
  471.             }
  472.  
  473.             /* If not there, add it */
  474.             if (!found) {
  475.                 if (!(total & CHUNK)) {
  476.                     size_t need = ((total*3)/2 | CHUNK) + 1;
  477.                     if (inpath != 0)
  478.                         inpath = TypeRealloc(INPATH,
  479.                                 inpath, need);
  480.                     else
  481.                         inpath = TypeAlloc(INPATH, need);
  482.                 }
  483.                 j = total++;
  484.                 inpath[j].name = the_name;
  485.                 inpath[j].node = TypeAlloc(NODE, path_len);
  486.                 FoundNode(&inpath[j], inx);
  487.             }
  488.             if (v_opt > 2) {
  489.                 (void)printf("%c %s/%s\n",
  490.                     found ? '+' : '*',
  491.                     path, the_name);
  492.             }
  493.         }
  494.         (void)closedir(dp);
  495.     }
  496. #if SYS_MSDOS || SYS_OS2
  497.     if (strcmp(dot, save_wd)) {
  498.         chdir (save_wd);
  499.     }
  500. #endif
  501.     (void)set_directory(dot);
  502. }
  503.  
  504. /*
  505.  * Build up the 'pathlist' string in the same form as we expect from an
  506.  * environment variable, from command-line option values.
  507.  */
  508. static
  509. void    AddToPath(path)
  510.     char    *path;
  511. {
  512.     size_t    need = strlen(path) + 1;
  513.  
  514.     if (pathlist != 0) {
  515.         size_t have = strlen(pathlist);
  516.         pathlist = realloc(pathlist, have + need + 1);
  517.         (void)sprintf(pathlist + have, "%c%s", PATHLIST_SEP, path);
  518.     } else {
  519.         pathlist = malloc(need);
  520.         (void)strcpy(pathlist, path);
  521.     }
  522. }
  523.  
  524. static
  525. void    usage()
  526. {
  527.     static    char    *tbl[] = {
  528.          "Usage: conflict [options] [list_of_files]"
  529.         ,""
  530.         ,"Lists conflicts of executable files in current PATH."
  531.         ,"First instance in path (and those linked to it) are marked"
  532.         ,"with \"*\", succeeding instances with \"+\" marks."
  533.         ,""
  534.         ,"Options are:"
  535.         ,"  -e name    specify another environment variable than $PATH"
  536.         ,"  -p         print pathnames only"
  537.         ,"  -r         look for read-able files"
  538.         ,"  -t types   look only for given file-types"
  539.         ,"  -v         (verbose) show names found in each directory"
  540.         ,"             once: shows all directory names"
  541.         ,"             twice: shows all filenames"
  542.         ,"  -w         look for write-able files"
  543.         ,"  -W number  expand width of display by number of columns"
  544.         ,"  -x         look for execut-able files (default)"
  545.         ,""
  546.         ,"Also, C-preprocessor-style -I and -L options"
  547.     };
  548.     register int    j;
  549.     for (j = 0; j < SIZEOF(tbl); j++)
  550.         (void)fprintf(stderr, "%s\n", tbl[j]);
  551.     (void)fflush(stderr);
  552.     (void)exit(EXIT_FAILURE);
  553. }
  554.  
  555. void    failed(s)
  556.     char    *s;
  557. {
  558.     perror(s);
  559.     exit(EXIT_FAILURE);
  560. }
  561.  
  562. int    main(argc, argv)
  563.     int    argc;
  564.     char    *argv[];
  565. {
  566.     register int    found, j, k;
  567.     char    *s, *t;
  568.     char    *type_list = 0;
  569.     char    bfr[MAXPATHLEN];
  570.  
  571.     while ((j = getopt(argc, argv, "D:e:I:prt:U:vwW:x")) != EOF) {
  572.         switch (j) {
  573.         case 'e':    env_name = optarg;    break;
  574.         case 't':    type_list = optarg;    break;
  575.         case 'v':    v_opt++;        break;
  576.         case 'p':    p_opt = TRUE;        break;
  577.  
  578.         case 'W':    k = (int)strtol(optarg, &t, 0);
  579.                 if (*t != EOS || k < 0)
  580.                     usage();
  581.                 k = strlen(w_opt_text) - k;
  582.                 if (k < 0)
  583.                     k = 0;
  584.                 w_opt = w_opt_text + k;
  585.                 break;
  586.  
  587.         case 'r':    acc_mask |= R_OK;    break;
  588.         case 'w':    acc_mask |= W_OK;    break;
  589.         case 'x':    acc_mask |= X_OK;    break;
  590.  
  591.             /*
  592.              * The 'U' and 'D' options are parsed to simplify
  593.              * using C-preprocessor options to scan for include-
  594.              * files.
  595.              */
  596.         case 'U':    /* ignored */        break;
  597.         case 'D':    /* ignored */        break;
  598.         case 'I':    AddToPath(optarg);    break;
  599.         case 'L':    AddToPath(optarg);    break;
  600.  
  601.         default:    usage();
  602.                 /*NOTREACHED*/
  603.         }
  604.     }
  605.  
  606.     /* The "-v" and "-p" options aren't meaningful in combination */
  607.     if (v_opt && p_opt)
  608.         usage();
  609.  
  610.     /* The remaining arguments are the programs/symbols that we're looking
  611.      * for.  Reduce the argument-list to the leaf-names (stripping paths).
  612.      */
  613.     if (argc > optind) {
  614.         for (j = optind; j < argc; j++) {
  615.             argv[j] = MakeString(DOS_upper(fleaf(argv[j])));
  616.         }
  617.     }
  618.  
  619.     do_blips = ((v_opt > 1) && isatty(fileno(stderr))); 
  620.  
  621.     if (acc_mask == 0)
  622.         acc_mask = X_OK;
  623.  
  624.     /*
  625.      * Get the current working-directory, so we have a reference point to
  626.      * go back after scanning directories.
  627.      */
  628.     if (getwd(bfr) == 0)
  629.         failed("getcwd");
  630.     dot = MakeString(bfr);
  631.     if (!p_opt)
  632.         (void)printf("Current working directory is \"%s\"\n", dot);
  633.  
  634.     /*
  635.      * Obtain the list of directories that we'll scan.
  636.      */
  637.     if (pathlist == 0) {
  638.         if (env_name == 0)
  639.             env_name = "PATH";
  640.  
  641.         if ((s = getenv(env_name)) != 0)
  642.             pathlist = strdup(s);
  643.         else
  644.             failed(env_name);
  645.  
  646. #if SYS_MSDOS || SYS_OS2
  647.         if (!strcmp(env_name, "PATH")) {
  648.             /* look in current directory before looking in $PATH */
  649.             s = malloc(strlen(pathlist)+3);
  650.             (void)sprintf(s, ".%c%s", PATHLIST_SEP, pathlist);
  651.             free(pathlist);
  652.             pathlist = s;
  653.         }
  654. #endif
  655.     }
  656.     for (s = DOS_upper(pathlist), path_len = 1; *s != EOS; s++)
  657.         if (*s == PATHLIST_SEP)
  658.             path_len++;
  659.     dirs = TypeAlloc(DIRS, path_len);
  660.  
  661.     /*
  662.      * Reconstruct the type-list (if any) as an array to simplify scanning.
  663.      */
  664. #if SYS_MSDOS || SYS_OS2
  665.      if (type_list == 0) {
  666.         if (!strcmp(env_name, "PATH"))
  667.             type_list = TYPES_PATH;
  668.     }
  669. #endif
  670.     if (type_list != 0) {
  671.         type_list = DOS_upper(strdup(type_list));
  672.         for (s = type_list, num_types = 0; *s != EOS; s++) {
  673.             if (*s == '.') {
  674.                 num_types++;
  675. #if SYS_UNIX            /* "." and "" are different types */
  676.                 if ((s[1] == '.') || (s[1] == EOS))
  677.                     num_types++;
  678. #endif
  679.             }
  680.         }
  681.         if (num_types == 0 || *type_list != '.') {
  682.             (void)fprintf(stderr, "Type-list must be .-separated\n"); 
  683.             exit(EXIT_FAILURE);
  684.         }
  685.         FileTypes = TypeAlloc(char *, num_types);
  686.         j = num_types;
  687.         do {
  688.             if (*--s == '.') {
  689.                 FileTypes[--j] = strdup(s);
  690. #if SYS_UNIX            /* "." and "" are different types */
  691.                 if (s[1] == EOS)
  692.                     FileTypes[--j] = strdup("");
  693. #endif
  694.                 *s = EOS;
  695.             }
  696.         } while (j != 0);
  697.         free(type_list);
  698.     }
  699.  
  700.     /*
  701.      * Build a list of the directories in the $PATH variable, excluding any
  702.      * that happen to be the same inode (e.g., ".", or symbolically linked
  703.      * directories).
  704.      */
  705.     for (s = pathlist, j = 0; *s != EOS; s = t) {
  706.         for (k = 0; s[k] != PATHLIST_SEP; k++)
  707.             if ((bfr[k] = s[k]) == EOS)
  708.                 break;
  709.  
  710.         t = (s[k] != EOS) ? s+k+1 : s+k;
  711.         if (k == 0)
  712.             (void)strcpy(bfr, ".");
  713.         else
  714.             bfr[k] = EOS;
  715.  
  716.         if (LookupDirs(bfr, j)) {
  717.             for (k = 0, found = FALSE; k < j; k++) {
  718.                 if (SAME_DIRS(j,k)) {
  719.                     found = TRUE;
  720.                     break;
  721.                 }
  722.             }
  723.             if (!found) {
  724.                 dirs[j].name = MakeString(bfr);
  725.                 j++;
  726.             }
  727.         }
  728.     }
  729.     path_len = j;
  730.  
  731.     /*
  732.      * For each unique directory in $PATH, scan it, looking for executable
  733.      * files.
  734.      */
  735.     for (j = 0; j < path_len; j++) {
  736.         if (v_opt > 1) {
  737.             ShowConflicts(j+1, (INPATH *)0);
  738.             (void)printf("> %s\n", dirs[j].name);
  739.         } else if (do_blips) {
  740.             (void)fprintf(stderr, "%s", dirs[j].name);
  741.             (void)fflush(stdout);
  742.             (void)fflush(stderr);
  743.         }
  744.         ScanConflicts(dirs[j].name, j, argc, argv);
  745.         if (do_blips)
  746.             blip('\n');
  747.     }
  748.  
  749.     /*
  750.      * The normal (unverbose) listing shows only the conflicting files,
  751.      * and the directories in which they are found.
  752.      */
  753.     if (!v_opt)
  754.         compress_list();
  755.  
  756.     if (!p_opt) {
  757.         for (j = 0; j < path_len; j++) {
  758.             ShowConflicts(j+1, (INPATH *)0);
  759.             (void)printf("> %s\n", dirs[j].name);
  760.         }
  761.     }
  762.  
  763.     if (total != 0) {
  764.         qsort((char *)inpath, (size_t)total, sizeof(INPATH), cmp_INPATH);
  765.         for (j = 0; j < total; j++) {
  766.             if ((v_opt > 1) || had_conflict(&inpath[j])) {
  767.                 if (!p_opt)
  768.                     ShowConflicts((int)path_len, &inpath[j]);
  769.                 ShowPathnames(&inpath[j]);
  770.             }
  771.         }
  772.     }
  773.  
  774.     /*
  775.      * Test/debug: free all memory so we can validate the heap.
  776.      */
  777. #if NO_LEAKS
  778.     if (FileTypes != 0) {
  779.         for (j = 0; j < num_types; j++)
  780.             free(FileTypes[j]);
  781.         free((char *)FileTypes);
  782.     }
  783.     if (dirs != 0) {
  784. # if !USE_TXTALLOC
  785.         for (j = 0; j < path_len; j++) {
  786.             FreeString(dirs[j].name);
  787.         }
  788. # endif
  789.         free((char *)dirs);
  790.     }
  791.     if (inpath != 0) {
  792.         for (j = 0; j < total; j++) {
  793.             FreeString(inpath[j].name);
  794.             free((char *)(inpath[j].node));
  795.         }
  796.         free((char *)inpath);
  797.     }
  798.     free(pathlist);
  799.     FreeString(dot);
  800. # if USE_TXTALLOC
  801.     free_txtalloc();
  802. # endif
  803. # if HAVE_DBMALLOC_H
  804.     malloc_dump(fileno(stdout));
  805. # endif
  806. #endif
  807.     (void)fflush(stderr);
  808.     (void)fflush(stdout);
  809.     (void)exit(EXIT_SUCCESS);
  810.     /*NOTREACHED*/
  811.     return 0;
  812. }
  813.