home *** CD-ROM | disk | FTP | other *** search
/ ftp.freefriends.org / ftp.freefriends.org.tar / ftp.freefriends.org / arnold / Source / mush.rstevens.tar.gz / mush.tar / glob.c.orig < prev    next >
Text File  |  1990-05-03  |  20KB  |  810 lines

  1. #include "mush.h"
  2. #include "glob.h"
  3.  
  4. /*
  5.  * Buried somewhere in here is the skeleton of a pattern matcher posted
  6.  * by koblas@mips.COM (David Koblas).  It has been hacked almost beyond
  7.  * recognition to handle more complex patterns, and directory search has
  8.  * been added (patterns are split at '/' characters when file globbing).
  9.  */
  10.  
  11. #ifdef TEST    /* Define TEST to build a stand-alone file globbing program */
  12.  
  13. extern char *malloc(), *realloc();
  14.  
  15. #define getpath(x,y) (*(y) = 0, (x))
  16. #define Access access
  17. #define Strcpy(x,y) (strcpy(x,y), strlen(x))
  18. #define savestr(x)  (strcpy(malloc(strlen(x)+1),x))
  19. #ifndef max
  20. #define max(x,y) ((x) > (y) ? (x) : (y))
  21. #endif /* max */
  22. #ifndef min
  23. #define min(x,y) ((x) > (y) ? (y) : (x))
  24. #endif /* min */
  25. #define xfree free
  26. #undef wprint
  27. #define wprint printf
  28. #define debug 0
  29. #undef sprintf
  30.  
  31. #define TESTGLOB(str1,str2) \
  32.     printf("%s %s = %s\n",str1,str2,glob(str1,str2)?"TRUE":"FALSE")
  33.  
  34. main(argc, argv)
  35. int argc;
  36. char **argv;
  37. {
  38.     char **e;
  39.     int f;
  40.  
  41.     if (argc > 1)
  42.     while (*++argv) {
  43.         (void) printf("%s -->\n", *argv);
  44.         if (f = filexp(*argv, &e)) {
  45.         columnate(f, e, 0);
  46.         }
  47.     }
  48. #ifdef TEST2    /* Define TEST2 to automatically run these test cases */
  49.     TESTGLOB("abcdefg", "abcdefg");
  50.     TESTGLOB("abcdefg", "a?cd?fg");
  51.     TESTGLOB("abcdefg", "ab[cde]defg");
  52.     TESTGLOB("abcdefg", "ab[a-z]defg");
  53.     TESTGLOB("abcdefg", "ab[a-z]defg");
  54.     TESTGLOB("ab]defg", "ab[a]c]defg");
  55.     TESTGLOB("ab]defg", "ab[a\\]c]defg");
  56.     TESTGLOB("abcdefg", "ab*fg");
  57.     TESTGLOB("./bc/def/gh/ij", "*de*");
  58.     TESTGLOB("./der/den/deq/der/", "*deq*");
  59.     TESTGLOB("./bc/def/gh/ij", "*ij");
  60.     TESTGLOB("./ij", ".?ij");
  61.     TESTGLOB("./bc/def/gh/ij", "./*");
  62.     TESTGLOB("abcdef", "*def");
  63.     TESTGLOB("abcdef", "*abcdef");
  64.     TESTGLOB("abcdef", "abc*");
  65.     TESTGLOB("abcdef", "abcdef*");
  66.     TESTGLOB("abcdef", "*?*{xxx,,yy}");
  67.     TESTGLOB("abcdef", "abcde{f}");
  68.     TESTGLOB("abcdef", "abcdef{xxx,,yyy}");
  69.     TESTGLOB("abcdef", "abc{def,qwrx}");
  70.     TESTGLOB("abcdef", "abc{ab,def,qwrx}");
  71.     TESTGLOB("abcdef", "{naqrwer,fuwnwer,as,abc,a}{ab,def,qwrx}");
  72.     TESTGLOB("abcdef", "{naqrwer,*,as,abc,a}{ab,def,qwrx}");
  73.     TESTGLOB("abcdef", "{{a*,b*},as,a}{ab,def,qwrx}");
  74.     TESTGLOB("abcdef", "{{c*,b*},as,a}{ab,def,qwrx}");
  75.     TESTGLOB("abcdef", "{{c*,?b*},as,a}{ab,def,qwrx}");
  76.     TESTGLOB("abcdef", "{naqrwer,fuwnwer,as,abc,a}{ab,d*f,qwrx}");
  77. #endif /* TEST2 */
  78. }
  79.  
  80. char *
  81. any(s1, s2)
  82. register char *s1, *s2;
  83. {
  84.     register char *p;
  85.     if (!s1 || !*s1 || !s2 || !*s2)
  86.     return 0;
  87.     for( ; *s1; s1++) {
  88.     for(p = s2; *p; p++)
  89.         if (*p == *s1)
  90.         return s1;
  91.     }
  92.     return 0;
  93. }
  94.  
  95. #endif /* TEST */
  96.  
  97. /*
  98.  * Make a string into a one-element vector
  99.  */
  100. char **
  101. unitv(s)
  102. char *s;
  103. {
  104.     char **v;
  105.  
  106.     if (v = (char **)malloc((unsigned)(2 * sizeof(char *)))) {
  107.     v[0] = savestr(s);
  108.     v[1] = NULL;
  109.     }
  110.     return v;
  111. }
  112.  
  113. /*
  114.  * Append one vector to another
  115.  */
  116. catv(s1, v1, s2, v2)
  117. int s1, s2;
  118. char ***v1, **v2;
  119. {
  120.     int i;
  121.  
  122.     if (s1 < 0 || !v1)
  123.     return -1;
  124.     if (s2 < 0 || !v2)
  125.     return s1;
  126.  
  127.     /* realloc(NULL, size) should be legal, but Sun doesn't support it. */
  128.     if (*v1)
  129.         *v1 = (char **)realloc(*v1,(unsigned)((s1+s2+1) * sizeof(char **)));
  130.     else
  131.         *v1 = (char **)malloc((unsigned)((s1+s2+1) * sizeof(char **)));
  132.  
  133.     if (*v1) {
  134.     for (i = 0; i < s2 && v2[i]; i++)
  135.         (*v1)[s1 + i] = v2[i]; 
  136.     (*v1)[s1 + i] = NULL;
  137.     xfree(v2);
  138.     return s1 + i;
  139.     }
  140.     return -1;
  141. }
  142.  
  143. /*
  144.  * A duplicate-eliminating comparison for sorting.  It treats an empty
  145.  * string as greater than any other string, and forces empty one of any
  146.  * pair of of equal strings.  Two passes are sufficient to move the empty
  147.  * strings to the end where they can be deleted by the calling function.
  148.  *
  149.  * This is NOT compatible with the ANSI C qsort(), which requires that the
  150.  * comparison function will not modify its arguments!
  151.  */
  152. uniqcmp(p1, p2)
  153. char **p1, **p2;
  154. {
  155.     int cmp;
  156.  
  157.     if (**p1 && !**p2)
  158.     return -1;
  159.     if (**p2 && !**p1)
  160.     return 1;
  161.     if (cmp = strcmp(*p1, *p2))
  162.     return cmp;
  163.     **p2 = 0;
  164.     return -1;
  165. }
  166.  
  167. /*
  168.  * Expand a pattern into a list of file names.  Returns the number of
  169.  * matches.  As in csh, names generated from pattern sets are returned
  170.  * even if there are no actual matches.
  171.  */
  172. filexp(pat, exp)
  173. char *pat, ***exp;
  174. {
  175.     char **t1, **t2;
  176.     int n, new, cnt;
  177.  
  178.     if (!exp)
  179.     return -1;
  180.     if (!pat || !*pat)
  181.     return 0;
  182.  
  183.     if ((n = sxp(pat, &t1)) > 0)
  184.     cnt = 0;
  185.     else
  186.     return n;
  187.     *exp = DUBL_NULL;
  188.     while (n--)
  189.     if ((new = fxp(t1[n], &t2)) > 0 || new++ == 0 && t2)
  190.         cnt = catv(cnt, exp, new, t2);
  191.     if (cnt > 1) {
  192.     /* Two sort passes to eliminate duplicates -- see uniqcmp() */
  193.     qsort((char *)*exp, cnt, sizeof(char *), uniqcmp);
  194.     qsort((char *)*exp, cnt, sizeof(char *), uniqcmp);
  195.     while (!(*exp)[cnt - 1][0]) {
  196.         xfree((*exp)[--cnt]);
  197.         (*exp)[cnt] = NULL;
  198.     }
  199.     }
  200.     return cnt;
  201. }
  202.  
  203. /*
  204.  * Expand a filename with globbing chars into a list of matching filenames.
  205.  * Pattern set notatation which crosses directories is not handled, e.g.
  206.  * "fi{le/exp,nger/h}and" will NOT expand to "file/expand finger/hand".
  207.  * Such patterns must be pre-expanded by sxp() before calling fxp().
  208.  *
  209.  * The list of expansions is placed in *exp, and the number of matches
  210.  * is returned, or -1 on an error.
  211.  */
  212. fxp(name, exp)
  213. char *name, ***exp;
  214. {
  215.     char *p;
  216.     int isdir;
  217.  
  218.     if (!exp)
  219.     return -1;
  220.  
  221.     isdir = 1; /* ignore no such file */
  222.     p = getpath(name, &isdir);
  223.     if (isdir < 0)
  224.     return -1;
  225.     else if (isdir)
  226.     return ((*exp = unitv(p)) ? 1 : -1);
  227.     return pglob(p, 0, exp);
  228. }
  229.  
  230. /*
  231.  * Match all globbings in a path.  Mutually recursive with dglob(), below.
  232.  * The first "skip" characters of the path are not globbed, see dglob().
  233.  *
  234.  * Returns the number of matches, or -1 on an error.  *exp is set to the
  235.  * list of matches.
  236.  *
  237.  * If the path has no metachars, it is returned in *exp whether it matches
  238.  * a real file or not.  This allows patterns built by sxp() to be recognized
  239.  * and returned even when there are no matches (ala csh generation of names
  240.  * from pattern sets).  pglob() still returns zero in this case.
  241.  */
  242. pglob(path, skip, exp)
  243. char *path, ***exp;
  244. int skip;
  245. {
  246.     char *t, *t2;
  247.     int ret = 0;
  248.  
  249.     if (!path || !exp || skip < 0)
  250.     return -1;
  251.     *exp = DUBL_NULL; /* Must be null in case of zero matches and no sets */
  252.  
  253.     for (t = t2 = path + skip; (t2 = any(t2, META)) && *t2 == '/'; t = t2++)
  254.     ;
  255.     if (!t2) {
  256.     ret = ((*exp = unitv(path)) ? 1 : -1);
  257.     if (ret > 0 && Access(path, F_OK) < 0)
  258.         ret = 0;
  259.     } else {
  260.     if (t2 = index(t + 1, '/'))
  261.         *t2++ = 0;
  262.     if (*t == '/') {
  263.         *t++ = 0;
  264.         if (!*path)
  265.         ret = dglob("/", t, t2, exp);
  266.         else
  267.         ret = dglob(path, t, t2, exp);
  268.     } else {
  269.         ret = dglob("", t, t2, exp);
  270.     }
  271.     }
  272.     return ret;
  273. }
  274.  
  275. /*
  276.  * Search a directory (possibly recursively) for glob matches.
  277.  * Argument pat1 is a pattern to be matched in this directory,
  278.  * and pat2 is a pattern to be matched in matched subdirectories.
  279.  *
  280.  * Matches are returned through *exp.
  281.  */
  282. dglob(dir, pat1, pat2, exp)
  283. char *dir, *pat1, *pat2, ***exp;
  284. {
  285.     DIR *dirp;
  286.     struct dirent *dp;
  287.     char *b, *d, buf[MAXPATHLEN], **tmp;
  288.     int n, ret = 0, skip;
  289.  
  290.     if (!dir || !exp)
  291.     return -1;
  292.     d = (*dir ? dir : ".");
  293.     if (!(dirp = opendir(d)))
  294.     return -1;
  295.     b = buf + Strcpy(buf, dir);
  296.     if (b > buf && *(b - 1) != '/')
  297.     *b++ = '/';
  298.     skip = b - buf; /* We know this much matches, don't glob it again */
  299.     while (ret >= 0 && (dp = readdir(dirp))) {
  300.     if (fglob(dp->d_name, pat1)) {
  301.         if (pat2) {
  302.         (void) sprintf(b, "%s/%s", dp->d_name, pat2);
  303.         n = pglob(buf, skip, &tmp);
  304.         ret = catv(ret, exp, n, tmp);
  305.         } else {
  306.         (void) strcpy(b, dp->d_name);
  307.         ret = catv(ret, exp, 1, unitv(buf));
  308.         }
  309.     }
  310.     }
  311.     closedir(dirp);
  312.     return ret;
  313. }
  314.  
  315. /*
  316.  * Match file names.  This means that metachars do not match leading ".".
  317.  */
  318. fglob(str, pat)
  319. char *str, *pat;
  320. {
  321.     if (!str || !pat || *str == '.' && *pat != '.')
  322.     return FALSE;
  323.     else
  324.     return glob(str, pat);
  325. }
  326.  
  327. /*
  328.  * Match two concatenated patterns.  Mainly for use by sglob().
  329.  */
  330. static
  331. glob2(str, pat1, pat2)
  332. char *str, *pat1, *pat2;
  333. {
  334.     char buf[MAXPATHLEN];
  335.  
  336.     if (!str || !pat1 && !pat2)
  337.     return FALSE;
  338.     (void) sprintf(buf, "%s%s", pat1? pat1 : "", pat2? pat2 : "");
  339.     return glob(str, buf);
  340. }
  341.  
  342. /*
  343.  * The basic globbing matcher.
  344.  *
  345.  * "*"           = match 0 or more occurances of anything
  346.  * "[abc]"       = match any of "abc" (ranges supported)
  347.  * "{xx,yy,...}" = match any of "xx", "yy", ... where
  348.  *                 "xx", "yy" can be any pattern or empty
  349.  * "?"           = match any character
  350.  */
  351. glob(str, pat)
  352. char *str, *pat;
  353. {
  354.     int done = FALSE, ret = FALSE;
  355.  
  356.     if (!str || !pat)
  357.     return FALSE;
  358.  
  359.     while (*pat && !done && (*str || (*pat == '{' || *pat == '*'))) /*}*/ {
  360.     /*
  361.      * First look for a literal match, stepping over backslashes
  362.      * in the pattern to match against the "protected" character.
  363.      * Ordering and precendence are important in this expression!
  364.      */
  365.     if (*pat == '\\' && *str == *++pat || *str == *pat) {
  366.         str++;
  367.         pat++;
  368.     } else switch (*pat++) {
  369.         case '*':    /* Match any string */
  370.         if (!*pat) {
  371.             while (*str)
  372.             str++;
  373.             break;
  374.         }
  375.         /*
  376.          * Try the rest of the glob against every
  377.          * possible suffix of the string.  A bit
  378.          * inefficient in cases that eventually fail.
  379.          */
  380.         while (*str && !(ret = glob(str++, pat)))
  381.             ;
  382.         return ret;
  383.         break;
  384.         case '[':    /* Match a set */
  385.         repeat:
  386.         /* If we've hit the end of the set, give up. */
  387.         if (!*pat || *pat == ']' || *pat == '\\' && !*++pat) {
  388.             done = TRUE;
  389.             break;
  390.         }
  391.         /* Check for a range. */
  392.         if (*(pat + 1) == '-') {
  393.             char c = *pat++;
  394.             /* We don't handle open-ended ranges. */
  395.             if (*++pat == ']' || *pat == '\\' && !*++pat) {
  396.             done = TRUE;
  397.             break;
  398.             }
  399.             if (*str < c || *str > *pat) {
  400.             pat++;
  401.             goto repeat;
  402.             }
  403.         } else if (*pat != *str) {
  404.             pat++;
  405.             goto repeat;
  406.         }
  407.         /*
  408.          * We matched either the range or a literal member of
  409.          * the set.  Skip to the end of the set.
  410.          */
  411.         pat++;
  412.         while (*pat && *pat != ']')
  413.             if (*pat++ == '\\' && *pat)
  414.             pat++;
  415.         /*
  416.          * If no pattern remains, the set was never closed,
  417.          * so don't increment.  This will cause a FALSE return.
  418.          */
  419.         if (*pat) {
  420.             pat++;
  421.             str++;
  422.         }
  423.         break;
  424.         case '?':    /* Match any one character */
  425.         str++;
  426.         break;
  427.         case '{':    /* } Match any of a set of patterns */
  428.         return sglob(str, pat - 1, TRPL_NULL);
  429.         break;
  430.         default:
  431.         done = TRUE;
  432.     }
  433.     }
  434.     while (*pat == '*')
  435.     pat++;
  436.     return ((*str == '\0') && (*pat == '\0'));
  437. }
  438.  
  439. /*
  440.  * Match a pattern set {s1,s2,...} followed by any other pattern.
  441.  * Pattern sets and other patterns may nest arbitrarily.
  442.  *
  443.  * If "mat" is not a null pointer, a vector of possible expansions
  444.  * is generated and placed in *mat; otherwise, the expansions are
  445.  * matched against str and a truth value is returned ("/" is NOT
  446.  * treated as a directory separator in this case).  NOTE: The vector
  447.  * of expansions may still contain nested pattern sets, which must
  448.  * be expanded separately.  See sxp().
  449.  *
  450.  * Currently allows at most 256 alternatives per set.  Enough? :-)
  451.  */
  452. static
  453. sglob(str, pat, mat)
  454. char *str, *pat, ***mat;
  455. {
  456.     char *p, *newpat[256], *oldpat[256], buf[MAXPATHLEN], *b = buf;
  457.     int copy = 1, nest = 0, i = 0, ret = 0;
  458.  
  459.     if (!pat)
  460.     return FALSE;
  461.  
  462.     while (*pat) {
  463.     if (copy)
  464.         if (*pat != '{') /*}*/ {
  465.         if (*pat == '\\' && pat[1])
  466.             *b++ = *pat++;
  467.         *b++ = *pat++;
  468.         continue;
  469.         } else {
  470.         copy = 0;
  471.         pat++;
  472.         }
  473.     p = pat;
  474.     while (*pat && (nest || *pat != ',' && /*{*/ *pat != '}')) {
  475.         if (*pat == '\\')
  476.         pat++;
  477.         else if (*pat == '{')
  478.         nest++;
  479.         else if (*pat == '}')
  480.         nest--;
  481.         if (*pat)
  482.         pat++;
  483.     }
  484.     if (*pat) {
  485.         oldpat[i] = pat;
  486.         newpat[i++] = p;
  487.         if (*pat != ',') {
  488.         *pat++ = 0;
  489.         break;
  490.         } else
  491.         *pat++ = 0;
  492.     }
  493.     }
  494.     oldpat[i] = NULL;
  495.     if (i > 0 && mat) {
  496.     *mat = (char **)malloc((unsigned)((i + 1) * sizeof(char *)));
  497.     if (*mat)
  498.         (*mat)[i] = NULL;
  499.     else
  500.         return -1;
  501.     ret = i;
  502.     }
  503.     while (!mat && i-- > 0)
  504.     if (ret = glob2(str, newpat[i], pat))
  505.         break;
  506.     for (i = 0; oldpat[i]; i++) {
  507.     if (mat && *mat) {
  508.         (void) sprintf(b, "%s%s", newpat[i], pat);
  509.         (*mat)[i] = savestr(buf);
  510.     }
  511.     if (oldpat[i + 1])
  512.         oldpat[i][0] = ',';
  513.     else
  514.         oldpat[i][0] = /*{*/ '}';
  515.     }
  516.     if (ret == 0 && b > buf && mat) {
  517.     *b = 0;
  518.     ret = ((*mat = unitv(buf)) ? 1 : -1);
  519.     }
  520.     return ret;
  521. }
  522.  
  523. /*
  524.  * Pre-expand pattern set notations so sets containing "/" separators
  525.  * can be globbed successfully.  Returns the number of expansions.
  526.  */
  527. sxp(pat, exp)
  528. char *pat, ***exp;
  529. {
  530.     char **t1 = DUBL_NULL, **t2;
  531.     int n, new, cnt = 0;
  532.  
  533.     if ((n = sglob(NULL, pat, &t1)) < 2) {
  534.     *exp = t1;
  535.     return n;
  536.     }
  537.     *exp = DUBL_NULL;
  538.     while (n-- && cnt >= 0) {
  539.     new = sxp(t1[n], &t2);
  540.     cnt = catv(cnt, exp, new, t2);
  541.     }
  542.     xfree(t1);
  543.     return cnt;
  544. }
  545.  
  546. /*
  547.  * Generate the "glob difference" of two vectors (*argvp and patv).
  548.  * The "glob difference" means to remove all strings from argv that
  549.  * match any of the glob patterns in patv.
  550.  *
  551.  * Returns the number of strings remaining in *argvp.  The strings "removed"
  552.  * from argv are actually left at the end of *argvp, so they can still be
  553.  * accessed; their number will of course be argc - (returned value).
  554.  */
  555. gdiffv(argc, argvp, patc, patv)
  556. int argc, patc;
  557. char ***argvp, **patv;
  558. {
  559.     char **argv, *t;
  560.     int ac, pc, oldac = argc;
  561.  
  562.     if (argc < 1 || patc < 1 || !patv || !*patv)
  563.     return argc;
  564.     if (!argvp || !(argv = *argvp) || !*argv)
  565.     return -1;
  566.     for (ac = 0; ac < argc && argv[ac]; ac++) {
  567.     for (pc = 0; ac < argc && pc < patc && patv[pc]; pc++) {
  568.         /*
  569.          * We shouldn't cross '/' characters, so test
  570.          * only the "tail" of each element of argv.
  571.          */
  572.         if (!(t = rindex(argv[ac], '/')))
  573.             t = argv[ac];
  574.         if (glob(t, patv[pc])) {
  575.         /* Move matches to the end and reduce argc */
  576.         t = argv[ac];
  577.         argv[ac] = argv[--argc];
  578.         argv[argc] = t;
  579.         /* Start patterns over on the new string */
  580.         pc = -1; /* It'll be incremented to 0 */
  581.         }
  582.     }
  583.     }
  584.     /*
  585.      * Sort the two parts of the argv.  uniqcmp() works here only if
  586.      * there already are no duplicates, but we assume that for now.
  587.      */
  588.     if (argc)
  589.     qsort((char *)argv, argc, sizeof(char *), uniqcmp);
  590.     if (oldac > argc)
  591.     qsort((char *)&argv[argc], oldac - argc, sizeof(char *), uniqcmp);
  592.     return argc;
  593. }
  594.  
  595. /*
  596.  * Generate the longest common prefix from all strings in a vector
  597.  * If "skip" is nonzero, that many chars are assumed to be in common
  598.  * and are not tested.  WARNING: skip must be <= than the length of
  599.  * the shortest string in the vector!  Safest to call with skip = 0.
  600.  *
  601.  * Returns the length of the longest common prefix.
  602.  */
  603. lcprefix(vec, skip)
  604. char **vec;
  605. int skip;
  606. {
  607.     char c, **v;
  608.     int done = FALSE;
  609.  
  610.     if (!vec || !*vec || skip < 0)
  611.     return 0;
  612.     do {
  613.     for (v = vec + 1, c = vec[0][skip]; c && *v; v++)
  614.         if (v[0][skip] != c) {
  615.         done = TRUE;
  616.         break;
  617.         }
  618.     } while (!done && c && ++skip);
  619.     return skip;
  620. }
  621.  
  622. #define MAXCOLS 8    /* Max number of columns of words to make */
  623. #define MINWIDTH 10    /* Minimum width of each column of words */
  624. #ifdef CURSES
  625. #define MAXWIDTH (iscurses? COLS : 80)
  626. #else /* CURSES */
  627. #define MAXWIDTH 80    /* Maximum width of all columns */
  628. #endif /* CURSES */
  629.  
  630. /*
  631.  * Print a vector in columns
  632.  *
  633.  * If "skip" is nonzero, that many chars are assumed to be in common
  634.  * and are not printed.  WARNING: skip must be <= than the length of
  635.  * the shortest string in the vector!  Safest to call with skip = 0.
  636.  */
  637. columnate(argc, argv, skip)
  638. int argc;
  639. char **argv;
  640. int skip;
  641. {
  642.     int colstep, colwidth[MAXCOLS + 1];
  643.     int maxcols = min(argc, MAXCOLS);
  644.     int minwidth, maxwidth, *widths;
  645.     int maxword = 0, n, c;
  646.  
  647.     if (argc <= 0 || !argv || !*argv)
  648.     return -1;
  649.     if (!(widths = (int *)malloc((unsigned)((argc + 1) * sizeof(int)))))
  650.     return -1;
  651.  
  652.     /*
  653.      * Compute the widths of all words in the vector, and
  654.      * remember the maximum width and which word had it.
  655.      * Also remember the minimum width.
  656.      */
  657.     for (minwidth = MAXWIDTH, maxwidth = n = 0; n < argc; n++) {
  658.     widths[n] = max(strlen(argv[n] + skip) + 2, MINWIDTH);
  659.     if (widths[n] > MAXWIDTH - MINWIDTH)
  660.         break;
  661.     if (widths[n] > maxwidth) {
  662.         maxwidth = widths[n];
  663.         maxword = n;
  664.     } 
  665.     if (widths[n] < minwidth)
  666.         minwidth = widths[n];
  667.     }
  668.  
  669.     for (; maxcols > 0; maxcols--) {
  670.     if (argc % maxcols)
  671.         colstep = argc / maxcols + 1;
  672.     else
  673.         colstep = argc / maxcols;
  674.     colwidth[MAXCOLS] = 0;
  675.     for (c = 0; c < maxcols; c++) {
  676.         colwidth[c] = 0;
  677.         for (n = c * colstep; n < (c + 1) * colstep && n < argc; n++)
  678.         colwidth[c] = max(colwidth[c], widths[n]);
  679.         colwidth[MAXCOLS] += colwidth[c];
  680.     }
  681.     if (colwidth[MAXCOLS] <= MAXWIDTH)
  682.         break;
  683.     }
  684.     xfree(widths);
  685.  
  686.     if (maxcols < 2 && minwidth <= MAXWIDTH / 2) {
  687.     /*
  688.      * The maxword fills too much screen, so redo everything
  689.      * above it, print maxword, then do everything below it.
  690.      */
  691.     if (maxword > 0 && columnate(maxword, argv, skip) < 0)
  692.         return -1;
  693.     wprint("%s\n", argv[maxword] + skip);
  694.     if (argc - maxword < 2)
  695.         return 0;
  696.     return columnate(argc - maxword - 1, &argv[maxword + 1], skip);
  697.     }
  698.  
  699.     for (n = 0; n < colstep; n++) {
  700.     for (c = 0; c < maxcols && n + c * colstep < argc - colstep; c++)
  701.         wprint("%-*.*s", colwidth[c], colwidth[c],
  702.                         argv[n + c * colstep] + skip);
  703.     wprint("%s\n", argv[n + c * colstep] + skip);
  704.     }
  705.  
  706.     return 0;
  707. }
  708.  
  709. #ifndef DIRECTORY
  710.  
  711. #undef NULL
  712. #define NULL 0
  713.  
  714. /*
  715.  *  4.2BSD directory access emulation for non-4.2 systems.
  716.  *  Based upon routines in appendix D of Portable C and Unix System
  717.  *  Programming by J. E. Lapin (Rabbit Software).
  718.  *
  719.  *  No responsibility is taken for any error in accuracies inherent
  720.  *  either to the comments or the code of this program, but if
  721.  *  reported to me then an attempt will be made to fix them.
  722.  */
  723.  
  724. /*  Support for Berkeley directory reading routines on a V7/SysV file
  725.  *  system.
  726.  */
  727.  
  728. /*  Open a directory. */
  729.  
  730. DIR *
  731. opendir(name)
  732. char *name ;
  733. {
  734.   register DIR *dirp ;
  735.   register int fd ;
  736.  
  737.   if ((fd = open(name, 0)) == -1) return NULL ;
  738.   if ((dirp = (DIR *) malloc(sizeof(DIR))) == NULL)
  739.     {
  740.       close(fd) ;
  741.       return NULL ;
  742.     }
  743.   dirp->dd_fd = fd ;
  744.   dirp->dd_loc = 0 ;
  745.   return dirp ;
  746. }
  747.  
  748.  
  749. /*  Read an old style directory entry and present it as a new one. */
  750.  
  751. #define  ODIRSIZ  14
  752.  
  753. struct olddirent
  754. {
  755.   short  od_ino ;
  756.   char   od_name[ODIRSIZ] ;
  757. } ;
  758.  
  759.  
  760. /*  Get next entry in a directory. */
  761.  
  762. struct dirent *
  763. readdir(dirp)
  764. register DIR *dirp ;
  765. {
  766.   register struct olddirent *dp ;
  767.   static struct dirent dir ;
  768.  
  769.   for (;;)
  770.     {
  771.       if (dirp->dd_loc == 0)
  772.         {
  773.           dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, DIRBLKSIZ) ;
  774.           if (dirp->dd_size <= 0) return NULL ;
  775.         }
  776.       if (dirp->dd_loc >= dirp->dd_size)
  777.         {
  778.           dirp->dd_loc = 0 ;
  779.           continue ;
  780.         }
  781.  
  782.       dp = (struct olddirent *)(dirp->dd_buf + dirp->dd_loc) ;
  783.       dirp->dd_loc += sizeof(struct olddirent) ;
  784.  
  785.       if (dp->od_ino == 0) continue ;
  786.  
  787.       dir.d_fileno = dp->od_ino ;
  788.       strncpy(dir.d_name, dp->od_name, ODIRSIZ) ;
  789.       dir.d_name[ODIRSIZ] = '\0' ;       /* Ensure termination. */
  790.       dir.d_namlen = strlen(dir.d_name) ;
  791.       dir.d_reclen = DIRSIZ(&dir) ;
  792.       return(&dir) ;
  793.     }
  794. }
  795.  
  796.  
  797. /*  Close a directory. */
  798.  
  799. void
  800. closedir(dirp)
  801. register DIR *dirp ;
  802. {
  803.   close(dirp->dd_fd) ;
  804.   dirp->dd_fd = -1 ;
  805.   dirp->dd_loc = 0 ;
  806.   xfree(dirp) ;
  807.  
  808. #endif /* DIRECTORY */
  809.