home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume31 / lc / part02 / lc.c
Encoding:
C/C++ Source or Header  |  1992-08-14  |  41.2 KB  |  1,747 lines

  1. /*
  2. ** This software is 
  3. **
  4. ** Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990,
  5. **               1991, 1992 by Kent Landfield.
  6. **
  7. ** Permission is hereby granted to copy, distribute or otherwise 
  8. ** use any part of this package as long as you do not try to make 
  9. ** money from it or pretend that you wrote it.  This copyright 
  10. ** notice must be maintained in any copy made.
  11. **
  12. ** Use of this software constitutes acceptance for use in an AS IS 
  13. ** condition. There are NO warranties with regard to this software.  
  14. ** In no event shall the author be liable for any damages whatsoever 
  15. ** arising out of or in connection with the use or performance of this 
  16. ** software.  Any use of this software is at the user's own risk.
  17. **
  18. **  If you make modifications to this software that you feel 
  19. **  increases it usefulness for the rest of the community, please 
  20. **  email the changes, enhancements, bug fixes as well as any and 
  21. **  all ideas to me. Thanks!
  22. **
  23. **              Kent Landfield
  24. **              kent@sterling.com
  25. **              sparky!kent
  26. **
  27. **  Subsystem:   lc - List Columnwise/Categories
  28. **
  29. **  Abstract :   lc -- categorize files in a directory and list columnwize
  30. **
  31. **  Usage:       lc [ options ] [ directory ... ]
  32. **
  33. **  Options:
  34. **               -a      List dot files as well.
  35. **               -b      List block special files only
  36. ** JB            -B      Display the size in blocks
  37. **               -c      List character special files only
  38. ** JB            -C      Sort down the columns instead of across
  39. **               -d      List directories only
  40. **               -D      Do not display singular files
  41. **               -e      Mark executable files with '*'
  42. **               -f      List regular files only
  43. **               -F      List fifo files only
  44. ** JB            -i      Display the inode number
  45. **               -I      Suppress unresolved symbolic link messages.
  46. **               -L      Display symbolic links
  47. **               -l      Mark symbolic links with '@'
  48. **               -m      List shared memory name space entry files only
  49. **               -M      List semaphore name space entry files only
  50. **               -r      Do not sort the filenames before displaying.
  51. **               -S      List socket file only
  52. **               -s      List symbolic links only
  53. **               -v      Print the version of lc 
  54. ** JB            -x      Only display those files the user owns or has access to
  55. ** JB            -X      Only display those files the user does not own and
  56. **                       does not have access to.
  57. **               -1      List files one per line instead of in columns
  58. **
  59. ** The "only" options can be combined.
  60. ** If there is no 'directory' specified, the current directory is used.
  61. ** Not all options are supported on every system. (e.g. no symbolic links
  62. ** on your system ? Options -s, -L or -l won't be available..)
  63. **
  64. */
  65. static char *sccsid = "@(#)lc.c    1.34 8/7/92  Kent Landfield";
  66. #include "patchlevel.h"
  67.  
  68. #include <stdio.h>
  69. #ifdef BSD
  70. #  include <strings.h>
  71. #  include <sys/param.h>
  72. #else
  73. #  include <string.h>
  74. #  include <sys/types.h>
  75. #endif
  76. #include <sys/stat.h>
  77. #ifdef POSIX
  78. #  include <limits.h>
  79. #  include <dirent.h>
  80. #else
  81. #  ifdef BSD
  82. #    ifdef DIRECT
  83. #      include <sys/dir.h>
  84. #    else
  85. #      include <dirent.h>
  86. #    endif
  87. #  else
  88. #    include <sys/dir.h>
  89. #  endif
  90. #endif
  91.  
  92. #ifndef NAME_MAX
  93. #  ifdef BSD
  94. #    define NAME_MAX    MAXNAMLEN
  95. #  else
  96. #    ifdef DIRSIZ
  97. #      define NAME_MAX  DIRSIZ
  98. #    else
  99. #      define NAME_MAX  14
  100. #    endif
  101. #  endif
  102. #endif
  103.  
  104. #ifndef PATH_MAX
  105. #  ifdef MAXPATHLEN
  106. #    define PATH_MAX    MAXPATHLEN
  107. #  else
  108. #    ifdef MAXNAMLEN
  109. #      define PATH_MAX  MAXNAMLEN
  110. #    else
  111. #      define PATH_MAX  255
  112. #    endif
  113. #  endif
  114. #endif
  115.  
  116. #define BUFSIZE         PATH_MAX
  117.  
  118. #define NODES_PER_HUNK  256
  119.  
  120. #define TRUE            1
  121. #define FALSE           0
  122.  
  123. #ifndef S_IXUSR
  124. #  define S_IXUSR       S_IEXEC
  125. #endif
  126.  
  127. #ifndef S_IXGRP
  128. #  define S_IXGRP       (S_IEXEC >> 3)
  129. #endif
  130.  
  131. #ifndef S_IXOTH
  132. #  define S_IXOTH       (S_IEXEC >> 6)
  133. #endif
  134.  
  135. #define DIR_ONLY        1<<0
  136. #define FILE_ONLY       1<<2
  137. #ifdef S_IFCHR
  138. #  define CHAR_ONLY     1<<3
  139. #endif
  140. #ifdef S_IFBLK
  141. #  define BLOCK_ONLY    1<<4
  142. #endif
  143. #ifndef apollo
  144. # ifdef S_IFIFO
  145. #  define FIFO_ONLY     1<<5
  146. # endif
  147. #endif
  148. #ifdef S_IFLNK
  149. #  define LNK_ONLY      1<<6
  150. #endif
  151. #ifdef S_IFSOCK
  152. #  define SOCK_ONLY     1<<7
  153. #endif
  154. #ifdef S_IFNAM
  155. #  define SEM_ONLY      1<<8
  156. #  define SD_ONLY       1<<9
  157. #endif
  158.  
  159. #ifdef BSD
  160. #  define strrchr       rindex
  161. #  define strchr        index
  162. #endif
  163.  
  164. /*
  165. ** File name storage structure
  166. */
  167.  
  168. struct list {
  169.     int num;
  170.     int max;
  171.     char **names;
  172. #ifdef LENS
  173.     int maxlen;
  174. #endif
  175. };
  176.  
  177. /*
  178. ** File name storage arrays
  179. */
  180.  
  181. #ifdef LENS
  182.  
  183. #ifdef S_IFBLK
  184. struct list Blks = { 0, 0, (char **) NULL, 0 };
  185. #endif
  186.  
  187. #ifdef S_IFCHR
  188. struct list Chrs = { 0, 0, (char **) NULL, 0 };
  189. #endif
  190.  
  191. struct list Dirs = { 0, 0, (char **) NULL, 0 };
  192. struct list Fls = { 0, 0, (char **) NULL, 0 };
  193.  
  194. #ifndef apollo
  195. #ifdef S_IFIFO
  196. struct list Fifos = { 0, 0, (char **) NULL, 0 };
  197. #endif
  198. #endif
  199.  
  200. #ifdef S_IFLNK
  201. struct list Lnks = { 0, 0, (char **) NULL, 0 };
  202. struct list Lnksn = { 0, 0, (char **) NULL, 0 };
  203. #endif
  204.  
  205. #ifdef S_IFSOCK
  206. struct list Socks = { 0, 0, (char **) NULL, 0 };
  207. #endif
  208.  
  209. #ifdef S_IFNAM
  210. struct list Sds = { 0, 0, (char **) NULL, 0 };
  211. struct list Sems = { 0, 0, (char **) NULL, 0 };
  212. #endif
  213.  
  214. #else   /* ifndef LENS */
  215.  
  216. #ifdef S_IFBLK
  217. struct list Blks = { 0, 0, (char **) NULL };
  218. #endif
  219.  
  220. #ifdef S_IFCHR
  221. struct list Chrs = { 0, 0, (char **) NULL };
  222. #endif
  223.  
  224. struct list Dirs = { 0, 0, (char **) NULL };
  225. struct list Fls = { 0, 0, (char **) NULL };
  226.  
  227. #ifndef apollo
  228. #ifdef S_IFIFO
  229. struct list Fifos = { 0, 0, (char **) NULL };
  230. #endif
  231. #endif
  232.  
  233. #ifdef S_IFLNK
  234. struct list Lnks = { 0, 0, (char **) NULL };
  235. struct list Lnksn = { 0, 0, (char **) NULL };
  236. #endif
  237.  
  238. #ifdef S_IFSOCK
  239. struct list Socks = { 0, 0, (char **) NULL };
  240. #endif
  241.  
  242. #ifdef S_IFNAM
  243. struct list Sds = { 0, 0, (char **) NULL };
  244. struct list Sems = { 0, 0, (char **) NULL };
  245. #endif
  246.  
  247. #endif /* LENS */
  248.  
  249. char *Progname;
  250.  
  251. int Allfiles = FALSE;       /* display '.' files as well    */
  252. int Display_single = TRUE;  
  253. int Executables = FALSE;    /* mark executable files        */
  254. int Level = 0;              
  255. int Maxlen = 0;             /* longest filename in category */
  256. int Only = FALSE;           /* limit display to types       */
  257. int Screen_width = 80;      /* display width 80/132         */
  258. int Single = FALSE;         /* display files one per line   */
  259. int Sort_wanted = TRUE;
  260. int Sort_down = FALSE;      /* sort by columns */
  261. int Display_inode = FALSE;  /* inode/file size */
  262. int Display_size = FALSE;   /* inode/file size */
  263. int Sort_offset = 0;        /* inode/file size */
  264. int Display_accessable = 0; /* accessable files */
  265.  
  266. #define ACCESSABLE_ONLY    1
  267. #define INACCESSABLE_ONLY  2
  268.  
  269. #ifdef S_IFLNK
  270. int Ignore = FALSE;         /* ignore unresolved errors   */
  271. int Current = 0;
  272. int Disp_links = FALSE;
  273. int Mark_links = FALSE;
  274. int lstat();
  275. int readlink();
  276. #endif
  277.  
  278. #ifndef _BSD
  279. # ifdef BSD
  280.     extern char *sprintf();
  281.     extern int  free();
  282.     extern int  qsort();
  283.     extern int  getuid();
  284.     extern int  getgid();
  285.     extern int  geteuid();
  286.     extern int  getegid();
  287. # else
  288.     extern int  sprintf();
  289.     extern void free();
  290.     extern void qsort();
  291.     extern uid_t getuid();
  292.     extern uid_t geteuid();
  293.     extern gid_t getgid();
  294.     extern gid_t getegid();
  295. # endif
  296.  extern int fprintf();
  297.  extern int printf();
  298.  extern int sscanf();
  299. #endif
  300.  
  301. void lc();
  302.  
  303. extern char *getenv();
  304. extern char *malloc();
  305. extern char *realloc();
  306. extern int access();
  307. extern int fputs();
  308. extern int puts();
  309. extern int stat();
  310. extern void exit();
  311.  
  312. /* S T R _ S A V  
  313.  *
  314.  *   str_sav() returns a pointer to a new string which is a dupli-
  315.  *   cate  of the string pointed to by s.  The space for the new
  316.  *   string is obtained using malloc(3).  If the new string  can-
  317.  *   not be created, the process prints an error message on stderr
  318.  *   and terminates.
  319.  */
  320.  
  321. char *str_sav(s)
  322.     char *s;
  323. {
  324.     char *p;
  325.  
  326.     if ((p = malloc((unsigned)(strlen(s) + 1))) == (char *) NULL) {
  327.         (void) fprintf(stderr, "%s: malloc: out of memory\n", Progname);
  328.         exit(1);
  329.     }
  330.     (void) strcpy(p, s);
  331.     return (p);
  332. }
  333.  
  334. /* D I R E C T O R Y
  335.  *
  336.  *  directory() is used to open and read the directory and pass
  337.  *  the filenames found to the routine lc();
  338.  */
  339. #if (POSIX || BSD)
  340. void directory(dname)
  341.     char *dname;
  342. {
  343.     register char *nbp;
  344.     register char *nep;
  345.     DIR *dstream;
  346.     int i;
  347. #ifdef DIRECT
  348.     struct direct *dp;
  349. #else
  350.     struct dirent *dp;
  351. #endif
  352.  
  353.     /* add a slash to the end of the directory name */
  354.     nbp = dname + strlen(dname);
  355. #ifdef AMIGA
  356.     if (*(nbp - 1) != ':') {
  357.         *nbp++ = '/';
  358.         *nbp = '\0';
  359.     }
  360. #else
  361.     *nbp++ = '/';
  362.     *nbp = '\0';
  363. #endif
  364.  
  365.     if ((dstream = opendir(dname)) == NULL) {
  366.         (void) fprintf(stderr, "%s: can't open %s\n", Progname, dname);
  367.         return;
  368.     }
  369.  
  370.     while ((dp = readdir(dstream)) != NULL) {
  371.         if (strcmp(dp->d_name, ".") == 0
  372.             || strcmp(dp->d_name, "..") == 0
  373.             || (!Allfiles && *(dp->d_name) == '.'))
  374.             continue;
  375.  
  376.         for (i = 0, nep = nbp; dp->d_name[i] && i < NAME_MAX; i++)
  377.             *nep++ = dp->d_name[i];
  378.         *nep++ = '\0';
  379.         lc(dname, 2);
  380.     }
  381.     (void) closedir(dstream);
  382.     *--nbp = '\0';
  383.     return;
  384. }
  385.  
  386. #else /* not POSIX or BSD */
  387.  
  388. /* D I R E C T O R Y
  389.  *
  390.  *  directory() is used to open and read the directory and pass
  391.  *  the filenames found to the routine lc();
  392.  */
  393. void directory(dname)
  394.     char *dname;
  395. {
  396.     register char *nbp;
  397.     register char *nep;
  398.     FILE *fd;
  399.     int i;
  400.     struct direct dir;
  401.  
  402.     /* add a slash to the end of the directory name */
  403.     nbp = dname + strlen(dname);
  404.     *nbp++ = '/';
  405.     *nbp = '\0';
  406.  
  407.     if ((nbp + NAME_MAX + 2) >= (dname + BUFSIZE))  { /* dname too long */
  408.         (void) fprintf(stderr, "%s: dirname too long: %s\n",
  409.                        Progname, dname);
  410.         return;
  411.     }
  412.  
  413.     if ((fd = fopen(dname, "r")) == (FILE *) NULL) { /* open the directory */
  414.         (void) fprintf(stderr, "%s: can't open %s\n", Progname, dname);
  415.         return;
  416.     }
  417.  
  418.     while (fread((char *) &dir, sizeof(dir), 1, fd) > 0) {
  419.         if (dir.d_ino == 0
  420.             || strcmp(dir.d_name, ".") == 0
  421.             || strcmp(dir.d_name, "..") == 0)
  422.             continue;
  423.         for (i = 0, nep = nbp; i < NAME_MAX; i++)
  424.             *nep++ = dir.d_name[i];
  425.         *nep++ = '\0';
  426.         lc(dname, 2);
  427.     }
  428.     (void) fclose(fd);
  429.     *--nbp = '\0';     /* restore dname */
  430.     return;
  431. }
  432. #endif
  433.  
  434. /* G E T L I N K
  435.  *
  436.  *  getlink() calls readlink() which places the contents of the 
  437.  *  symbolic link referred to by fn in the buffer wk. The contents 
  438.  *  of the link are null terminated and the path is returned to
  439.  *  the calling funtion as a pointer to a storage area created
  440.  *  by using malloc(3);
  441.  */
  442.  
  443. #ifdef S_IFLNK
  444. char *getlink(fn)
  445.     char *fn;
  446. {
  447.     char wk[PATH_MAX + 1];
  448.     int rc;
  449.  
  450.     rc = readlink(fn, wk, sizeof(wk));
  451.     if (rc < 0)
  452.         return ((char *) NULL);
  453.     wk[rc] = '\0';
  454.     return (str_sav(wk));
  455. }
  456. #endif
  457.  
  458. /* P R I N T _ L I N E 
  459.  *
  460.  *  print_line() is used to format and output the files previously 
  461.  *  located. This routine could use a lot more smarts but currently
  462.  *  it is rather crude...
  463.  */
  464.  
  465. int print_line(files, ind)
  466.     struct list *files;
  467.     int ind;
  468. {
  469.     register char *frmt;
  470.     char out_str[PATH_MAX + 5];
  471.     int i;
  472.     int prt_limit;
  473.     int numrows = 0;
  474.  
  475.     if (Single) {
  476. #ifdef S_IFLNK
  477.         if (Current == LNK_ONLY) {
  478.             if (*(Lnksn.names + ind) != (char *) NULL)
  479.                 (void) printf("    %s -> %s\n",
  480.                               *(Lnks.names + ind), *(Lnksn.names + ind));
  481.             else
  482.                 (void) printf("    %s -> %s\n",
  483.                               *(Lnks.names + ind), "UNRESOLVED");
  484.             ind++;
  485.             return (ind);
  486.         }
  487. #endif
  488.         (void) puts(*(files->names + ind));
  489.         ind++;
  490.     }
  491.     else if (Maxlen > ((Screen_width - 4) / 2)) {
  492.         (void) printf("    %s\n", *(files->names + ind));
  493.         ind++;
  494.     }
  495.     else {
  496.          frmt = out_str;
  497.          for (i = 0; i < 4; i++)
  498.               *frmt++ = ' ';
  499.  
  500.         /* The prt_limit may need to be smarter */
  501.  
  502.          prt_limit = (Screen_width - 4) / (Maxlen + 1);
  503.  
  504.          /*  sort by columns */
  505. #ifdef LNK_ONLY
  506.          if (Sort_down && Current != LNK_ONLY) {
  507. #else       
  508.          if (Sort_down) {
  509. #endif
  510.              numrows = (int)( (float)files->num / (float)prt_limit + 1.0);
  511.              prt_limit = (int) ( (float)files->num / (float)numrows + (float)(numrows - 1) / (float)numrows);
  512.          }
  513.  
  514.          if (Maxlen == 3 || Maxlen == 1)
  515.              prt_limit--;
  516.  
  517.          while ((ind < files->num) && (prt_limit-- > 0)) {
  518.               i = 0;
  519.               do {
  520.                    if (*(*(files->names + ind) + i) == '\0') {
  521.                        while (i++ <= Maxlen)
  522.                              *frmt++ = ' ';
  523.                    }
  524.                    else
  525.                        *frmt++ = *(*(files->names + ind) + i);
  526.                    i++;
  527.               } while (i <= Maxlen);
  528. #ifdef LNK_ONLY
  529.               if (Sort_down && Current != LNK_ONLY)
  530. #else       
  531.               if (Sort_down)
  532. #endif
  533.                  ind += numrows;
  534.               else
  535.                  ind++;
  536.          }
  537.          *frmt = '\0';
  538.          while (*--frmt == ' ')  /* strip trailing blanks */
  539.              *frmt = '\0';
  540.         
  541.          (void) puts(out_str);
  542.     }
  543.     return (ind);
  544. }
  545.  
  546. /* S T R _ C M P
  547.  *
  548.  *  str_cmp is the comparison routine used by
  549.  *  qsort(3) to order the filenames inplace.
  550.  */
  551.  
  552. int str_cmp(s1, s2)
  553.     char **s1;
  554.     char **s2;
  555. {
  556.     /* inode/file sizes */
  557.  
  558.     return strcmp(&**s1 + Sort_offset, &**s2 + Sort_offset);
  559. }
  560.  
  561. /* P R _ I N F O
  562.  *
  563.  *  pr_info() is used to sort the data if required
  564.  *  and then pass it to print_line to actually output
  565.  *  the data.`
  566.  */
  567.  
  568. int pr_info(strng, files, flg, sort_needed)
  569.     char *strng;
  570.     struct list *files;
  571.     int flg;
  572.     int sort_needed;
  573. {
  574.     int pnum = 0;
  575.  
  576. #ifdef LENS
  577.     Maxlen = files->maxlen;
  578. #endif
  579.  
  580. #ifdef S_IFLNK
  581.     if (!Single || Current == LNK_ONLY) {
  582.         if (flg)
  583.             (void) puts("");
  584.         (void) puts(strng);
  585.     }
  586. #else
  587.     if (!Single) {
  588.         if (flg)
  589.             (void) puts("");
  590.         (void) puts(strng);
  591.     }
  592. #endif
  593.  
  594.     if (sort_needed)
  595.         qsort((char *) (files->names), files->num, sizeof(char *), str_cmp);
  596.  
  597.     /* sort by columns */
  598.     Maxlen++; /* this is to force an extra space between columns */
  599. #ifdef LNK_ONLY
  600.     if (Sort_down && Current != LNK_ONLY) {
  601. #else
  602.     if (Sort_down) {
  603. #endif
  604.         int numcols = (Screen_width - 4) / (Maxlen + 1);
  605.         int numrows = (int)( (float)files->num / (float)numcols + 1.0);
  606.          
  607.         numcols = (int) ( (float)files->num / (float)numrows + (float)(numrows - 1) / (float)numrows);
  608.  
  609.         do {
  610.                 (void) print_line(files, pnum);
  611.                 pnum++;
  612.         } while (pnum < numrows);
  613.     }    
  614.     else {
  615.         do {
  616.             pnum = print_line(files, pnum);
  617.         } while (pnum < files->num);
  618.     }    
  619.     return (1);
  620. }
  621.  
  622.  
  623. /* P R I N T _ I N F O
  624.  *
  625.  *  print_info() is called to display all the filenames
  626.  *  located in the directory reading and storage functions.
  627.  */
  628.  
  629. void print_info()
  630. {
  631.     int flag = 0;
  632.  
  633. #ifdef S_IFLNK
  634.     int ssing;
  635.  
  636.     Current = 0;
  637.  
  638.     if (Lnks.num > 0 && (Disp_links == TRUE || Only & LNK_ONLY)) {
  639.         ssing = Single;
  640.         Single = TRUE;
  641.         Current = LNK_ONLY;
  642. #ifdef NOTDEF
  643.         flag = pr_info("Symbolic Links: ", &Lnks, flag, 0);
  644. #else
  645.         flag = pr_info("Symbolic Links: ", &Lnks, flag, Sort_wanted);
  646. #endif
  647.         Single = ssing;
  648.         Current = 0;
  649.     }
  650. #endif
  651.  
  652. #ifdef S_IFSOCK
  653.     if (Socks.num > 0 && (Only == 0 || Only & SOCK_ONLY))
  654.         flag = pr_info("Sockets: ", &Socks, flag, Sort_wanted);
  655. #endif
  656.  
  657. #ifdef S_IFNAM
  658.     if (Sems.num > 0 && (Only == 0 || Only & SEM_ONLY))
  659.         flag = pr_info("Semaphore Files: ", &Sems, flag, Sort_wanted);
  660.  
  661.     if (Sds.num > 0 && (Only == 0 || Only & SD_ONLY))
  662.         flag = pr_info("Shared Data Files: ", &Sds, flag, Sort_wanted);
  663. #endif
  664.  
  665. #ifndef apollo
  666. # ifdef S_IFIFO
  667.     if (Fifos.num > 0 && (Only == 0 || Only & FIFO_ONLY))
  668.         flag = pr_info("Fifo Files: ", &Fifos, flag, Sort_wanted);
  669. # endif
  670. #endif
  671.  
  672. #ifdef S_IFCHR
  673.     if (Chrs.num > 0 && (Only == 0 || Only & CHAR_ONLY))
  674.         flag = pr_info("Character Special Files: ", &Chrs, flag, Sort_wanted);
  675. #endif
  676.  
  677. #ifdef S_IFBLK
  678.     if (Blks.num > 0 && (Only == 0 || Only & BLOCK_ONLY))
  679.         flag = pr_info("Block Special Files: ", &Blks, flag, Sort_wanted);
  680. #endif
  681.  
  682.     if (Dirs.num > 0 && (Only == 0 || Only & DIR_ONLY))
  683.         flag = pr_info("Directories: ", &Dirs, flag, Sort_wanted);
  684.  
  685.     if (Fls.num > 0 && (Only == 0 || Only & FILE_ONLY))
  686.         flag = pr_info("Files: ", &Fls, flag, Sort_wanted);
  687.  
  688.     return;
  689. }
  690.  
  691. /* B A S E N A M E
  692.  *
  693.  *  basename() is used to return the base file name of a
  694.  *  path refered to by name. The base file name is stored
  695.  *  into a storage location refered to by str. It is the
  696.  *  calling function's responsibility to assure adequate
  697.  *  storage is supplied.
  698.  */
  699.  
  700. void basename(name, str)
  701.     char *name;
  702.     char *str;
  703. {
  704.     char *p;
  705.  
  706.     if ((p = strrchr(name, '/')) == (char *) NULL) {
  707. #ifdef AMIGA
  708.         if ((p = strchr(name, ':')) == (char *) NULL)
  709.             (void) strcpy(str, name);
  710.         else
  711.             (void) strcpy(str, ++p);
  712. #else
  713.         (void) strcpy(str, name);
  714. #endif
  715.     }
  716.     else
  717.         (void) strcpy(str, ++p);
  718.     return;
  719. }
  720.  
  721. /* A D D _ T O _ L I S T
  722.  *
  723.  * add_to_list() is used to add the supplied filename refered to
  724.  * by str to the appropriate category storage array.
  725.  */
  726.  
  727. void add_to_list(files, str)
  728.     struct list *files;
  729.     char *str;
  730. {
  731.     if (files->max == 0) {
  732.         files->names = (char **) malloc(sizeof(char *) * NODES_PER_HUNK);
  733.         if (files->names == (char **) NULL) {
  734.             (void) fprintf(stderr,
  735.                            "%s: malloc: out of memory\n", Progname);
  736.             exit(1);
  737.         }
  738.         files->max = NODES_PER_HUNK;
  739.     }
  740.     else if (files->num == files->max) {
  741.         files->names =
  742.             (char **) realloc((char *) files->names,
  743.                               (unsigned) sizeof(char *)
  744.                               * (files->max + NODES_PER_HUNK));
  745.         if (files->names == (char **) NULL) {
  746.             (void) fprintf(stderr,
  747.                            "%s: realloc: out of memory\n", Progname);
  748.             exit(1);
  749.         }
  750.         files->max += NODES_PER_HUNK;
  751.     }
  752.     if (str == (char *) NULL)
  753.         *(files->names + files->num++) = (char *) NULL;
  754.     else
  755.         *(files->names + files->num++) = str_sav(str);
  756.     return;
  757. }
  758.  
  759. /* L C
  760.  *
  761.  * lc() is main function for determining the type of
  762.  * the file refered to by name.
  763.  */
  764.  
  765. void lc(name, cnt)
  766.     char *name;
  767.     int cnt;
  768. {
  769. #ifdef S_IFLNK
  770.     char *link;
  771. #endif
  772.     char sav_str[BUFSIZE + 2 + 10]; /* inode/file size */
  773.     char tmp[BUFSIZE + 2 + 10];     /* inode/file size */
  774.     int mlen;
  775.     struct stat sbuf;
  776.     int display = TRUE;             /* access */
  777.  
  778. #ifdef S_IFLNK
  779.     if (lstat(name, &sbuf) < 0) {
  780.         (void) fprintf(stderr, "%s: can't stat %s\n", Progname, name);
  781.         return;
  782.     }
  783. #else
  784.     if (stat(name, &sbuf) == -1) {
  785.         (void) fprintf(stderr, "%s: can't stat %s\n", Progname, name);
  786.         return;
  787.     }
  788. #endif
  789.  
  790.     /*
  791.     ** Only list files the user owns or has access to.
  792.     */
  793.  
  794.     if (Display_accessable &&
  795.         Display_accessable != (ACCESSABLE_ONLY | INACCESSABLE_ONLY)) {   
  796.         static int first = 1;
  797.         static int uid;
  798.         static int euid;
  799.         static int gid;
  800.         static int egid;
  801.  
  802.         if (first) {
  803.             first = 0;
  804.             uid = getuid();
  805.             euid = geteuid();
  806.             gid = getgid();
  807.             egid = getegid();
  808.         }
  809.         if (uid != sbuf.st_uid && euid != sbuf.st_uid &&
  810.             gid != sbuf.st_gid && egid != sbuf.st_gid &&
  811.             ((sbuf.st_mode & 0007) == 0) ) {
  812.             if (Display_accessable & ACCESSABLE_ONLY)
  813.                 return;
  814.         }
  815.         else {
  816.             if ((Display_accessable & INACCESSABLE_ONLY) &&
  817.                     (sbuf.st_mode & S_IFMT) != S_IFDIR
  818. #ifdef S_IFLNK
  819.                  && (sbuf.st_mode & S_IFMT) != S_IFLNK
  820. #endif
  821.                                                         )
  822.                 return;
  823.             if ((Display_accessable & INACCESSABLE_ONLY)
  824. #ifdef S_IFLNK
  825.                  && (sbuf.st_mode & S_IFMT) == S_IFLNK
  826. #endif
  827.                                                            )
  828.                 display = FALSE;
  829.         }
  830.     }
  831.  
  832.     basename(name, sav_str);
  833.  
  834.     /* inode/file size */
  835.  
  836.     *tmp = 0;
  837.     if (Display_inode) 
  838.         (void) sprintf(tmp, "%5d", sbuf.st_ino);
  839.     
  840.     if (Display_size) {
  841.         /* Make our best guess here for the block size.  Allow the user */
  842.         /* compiling the program to override any system constant by     */
  843.         /* specifying the blocksize on the command line.   JB           */
  844.  
  845. #ifdef BLOCKSIZE
  846.         long st_blocks = (BLOCKSIZE - 1 + sbuf.st_size) / BLOCKSIZE;
  847. #else
  848. # ifdef BSD
  849.         long st_blocks = sbuf.st_blocks / BLK_MULTIPLE;
  850. # else    
  851. #  ifdef STD_BLK
  852.         long st_blocks = (STD_BLK - 1 + sbuf.st_size) / STD_BLK;
  853. #  else
  854. #   ifdef BUFSIZ
  855.         long st_blocks = (BUFSIZ - 1 + sbuf.st_size) / BUFSIZ;
  856. #   else
  857.         long st_blocks = (511 + sbuf.st_size) / 512;
  858. #   endif
  859. #  endif
  860. # endif
  861. #endif
  862.  
  863.         if (*tmp)
  864.             (void) sprintf(&tmp[strlen(tmp)], " %4d", st_blocks);
  865.         else    
  866.             (void) sprintf(tmp, "%4d", st_blocks);
  867.  
  868.     }
  869.     if (*tmp) {
  870.         Sort_offset = strlen(tmp) + 1;
  871.         (void) sprintf(&tmp[strlen(tmp)], " %s", sav_str);
  872.     }
  873.     else
  874.         (void) sprintf(tmp, "%s", sav_str);
  875.     (void) strcpy(sav_str, tmp);
  876.  
  877.     mlen = strlen(sav_str);
  878.  
  879. #ifndef LENS
  880.     if (mlen > Maxlen)
  881.         Maxlen = mlen;
  882. #endif
  883.  
  884.     switch (sbuf.st_mode & S_IFMT) {
  885.  
  886.     case S_IFDIR:
  887.         if (!Allfiles && sav_str[0] == '.' && Level != 0)
  888.             break;
  889.         if (cnt != 1)        /* dont store the dir name on entry */
  890.             add_to_list(&Dirs, sav_str);
  891.         /* never called - left for expansion to recursive     */
  892.         /* searches of subdirectories. Right, re-write needed */
  893.         /* in output facilities first...                      */
  894.         if (Level++ == 0)
  895.             directory(name);
  896. #ifdef LENS
  897.         if (mlen > Dirs.maxlen)
  898.             Dirs.maxlen = mlen;
  899. #endif
  900.         break;
  901.  
  902.     case S_IFREG:
  903.         /* do not print .files unless enviromental variable */
  904.         /* or option set.                                   */
  905.         if (!Allfiles && sav_str[0] == '.')
  906.             break;
  907.         if (Executables
  908.             && (sbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
  909.             *(sav_str + mlen) = '*';
  910.             ++mlen;
  911.             *(sav_str + mlen) = '\0';
  912. #ifndef LENS
  913.             if (mlen > Maxlen)
  914.                 Maxlen = mlen;
  915. #endif
  916.         }
  917.         add_to_list(&Fls, sav_str);
  918. #ifdef LENS
  919.         if (mlen > Fls.maxlen)
  920.             Fls.maxlen = mlen;
  921. #endif
  922.         break;
  923.  
  924. #ifdef S_IFCHR
  925.     case S_IFCHR:
  926.         if (!Allfiles && sav_str[0] == '.')
  927.             break;
  928.         add_to_list(&Chrs, sav_str);
  929. #ifdef LENS
  930.         if (mlen > Chrs.maxlen)
  931.             Chrs.maxlen = mlen;
  932. #endif
  933.         break;
  934. #endif
  935.  
  936. #ifdef S_IFBLK
  937.     case S_IFBLK:
  938.         if (!Allfiles && sav_str[0] == '.')
  939.             break;
  940.         add_to_list(&Blks, sav_str);
  941. #ifdef LENS
  942.         if (mlen > Blks.maxlen)
  943.             Blks.maxlen = mlen;
  944. #endif
  945.         break;
  946. #endif
  947.  
  948. #ifndef apollo
  949. #ifdef S_IFIFO
  950.     case S_IFIFO:
  951.         if (!Allfiles && sav_str[0] == '.')
  952.             break;
  953.         add_to_list(&Fifos, sav_str);
  954. #ifdef LENS
  955.         if (mlen > Fifos.maxlen)
  956.             Fifos.maxlen = mlen;
  957. #endif
  958.         break;
  959. #endif
  960. #endif
  961.  
  962. #ifdef S_IFLNK
  963.     case S_IFLNK:
  964.         if (!Allfiles && sav_str[0] == '.')
  965.             break;
  966.         if (display)
  967.             add_to_list(&Lnks, sav_str);
  968.         link = getlink(name);
  969.         if (display)
  970.             add_to_list(&Lnksn, link);
  971.         if (link != (char *) NULL)
  972.             free(link);
  973. #ifdef LENS
  974.         if (mlen > Lnks.maxlen && display)
  975.             Lnks.maxlen = mlen;
  976. #endif
  977.         if (stat(name, &sbuf) < 0) {
  978.             if (!Ignore) 
  979.                 (void) fprintf(stderr,
  980.                                "%s: %s: can't resolve symbolic link\n",
  981.                                Progname, name);
  982.         }
  983.         else {
  984.             if (display && Mark_links) {
  985.                 *(sav_str + mlen) = '@';
  986.                 ++mlen;
  987.                 *(sav_str + mlen) = '\0';
  988. #ifndef LENS
  989.                 if (mlen > Maxlen)
  990.                     Maxlen = mlen;
  991. #endif
  992.             }
  993.  
  994.             if (display ||  (sbuf.st_mode & S_IFMT) == S_IFDIR)
  995.                 switch (sbuf.st_mode & S_IFMT) {
  996.  
  997.             case S_IFDIR:
  998.                 if (cnt != 1)        /*dont store the dir name on entry */
  999.                     add_to_list(&Dirs, sav_str);
  1000.                 /* never called - left for expansion to recursive */
  1001.                 /* searches of subdirectories                     */
  1002.                 if (Level++ == 0)
  1003.                     directory(name);
  1004. #ifdef LENS
  1005.                 if (mlen > Dirs.maxlen)
  1006.                     Dirs.maxlen = mlen;
  1007. #endif
  1008.                 break;
  1009.  
  1010.             case S_IFREG:
  1011.                 if (Executables
  1012.                     && (sbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
  1013.                     *(sav_str + mlen) = '*';
  1014.                     ++mlen;
  1015.                     *(sav_str + mlen) = '\0';
  1016. #ifndef LENS
  1017.                     if (mlen > Maxlen)
  1018.                         Maxlen = mlen;
  1019. #endif
  1020.                 }
  1021.                 add_to_list(&Fls, sav_str);
  1022. #ifdef LENS
  1023.                 if (mlen > Fls.maxlen)
  1024.                     Fls.maxlen = mlen;
  1025. #endif
  1026.                 break;
  1027.  
  1028. #ifdef S_IFCHR
  1029.             case S_IFCHR:
  1030.                 add_to_list(&Chrs, sav_str);
  1031. #ifdef LENS
  1032.                 if (mlen > Chrs.maxlen)
  1033.                     Chrs.maxlen = mlen;
  1034. #endif
  1035.                 break;
  1036. #endif
  1037.  
  1038. #ifdef S_IFBLK
  1039.             case S_IFBLK:
  1040.                 add_to_list(&Blks, sav_str);
  1041. #ifdef LENS
  1042.                 if (mlen > Blks.maxlen)
  1043.                     Blks.maxlen = mlen;
  1044. #endif
  1045.                 break;
  1046. #endif
  1047.  
  1048. #ifndef apollo
  1049. #ifdef S_IFIFO
  1050.             case S_IFIFO:
  1051.                 add_to_list(&Fifos, sav_str);
  1052. #ifdef LENS
  1053.                 if (mlen > Fifos.maxlen)
  1054.                     Fifos.maxlen = mlen;
  1055. #endif
  1056.                 break;
  1057. #endif
  1058. #endif
  1059.  
  1060. #ifdef S_IFSOCK
  1061.             case S_IFSOCK :
  1062.                 add_to_list(&Socks, sav_str);
  1063. #ifdef LENS
  1064.                 if (mlen > Socks.maxlen)
  1065.                     Socks.maxlen = mlen;
  1066. #endif
  1067.                 break;
  1068. #endif
  1069.             }
  1070.         }
  1071.         break;
  1072. #endif
  1073.  
  1074. #ifdef S_IFSOCK
  1075.     case S_IFSOCK:
  1076.         if (!Allfiles && sav_str[0] == '.')
  1077.             break;
  1078.         add_to_list(&Socks, sav_str);
  1079. #ifdef LENS
  1080.         if (mlen > Socks.maxlen)
  1081.             Socks.maxlen = mlen;
  1082. #endif
  1083.         break;
  1084. #endif
  1085.  
  1086. #ifdef S_IFNAM
  1087.     case S_IFNAM:
  1088.         switch (sbuf.st_rdev) {
  1089.  
  1090.         case S_INSEM:
  1091.             if (!Allfiles && sav_str[0] == '.')
  1092.                 break;
  1093.             add_to_list(&Sems, sav_str);
  1094. #ifdef LENS
  1095.             if (mlen > Sems.maxlen)
  1096.                 Sems.maxlen = mlen;
  1097. #endif
  1098.             break;
  1099.  
  1100.         case S_INSHD:
  1101.             if (!Allfiles && sav_str[0] == '.')
  1102.                 break;
  1103.             add_to_list(&Sds, sav_str);
  1104. #ifdef LENS
  1105.             if (mlen > Sds.maxlen)
  1106.                 Sds.maxlen = mlen;
  1107. #endif
  1108.             break;
  1109.         }
  1110.         break;
  1111. #endif
  1112.  
  1113.     }
  1114.     return;
  1115. }
  1116.  
  1117. /* V A L I D _ O P T
  1118.  *
  1119.  * valid_opt() is used to translate user has supplied option
  1120.  * letters into something that this process can use. It sets
  1121.  * up the options and if a usage is requested, it sets up and
  1122.  * prints a usage message for the user.
  1123.  */
  1124.  
  1125. void valid_opt(c, usage)
  1126.     char c;
  1127.     int usage;
  1128. {
  1129.     char up[7];
  1130.  
  1131.     up[0] = '\0';
  1132.  
  1133.     switch(c) {
  1134.  
  1135.     case 'a':
  1136.         Allfiles = TRUE;
  1137.         break;
  1138.  
  1139.     case 'b':
  1140.         Only |= BLOCK_ONLY;
  1141.         break;
  1142.  
  1143.     case 'B':
  1144.         Display_size = TRUE;
  1145.         break;
  1146.  
  1147.     case 'c':
  1148.         Only |= CHAR_ONLY;
  1149.         break;
  1150.  
  1151.     case 'C':
  1152.         Sort_down = TRUE;
  1153.         break;
  1154.  
  1155.     case 'd':
  1156.         Only |= DIR_ONLY;
  1157.         break;
  1158.  
  1159.     case 'D':
  1160.         Display_single = FALSE;
  1161.         break;
  1162.  
  1163.     case 'e':
  1164.         Executables = TRUE;
  1165.         break;
  1166.  
  1167.     case 'f':
  1168.         Only |= FILE_ONLY;
  1169.         break;
  1170.  
  1171. #ifndef apollo
  1172. # ifdef S_IFIFO
  1173.     case 'F':
  1174.         Only |= FIFO_ONLY;
  1175.         break;
  1176. # endif
  1177. #endif
  1178.  
  1179.     case 'i':
  1180.         Display_inode = TRUE;
  1181.         break;
  1182.  
  1183. #ifdef S_IFLNK
  1184.     case 's':
  1185.         Only |= LNK_ONLY;
  1186.         break;
  1187.  
  1188.     case 'l':
  1189.         Mark_links = TRUE;
  1190.         break;
  1191.  
  1192.     case 'I':
  1193.         Ignore = TRUE;
  1194.         break;
  1195.  
  1196.     case 'L':
  1197.         Disp_links = TRUE;
  1198.         break;
  1199. #endif
  1200.  
  1201. #ifdef S_IFNAM
  1202.     case 'm':
  1203.         Only |= SD_ONLY;
  1204.         break;
  1205.  
  1206.     case 'M':
  1207.         Only |= SEM_ONLY;
  1208.         break;
  1209. #endif
  1210.  
  1211.     case 'r':
  1212.         Sort_wanted = FALSE;
  1213.         break;
  1214.  
  1215. #ifdef S_IFSOCK
  1216.     case 'S':
  1217.         Only |= SOCK_ONLY;
  1218.         break;
  1219. #endif
  1220.  
  1221.     case '1':
  1222.         Single = TRUE;
  1223.         break;
  1224.  
  1225.     case 'v':
  1226.         (void) fprintf(stderr,"%s:\t%s\n\tRelease: %d\n\tPatch level: %d\n",
  1227.                                Progname, sccsid, RELEASE, PATCHLEVEL);
  1228.         exit(1);
  1229.     /*NOTREACHED*/
  1230.  
  1231.   case 'x':
  1232.         Display_accessable += ACCESSABLE_ONLY;
  1233.         break;
  1234.  
  1235.     case 'X':
  1236.         Display_accessable += INACCESSABLE_ONLY;
  1237.         break;
  1238.  
  1239.     default:
  1240.         if (usage == TRUE) {
  1241. #ifdef S_IFLNK
  1242.             (void) strcat(up, "IlLs");
  1243. #endif
  1244. #ifdef S_IFSOCK
  1245.             (void) strcat(up, "S");
  1246. #endif
  1247. #ifdef S_IFNAM
  1248.             (void) strcat(up, "mM");
  1249. #endif
  1250.             (void) fprintf(stderr,
  1251.                     "usage: %s [-abBcCdDefFivxX1%s] [directories or files]\n",
  1252.                      Progname, up);
  1253.             exit(1);
  1254.         }
  1255.     }
  1256.  
  1257.     return;
  1258. }
  1259.  
  1260. /* S E T _ E N V _ V A R S
  1261.  *
  1262.  * set_env_vars() is used get the environment variables that
  1263.  * lc uses. The environment variable LC can be used to setup
  1264.  * the default way in which the user likes to see a directory
  1265.  * listing. Command line options override those specified in
  1266.  * the environment.
  1267.  */
  1268.  
  1269. void set_env_vars()
  1270. {
  1271.     char *ep;
  1272.  
  1273.     if ((ep = getenv("COLS")) != (char *) NULL) {
  1274.         if (sscanf(ep, "%d", &Screen_width) == 0
  1275.             || (Screen_width != 80 && Screen_width != 132))
  1276.             Screen_width = 80;
  1277.     }
  1278.  
  1279.     if ((ep = getenv("LC")) != (char *) NULL) {
  1280.         while (*ep != '\0') {
  1281.             valid_opt(*ep, FALSE);
  1282.             ep++;
  1283.         }
  1284.     }
  1285.  
  1286.     return;
  1287. }
  1288.  
  1289. /* S P D I S T:   return the distance between two names
  1290.  *
  1291.  * very rough spelling metric:
  1292.  *          0 if the strings are identical
  1293.  *          1 if two chars are transposed
  1294.  *          2 if 1 char wrong, added or deleted
  1295.  *          3 otherwise
  1296.  */
  1297. #define EQ(s, t) (strcmp(s, t) == 0)
  1298.  
  1299. int spdist(s, t)
  1300.     char *s;
  1301.     char *t;
  1302. {
  1303.     while (*s++ == *t) {
  1304.         if (*t++ == '\0')
  1305.             return 0;             /* exact match */
  1306.     }
  1307.     if (*--s) {
  1308.         if (*t) {
  1309.             if (s[1] && t[1] && *s == t[1] && *t == s[1] && EQ(s+2, t+2))
  1310.                 return 1;             /* transposition */
  1311.             if (EQ(s+1, t+1))
  1312.                 return 2;             /* 1 char mismatch */
  1313.         }
  1314.         if (EQ(s+1, t))
  1315.             return 2;                /* extra chacter */
  1316.     }
  1317.     if (*t && EQ(s, t+1))
  1318.         return 2;                   /* missing character */
  1319.     return 3;
  1320. }
  1321.  
  1322. /*    M I N D I S T       
  1323.  * 
  1324.  *  mindist() searches the directory for the best guess
  1325.  *  in the event the requested file was not located.
  1326.  */
  1327.  
  1328. #if (POSIX || BSD)
  1329. int mindist(dir, guess, best)    /* set best, return distance 0..3 */
  1330.     char *dir;
  1331.     char *guess;
  1332.     char *best;
  1333. {
  1334.     DIR *dfd;
  1335.     int d;
  1336.     int nd;
  1337. #ifdef DIRECT
  1338.     struct direct *dp;
  1339. #else
  1340.     struct dirent *dp;
  1341. #endif
  1342.  
  1343.     if (dir[0] == '\0')
  1344.         dir = ".";
  1345.     d = 3;                      /* minimum distance */
  1346.  
  1347.     if ((dfd = opendir(dir)) == NULL)
  1348.         return d;
  1349.  
  1350.     while ((dp = readdir(dfd)) != NULL) {
  1351.         if (dp->d_ino) {
  1352.             nd = spdist(dp->d_name, guess);
  1353.             if (nd <= d && nd != 3) {
  1354.                 (void) strcpy(best, dp->d_name);
  1355.                 d = nd;
  1356.                 if (d == 0)    /* exact match */
  1357.                     break;
  1358.             }
  1359.         }
  1360.     }
  1361.     (void) closedir(dfd);
  1362.     return d;
  1363. }
  1364.  
  1365. #else /* not POSIX or BSD */
  1366.  
  1367. /*    M I N D I S T       
  1368.  * 
  1369.  *  mindist() searches the directory for the best guess
  1370.  *  in the event the requested file was not located.
  1371.  */
  1372.  
  1373. int mindist(dir, guess, best)    /* set best, return distance 0..3 */
  1374.     char *dir;
  1375.     char *guess;
  1376.     char *best;
  1377. {
  1378.     FILE *fd;
  1379.     int d;
  1380.     int nd;
  1381.     struct {
  1382.         ino_t ino;
  1383.         char name[NAME_MAX + 1];   /* 1 more than in dir.h */
  1384.     } nbuf;
  1385.  
  1386.     nbuf.name[NAME_MAX] = '\0';   /* +1 for terminal '\0' */
  1387.     if (dir[0] == '\0')
  1388.         dir = ".";
  1389.     d = 3;                      /* minimum distance */
  1390.     if ((fd = fopen(dir, "r")) == (FILE *) NULL)
  1391.         return d;
  1392.     while (fread((char *) &nbuf, sizeof(struct direct), 1, fd) > 0) {
  1393.         if (nbuf.ino) {
  1394.             nd = spdist(nbuf.name, guess);
  1395.             if (nd <= d && nd != 3) {
  1396.                 (void) strcpy(best, nbuf.name);
  1397.                 d = nd;
  1398.                 if (d == 0)    /* exact match */
  1399.                     break;
  1400.             }
  1401.         }
  1402.     }
  1403.     (void) fclose(fd);
  1404.     return d;
  1405. }
  1406. #endif
  1407.  
  1408. /* S P N A M E:    return correctly spelled filename
  1409.  *
  1410.  * spname(oldname, newname) char *oldname, *newname;
  1411.  *      returns  -1 if no reasonable match to oldname,
  1412.  *                0 if exact match,
  1413.  *                1 if corrected.
  1414.  * stores corrected name in newname.
  1415.  */
  1416.  
  1417. int spname(oldname, newname)
  1418.     char *oldname;
  1419.     char *newname;
  1420. {
  1421.     char *new = newname;
  1422.     char *old = oldname;
  1423.     char *p;
  1424.     char best[NAME_MAX + 1];
  1425.     char guess[NAME_MAX + 1];
  1426.  
  1427.     for (;;) {
  1428.         while (*old == '/')   /* skip slashes */
  1429.             *new++ = *old++;
  1430.         *new = '\0';
  1431.         if (*old == '\0')     /* exact or corrected */
  1432.             return (strcmp(oldname, newname) != 0);
  1433.         p = guess;            /* copy next component into guess */
  1434.         for (/* void */ ; *old != '/' && *old != '\0'; old++) {
  1435.             if (p < (guess + NAME_MAX))
  1436.                 *p++ = *old;
  1437.         }
  1438.         *p = '\0';
  1439.         if (mindist(newname, guess, best) >= 3)
  1440.             return (-1);        /* hopeless */
  1441.         for (p = best; *new = *p++; new++)   /* add to end */
  1442.             /* void */;
  1443.     }
  1444. }
  1445.  
  1446. /*  I N _ C D P A T H
  1447.  *
  1448.  *  in_cdpath() searches the CDPATH stored in the environment
  1449.  *  for the filename specified. If it is found, fill the
  1450.  *  storage area refered to by buffer with the corrected path.
  1451.  *  Return TRUE if located and FALSE if not located in the CDPATH.
  1452.  */
  1453.  
  1454. int in_cdpath(requested_dir, buffer, check_spelling)
  1455.     char *requested_dir;
  1456.     char *buffer;
  1457.     int  check_spelling; 
  1458. {
  1459.     static char *cdpath;
  1460.     static int first = 1;
  1461.  
  1462.     char *cp;
  1463.     char *path;
  1464.     char patbuf[BUFSIZE + 1];
  1465.     int quit;
  1466.  
  1467.     if (first) {
  1468.         if ((cdpath = getenv("CDPATH")) != (char *) NULL)
  1469.             cdpath = str_sav(cdpath);
  1470.         first = 0;
  1471.     }   
  1472.  
  1473.     if (cdpath == (char *) NULL)
  1474.         return (0);
  1475.  
  1476.     (void) strcpy(patbuf, cdpath);
  1477.     path = patbuf;
  1478.  
  1479.     quit = 0;
  1480.  
  1481.     while (!quit) {
  1482.         cp = strchr(path, ':');
  1483.         if (cp == (char *) NULL)
  1484.             quit++;
  1485.         else
  1486.             *cp = '\0';
  1487.  
  1488.         if (*(path + 1) == '\0' && *path == '/')
  1489.             (void) sprintf(buffer, "/%s", requested_dir);
  1490.         else
  1491.             (void) sprintf(buffer, "%s/%s",
  1492.                            (*path ? path : "."), requested_dir);
  1493.  
  1494.         if (access(buffer, 0) == 0)
  1495.             return (TRUE);
  1496.  
  1497.         if (check_spelling)
  1498.         {
  1499.             char bfr[BUFSIZ + 1];
  1500.             (void) strcpy(bfr, buffer);
  1501.             if (spname(bfr, buffer) == 1)
  1502.                 return (TRUE);
  1503.         }
  1504.         
  1505.         path = ++cp;
  1506.     }
  1507.     return (FALSE);
  1508. }
  1509.  
  1510.  
  1511. /*  M A I N 
  1512.  * 
  1513.  *  Ye olde main();
  1514.  */
  1515.  
  1516. int main(argc, argv)
  1517.     int argc;
  1518.     char *argv[];
  1519. {
  1520.     char *argp;
  1521. #ifdef S_IFLNK
  1522.     char *link;
  1523.     int lnk_found;
  1524. #endif
  1525.     char buf[BUFSIZE + 1];
  1526.     int idx;
  1527.     int nl;
  1528.     struct stat sbuf;
  1529.  
  1530.     nl = idx = FALSE;
  1531.  
  1532.     /* get the base name of the command */
  1533.  
  1534.     if ((Progname = strrchr(argv[0], '/')) == (char *) NULL) 
  1535.         Progname = argv[0];
  1536.     else
  1537.         ++Progname;
  1538.  
  1539.     set_env_vars();                       /* get environment variables */
  1540.  
  1541.     /* All command line arguments must be */
  1542.     /* lumped together such as `lc -aef`  */
  1543.  
  1544.     if (argc > 1 && argv[1][0] == '-') {  /* if first parm is command */
  1545.         argp = argv[1];
  1546.  
  1547.         while (*(++argp))
  1548.             valid_opt(*argp, TRUE);
  1549.  
  1550.         ++argv;
  1551.         --argc;
  1552.     }
  1553.  
  1554.     /*
  1555.     ** The user has not specified a file or directory 
  1556.     ** to be examined so assume that the current directory
  1557.     ** is what the user is requesting.
  1558.     */
  1559.     if (argc == 1) {
  1560.         (void) strcpy(buf, ".");
  1561.         lc(buf, 1);
  1562.         print_info();
  1563.         return(0);
  1564.     }
  1565.  
  1566.     /*
  1567.     ** The user has specified at least one file or 
  1568.     ** directory to be examined.
  1569.     */
  1570.     if (argc > 2)
  1571.         nl = TRUE;
  1572.     while (--argc > 0) {
  1573.         ++argv;
  1574.         (void) strcpy(buf, *argv);
  1575. skipit:
  1576. #ifdef S_IFLNK
  1577.         lnk_found = 0;
  1578.         if (lstat(buf, &sbuf) == -1) {
  1579.             lnk_found = 1;
  1580. #else
  1581.         if (stat(buf, &sbuf) == -1) {
  1582. #endif
  1583.             if (in_cdpath(*argv, buf, FALSE) ||   /* Look for it in CDPATH */
  1584.                 (spname(*argv, buf) != -1)   ||   /* Look for it mispelled */
  1585.                 in_cdpath(*argv, buf, TRUE)) {    /* Mispelled in CDPATH ? */
  1586.                 /*
  1587.                  ** Check to see if the requested is in the CDPATH
  1588.                  ** and if not try to correct for typos. If that fails
  1589.                  ** then check for typos in the CDPOATH. Always print
  1590.                  ** the name of what was found...
  1591.                  */
  1592.  
  1593.                 nl = TRUE;
  1594.                 goto skipit;
  1595.             }
  1596. #ifdef S_IFLNK
  1597.             else if (lnk_found) 
  1598.                 (void) fprintf(stderr,"%s: %s: can't resolve symbolic link\n",
  1599.                                Progname, *argv);
  1600. #endif 
  1601.             else 
  1602.                 (void)fprintf(stderr, "%s: can't find %s\n",
  1603.                               Progname, *argv);
  1604.         }
  1605.         else {
  1606. #ifdef S_IFLNK
  1607.             if ((sbuf.st_mode & S_IFMT) == S_IFLNK)
  1608.                 (void) stat(buf, &sbuf);
  1609.             /*
  1610.             ** No need to check return code here: if stat() fails use
  1611.             ** the sbuf retrieved with lstat() earlier.
  1612.             */
  1613. #endif
  1614.             switch (sbuf.st_mode & S_IFMT) {
  1615.  
  1616.             case S_IFREG:
  1617.                 if (Display_single)
  1618.                    (void) printf("%s: file\n", buf);
  1619.                 break;
  1620.  
  1621. #ifdef S_IFCHR
  1622.             case S_IFCHR:
  1623.                 if (Display_single)
  1624.                    (void) printf("%s: character special file\n", buf);
  1625.                 break;
  1626. #endif
  1627.  
  1628. #ifdef S_IFBLK
  1629.             case S_IFBLK:
  1630.                 if (Display_single)
  1631.                    (void) printf("%s: block special file\n", buf);
  1632.                 break;
  1633. #endif
  1634.  
  1635. #ifndef apollo
  1636. #ifdef S_IFIFO
  1637.             case S_IFIFO:
  1638.                 if (Display_single)
  1639.                    (void) printf("%s: fifo file\n", buf);
  1640.                 break;
  1641. #endif
  1642. #endif
  1643.  
  1644. #ifdef S_IFSOCK
  1645.             case S_IFSOCK:
  1646.                 if (Display_single)
  1647.                    (void) printf("%s: socket file\n", buf);
  1648.                 break;
  1649. #endif
  1650.  
  1651. #ifdef S_IFLNK
  1652.             case S_IFLNK:
  1653.                 if (Display_single) {
  1654.                    if ((link = getlink(buf)) != (char *) NULL) {
  1655.                        (void) printf("%s: symbolic link to %s\n", buf,link);
  1656.                        free(link);
  1657.                    }
  1658.                    else
  1659.                        (void) printf("%s: unresolved symbolic link\n", buf);
  1660.                 }
  1661.                 break;
  1662. #endif
  1663.  
  1664. #ifdef S_IFNAM
  1665.             case S_IFNAM:
  1666.                 if (Display_single) {
  1667.                    if (sbuf.st_rdev == S_INSHD)
  1668.                        (void) printf("%s: shared memory file\n", buf);
  1669.                    if (sbuf.st_rdev == S_INSEM)
  1670.                        (void) printf("%s: semaphore file\n", buf);
  1671.                 }
  1672.                 break;
  1673. #endif
  1674.  
  1675.             case S_IFDIR:
  1676.                 Maxlen = Level = 0;
  1677. #ifdef S_IFBLK
  1678.                 Blks.num = 0;
  1679. #ifdef LENS
  1680.                 Blks.maxlen = 0;
  1681. #endif
  1682. #endif
  1683.  
  1684. #ifdef S_IFCHR
  1685.                 Chrs.num = 0;
  1686. #ifdef LENS
  1687.                 Chrs.maxlen = 0;
  1688. #endif
  1689. #endif
  1690.  
  1691.                 Dirs.num = Fls.num = 0;
  1692. #ifdef LENS
  1693.                 Dirs.maxlen = Fls.maxlen = 0;
  1694. #endif
  1695.  
  1696. #ifndef apollo
  1697. #ifdef S_IFIFO
  1698.                 Fifos.num = 0;
  1699. #ifdef LENS
  1700.                 Fifos.maxlen = 0;
  1701. #endif
  1702. #endif
  1703. #endif
  1704.  
  1705. #ifdef S_IFLNK
  1706.                 Lnks.num = Lnksn.num = 0;
  1707. #ifdef LENS
  1708.                 Lnks.maxlen = Lnksn.maxlen = 0;
  1709. #endif
  1710. #endif
  1711.  
  1712. #ifdef S_IFSOCK
  1713.                 Socks.num = 0;
  1714. #ifdef LENS
  1715.                 Socks.maxlen = 0;
  1716. #endif
  1717. #endif
  1718.  
  1719. #ifdef S_IFNAM
  1720.                 Sds.num = Sems.num = 0;
  1721. #ifdef LENS
  1722.                 Sds.maxlen = Sems.maxlen = 0;
  1723. #endif
  1724. #endif
  1725.  
  1726.                 if (nl == TRUE) {
  1727.                     if (idx > 0)
  1728.                         (void) puts("");
  1729.                     else
  1730.                         ++idx;
  1731.                     (void) fputs(": ", stdout);
  1732.                     (void) fputs(buf, stdout);
  1733.                     (void) puts(" :");
  1734.                 }
  1735.                 lc(buf, 1);
  1736.                 print_info();
  1737.                 break;
  1738.  
  1739.             default:
  1740.                 (void) printf("%s: unknown file type\n", buf);
  1741.                 break;
  1742.             }
  1743.         }
  1744.     }
  1745.     return(0);
  1746. }
  1747.