home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume31 / lc / part02 < prev    next >
Encoding:
Text File  |  1992-08-13  |  44.6 KB  |  1,800 lines

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