home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / unix / volume06 / glob < prev    next >
Encoding:
Text File  |  1988-09-11  |  14.8 KB  |  707 lines

  1. Subject: v06i047:  'Globbing' library routine (glob)
  2. Newsgroups: mod.sources
  3. Approved: rs@mirror.UUCP
  4.  
  5. Submitted by: seismo!mcvax!guido (Guido van Rossum)
  6. Mod.sources: Volume 6, Issue 47
  7. Archive-name: glob
  8.  
  9. [  A few items: 1) If you use csh, you have to invoke the test program
  10.    at "./glob"; 2) A comment in the code says [...] is not implemented;
  11.    it is; 3) As the README says, some sites will have to add -Dindex=strchr
  12.    to the CFLAGS variable in the Makefile; 4) This uses the opendir()
  13.    family of routines.  --r$ ]
  14.  
  15. : This is a shell archive.
  16. : Extract with 'sh this_file'.
  17. echo 'Start of glob -- expand meta-characters, part 01 out of 01:'
  18. echo 'x - Makefile'
  19. sed 's/^X//' > 'Makefile' << 'EOF'
  20. XCFLAGS=
  21. XLINTFLAGS=-abh
  22. XSRCS=    glob.c main.c
  23. XOBJS=    glob.o main.o
  24. XFILES=    README $(SRCS) Makefile glob.3
  25. X
  26. Xglob:    glob.o main.o
  27. X    $(CC) -o glob $(CFLAGS) glob.o main.o
  28. X
  29. Xtags:    $(SRCS)
  30. X    ctags $(SRCS)
  31. X
  32. Xlint:    $(SRCS)
  33. X    lint $(LINTFLAGS) $(SRCS)
  34. X
  35. Xshar:    $(FILES)
  36. X    packmail -o shar -t 'glob -- expand meta-characters' $(FILES)
  37. EOF
  38. echo 'x - README'
  39. sed 's/^X//' > 'README' << 'EOF'
  40. XGlob -- expand meta-characters in file names -- by Guido van Rossum
  41. X
  42. XThis is a filename 'globbing' routine that I wrote.  Besides expanding
  43. X*, ? and [...] exactly like sh(1), it also replaces initial ~ or ~user
  44. Xby user's home directory, replaces initial $var by the contents of the
  45. Xenvironment variable, and has a quoting mechanism whereby any of the
  46. Xmagic characters can be quoted by '\' (including '\' itself).
  47. X
  48. XIt is really intended for inclusion in some larger program, but there is
  49. Xa small test program that expands patterns given as arguments.
  50. X
  51. XYou should have received the following files:
  52. X
  53. X    README        this file
  54. X    glob.c        the code
  55. X    main.c        test program
  56. X    glob.3        manual page
  57. X    Makefile    make file
  58. X
  59. XPortability issues: I have only been able to test it on a VAX running
  60. X4.3-beta, but I believe it can be ported to anything that supports
  61. Xopendir, readdir and closedir.  If you don't have index, substitute
  62. Xstrchr using the -D flag to the C compiler.  The program passes lint
  63. Xexcept for the usual complaints about malloc and strcpy.
  64. X
  65. XThis software is copyright (c) 1986 by Stichting Mathematisch Centrum.
  66. XYou may use, modify and copy it, provided this notice is present in all
  67. Xmodified or unmodified copies, and provided you don't make money for it.
  68. X
  69. X*** You may only distribute the set of files mentioned above as a whole. ***
  70. X
  71. XIf you find bugs, or have any other questions, please mail them to the
  72. Xauthor, whose address is:
  73. X
  74. X    Guido van Rossum
  75. X    CWI, dept. AA
  76. X    Kruislaan 413
  77. X    1089 SJ  Amsterdam
  78. X
  79. X    Electronic mail: guido@mcvax.UUCP
  80. EOF
  81. echo 'x - glob.3'
  82. sed 's/^X//' > 'glob.3' << 'EOF'
  83. X.TH GLOB 3 "June 28, 1986"
  84. X.AT 3
  85. X.SH NAME
  86. Xglob \- expand meta-characters in file names
  87. X.SH SYNOPSIS
  88. X.nf
  89. X.B "int glob(pattern, buffer, size)
  90. X.B "char *pattern;
  91. X.B "char *buffer;
  92. X.B "unsigned int size;
  93. X.fi
  94. X.SH DESCRIPTION
  95. X.I Glob
  96. Xexpands the meta-characters *, ? and [...] in
  97. X.I pattern
  98. Xand copies the file name(s) that match the pattern to 
  99. X.I buffer ,
  100. Xwhose length is given by
  101. X.I size .
  102. X.PP
  103. XBefore expansion is attempted, the pattern is processed as follows:
  104. Xan initial
  105. X.I ~user
  106. Xis replaced by
  107. X.I user's
  108. Xhome directory, an initial ~ is replaced by the contents of $HOME,
  109. Xand an initial
  110. X.I $variable
  111. Xis replaced by the contents of the environment variable by that name.
  112. XThe universal quoting character \e can be prefixed to *, ? and [, to
  113. Xinitial ~ or $, and to itself to prevent the special meaning.
  114. X.PP
  115. XThe expansion algorithm used is believed to give exactly the same
  116. Xresults as the expansion of meta-characters by the Bourne shell
  117. X.I sh (1),
  118. Xexcept for the different quoting mechanism and the expansion of ~ and $.
  119. XThe files are sorted.
  120. X.SH RETURN VALUE
  121. X.I Glob
  122. Xreturns the number of files matching the pattern that could be
  123. Xtransferred to the buffer.
  124. XIf no files match, the return value is 0 but the pattern is copied to
  125. Xthe buffer after expansion of ~, $ and \e-quotes.
  126. X.SH SEE ALSO
  127. Xsh(1)
  128. X.SH AUTHOR
  129. XGuido van Rossum, CWI, Amsterdam <guido@mcvax.uucp>
  130. X.SH DIAGNOSTICS
  131. XThere are no explicit diagnostics.
  132. XIf there is not enough memory to hold the internal lists of
  133. Xpartially-matching files, not all matching files may be found.
  134. XIf the buffer has not enough space to hold all matching files, as many
  135. Xas will fit are transferred to it.
  136. XIf
  137. X.I $variable
  138. Xhas no value, the empty string is substituted.
  139. XIf no expansion can be found for ~ or
  140. X.I ~user ,
  141. Xit is left in the pattern unchanged.
  142. XIf 
  143. X.SH BUGS
  144. XThere should be an interface to the ``raw'' globbing algorithm, which
  145. Xcan return an arbitrary number of files in a ``malloced'' structure and
  146. Xdoes not do ~ or $ expansion or \e-quoting.
  147. X.PP
  148. XThe code uses the Berkeley directory-reading package, so in order to
  149. Xport it to v7 or sys5 you'll need to get hold of a public domain
  150. Xre-implementation of that package.
  151. X.PP
  152. XThe name is a relict from ancient times.
  153. EOF
  154. echo 'x - glob.c'
  155. sed 's/^X//' > 'glob.c' << 'EOF'
  156. X/* This software is copyright (c) 1986 by Stichting Mathematisch Centrum.
  157. X * You may use, modify and copy it, provided this notice is present in all
  158. X * copies, modified or unmodified, and provided you don't make money for it.
  159. X *
  160. X * Written 86-jun-28 by Guido van Rossum, CWI, Amsterdam <guido@mcvax.uucp>
  161. X */
  162. X
  163. X/*
  164. X * 'Glob' routine -- match * and ? in filename.
  165. X * Extra services: initial ~username is replaced by username's home dir,
  166. X * initial ~ is replaced by $HOME, initial $var is replaced by
  167. X * return value of getenv("var").
  168. X * Quoting convention: \ followed by a magic character inhibits its
  169. X * special meaning.
  170. X * BUGS: [...] is not implemented.
  171. X * Nonexisting $var is treated as empty string; nonexisting ~user
  172. X * is copied literally.
  173. X */
  174. X
  175. X#include <stdio.h>
  176. X#include <strings.h>
  177. X#include <ctype.h>
  178. X#include <pwd.h>
  179. X#include <sys/types.h>
  180. X#include <sys/dir.h>
  181. X
  182. Xstruct passwd *getpwnam();
  183. Xchar *getenv();
  184. Xchar *malloc();
  185. Xchar *realloc();
  186. X
  187. X#define EOS '\0'
  188. X#define SEP '/'
  189. X#define DOT '.'
  190. X#define QUOTE '\\'
  191. X
  192. X#define BOOL int
  193. X#define NO 0
  194. X#define YES 1
  195. X
  196. X#define MAXPATH 1024
  197. X#define MAXBASE 256
  198. X
  199. X#define META(c) ((char)((c)|0200))
  200. X#define M_ALL META('*')
  201. X#define M_ONE META('?')
  202. X#define M_SET META('[')
  203. X#define M_RNG META('-')
  204. X#define M_END META(']')
  205. X
  206. Xstruct flist {
  207. X    int len;
  208. X    char **item;
  209. X};
  210. X
  211. X/* Make a null-terminated string of the chars between two pointers */
  212. X/* (Limited length, static buffer returned) */
  213. X
  214. Xstatic char *
  215. Xmakestr(start, end)
  216. X    char *start;
  217. X    char *end;
  218. X{
  219. X    static char buf[100];
  220. X    char *p= buf;
  221. X    
  222. X    while (start < end && p < &buf[100])
  223. X        *p++ = *start++;
  224. X    *p= EOS;
  225. X    return buf;
  226. X}
  227. X
  228. X/* Append string to buffer, return new end of buffer.  Guarded. */
  229. X
  230. Xstatic char *
  231. Xaddstr(dest, src, end)
  232. X    char *dest;
  233. X    char *src;
  234. X    char *end;
  235. X{
  236. X    while (*dest++ = *src++) {
  237. X        if (dest >= end)
  238. X            break;
  239. X    }
  240. X    return --dest;
  241. X}
  242. X
  243. X/* Form a pathname by concatenating head, maybe a / and tail. */
  244. X/* Truncates to space available. */
  245. X
  246. Xstatic void
  247. Xformpath(dest, head, tail, size)
  248. X    char *dest;
  249. X    char *head;
  250. X    char *tail;
  251. X    unsigned int size; /* sizeof dest */
  252. X{
  253. X    char *start= dest;
  254. X    
  255. X    for (;;) {
  256. X        if (--size == 0)
  257. X            goto out;
  258. X        if ((*dest++ = *head++) == EOS)
  259. X            break;
  260. X    }
  261. X    if (*tail != EOS && size != 0) {
  262. X        --dest;
  263. X        ++size;
  264. X        if (dest > start && dest[-1] != SEP) {
  265. X            *dest++ = SEP;
  266. X            --size;
  267. X        }
  268. X        for (;;) {
  269. X            if (--size == 0)
  270. X                goto out;
  271. X            if ((*dest++ = *tail++) == EOS)
  272. X                break;
  273. X        }
  274. X    }
  275. X    return;
  276. X    
  277. X out:    *dest= EOS;
  278. X}
  279. X
  280. X/* Find a user's home directory, NULL if not found */
  281. X
  282. Xstatic char *
  283. Xgethome(username)
  284. X    char *username;
  285. X{
  286. X    struct passwd *p;
  287. X    
  288. X    p= getpwnam(username);
  289. X    if (p == NULL)
  290. X        return NULL;
  291. X    return p->pw_dir;
  292. X}
  293. X
  294. X/* String compare for qsort */
  295. X
  296. Xstatic int
  297. Xcompare(p, q)
  298. X    char **p;
  299. X    char **q;
  300. X{
  301. X    return strcmp(*p, *q);
  302. X}
  303. X
  304. X/*
  305. X * Maintain lists of strings.
  306. X * When memory is full, data is lost but 
  307. X */
  308. X
  309. Xstatic void
  310. Xaddfile(list, head, tail)
  311. X    struct flist *list;
  312. X    char *head;
  313. X    char *tail;
  314. X{
  315. X    char *str;
  316. X    
  317. X    str= malloc((unsigned) strlen(head) + strlen(tail) + 2);
  318. X    
  319. X    if (str == NULL)
  320. X        return;
  321. X    if (list->item == 0) {
  322. X        list->len= 0;
  323. X        list->item= (char**) malloc(sizeof(char*));
  324. X    }
  325. X    else {
  326. X        list->item= (char**) realloc((char*)list->item,
  327. X                (unsigned) (list->len+1)*sizeof(char*));
  328. X        if (list->item == 0)
  329. X            list->len= 0;
  330. X    }
  331. X    if (list->item != NULL) {
  332. X        formpath(str, head, tail, MAXPATH);
  333. X        list->item[list->len++]= str;
  334. X    }
  335. X    else
  336. X        free(str);
  337. X}
  338. X
  339. X/* Clear the fields of a struct flist before first use */
  340. X
  341. Xstatic void
  342. Xclear(list)
  343. X    struct flist *list;
  344. X{
  345. X    list->len= 0;
  346. X    list->item= NULL;
  347. X}
  348. X
  349. X/* Free memory held by struct flist after use */
  350. X
  351. Xstatic void
  352. Xdiscard(list)
  353. X    struct flist *list;
  354. X{
  355. X    int i= list->len;
  356. X    
  357. X    if (list->item != NULL) {
  358. X        while (--i >= 0) {
  359. X            if (list->item[i] != NULL)
  360. X                free(list->item[i]);
  361. X        }
  362. X        free((char*)list->item);
  363. X        list->item= NULL;
  364. X    }
  365. X    list->len= 0;
  366. X}
  367. X
  368. X/* Pattern matching function for filenames */
  369. X/* Each occurrence of the * pattern causes a recursion level */
  370. X
  371. Xstatic BOOL
  372. Xmatch(name, pat)
  373. X    char *name;
  374. X    char *pat;
  375. X{
  376. X    char c, k;
  377. X    BOOL ok;
  378. X    
  379. X    while ((c= *pat++) != EOS) {
  380. X        switch (c) {
  381. X
  382. X        case M_ONE:
  383. X            if (*name++ == EOS)
  384. X                return NO;
  385. X            break;
  386. X
  387. X        case M_ALL:
  388. X            if (*pat == EOS)
  389. X                return YES;
  390. X            for (; *name != EOS; ++name) {
  391. X                if (match(name, pat))
  392. X                    return YES;
  393. X            }
  394. X            return NO;
  395. X
  396. X        case M_SET:
  397. X            ok= NO;
  398. X            k= *name++;
  399. X            while ((c= *pat++) != M_END) {
  400. X                if (*pat == M_RNG) {
  401. X                    if (c <= k && k <= pat[1])
  402. X                        ok= YES;
  403. X                    pat += 2;
  404. X                }
  405. X                else if (c == k)
  406. X                    ok= YES;
  407. X            }
  408. X            if (!ok)
  409. X                return NO;
  410. X            break;
  411. X
  412. X        default:
  413. X            if (*name++ != c)
  414. X                return NO;
  415. X            break;
  416. X
  417. X        }
  418. X    }
  419. X    return *name == EOS;
  420. X}
  421. X
  422. X/* Perform pattern matching for one segment of the pathname */
  423. X
  424. Xstatic void
  425. Xsegment(files, mid, pat)
  426. X    struct flist *files;
  427. X    char *mid;
  428. X    char *pat;
  429. X{
  430. X    char path[MAXPATH];
  431. X    int i;
  432. X    DIR *dirp;
  433. X    struct direct *dp;
  434. X    struct flist new;
  435. X    
  436. X    clear(&new);
  437. X    for (i= 0; i < files->len; ++i) {
  438. X        strcpy(path, files->item[i]);
  439. X        strcat(path, mid);
  440. X        free(files->item[i]);
  441. X        files->item[i]= NULL;
  442. X        dirp= opendir(path);
  443. X        if (dirp == NULL)
  444. X            continue;
  445. X        while ((dp= readdir(dirp)) != NULL) {
  446. X            if (*dp->d_name == DOT && *pat != DOT)
  447. X                ; /* No meta-match on initial '.' */
  448. X            else if (match(dp->d_name, pat))
  449. X                addfile(&new, path, dp->d_name);
  450. X        }
  451. X        closedir(dirp);
  452. X    }
  453. X    discard(files);
  454. X    *files= new;
  455. X}
  456. X
  457. X/* Finish by matching the rest of the pattern (which has no metas) */
  458. X
  459. Xstatic void
  460. Xfindfiles(files, tail)
  461. X    struct flist *files;
  462. X    char *tail;
  463. X{
  464. X    int i;
  465. X    struct flist new;
  466. X    char path[MAXPATH];
  467. X
  468. X    if (*tail == EOS || files->len == 0)
  469. X        return;
  470. X    clear(&new);
  471. X    for (i= 0; i < files->len; ++i) {
  472. X        strcpy(path, files->item[i]);
  473. X        strcat(path, tail);
  474. X        free(files->item[i]);
  475. X        files->item[i]= NULL;
  476. X        if (access(path, 0) == 0)
  477. X            addfile(&new, path, "");
  478. X    }
  479. X    discard(files);
  480. X    *files= new;
  481. X}
  482. X
  483. Xstatic void
  484. Xglob1(pat, files)
  485. X    char *pat;
  486. X    struct flist *files;
  487. X{
  488. X    char mid[MAXPATH];
  489. X    char *end= mid;
  490. X    char *basestart= mid;
  491. X    BOOL meta= NO;
  492. X    char c;
  493. X    
  494. X    clear(files);
  495. X    addfile(files, "", "");
  496. X    for (;;) {
  497. X        switch (c= *pat++) {
  498. X
  499. X        case EOS:
  500. X        case SEP:
  501. X            *end= EOS;
  502. X            if (meta) {
  503. X                if (basestart == mid)
  504. X                    segment(files, "", basestart);
  505. X                else if (basestart == mid+1) {
  506. X                    static char sepstr[]= {SEP, EOS};
  507. X                    segment(files, sepstr, basestart);
  508. X                }
  509. X                else {
  510. X                    basestart[-1]= EOS;
  511. X                    segment(files, mid, basestart);
  512. X                }
  513. X                if (files->len == 0)
  514. X                    return;
  515. X                end= basestart= mid;
  516. X                meta= NO;
  517. X            }
  518. X            else if (c == EOS)
  519. X                findfiles(files, mid);
  520. X            if (c == EOS)
  521. X                return;
  522. X            *end++= c;
  523. X            basestart= end;
  524. X            break;
  525. X
  526. X        case M_ALL:
  527. X        case M_ONE:
  528. X        case M_SET:
  529. X            meta= YES;
  530. X            /* Fall through */
  531. X        default:
  532. X            *end++ = c;
  533. X
  534. X        }
  535. X    }
  536. X}
  537. X
  538. X/*
  539. X * The main 'glob' routine: does $ and ~ substitution and \ quoting,
  540. X * and then calls 'glob1' to do the pattern matching.
  541. X * Returns 0 if file not found, number of matches otherwise.
  542. X * The matches found are appended to the buffer, separated by
  543. X * EOS characters.  If no matches were found, the pattern is copied
  544. X * to the buffer after $ and ~ substitution and \ quoting.
  545. X */
  546. X
  547. Xint
  548. Xglob(pat, buf, size)
  549. X    char *pat;
  550. X    char *buf;
  551. X    unsigned int size; /* sizeof buf */
  552. X{
  553. X    char *p, *q;
  554. X    char c;
  555. X    struct flist files;
  556. X    char *start= buf;
  557. X    char *end= buf+size;
  558. X    
  559. X    c= *pat;
  560. X    if (c == '~') {
  561. X        p= ++pat;
  562. X        while (*p != EOS && *p != SEP)
  563. X            ++p;
  564. X        if (p == pat) {
  565. X            q= getenv("HOME");
  566. X            if (q == NULL)
  567. X                --pat;
  568. X            else
  569. X                buf= addstr(buf, q, end);
  570. X        }
  571. X        else {
  572. X            q= gethome(makestr(pat, p));
  573. X            if (q == NULL)
  574. X                --pat;
  575. X            else {
  576. X                buf= addstr(buf, q, end);
  577. X                pat= p;
  578. X            }
  579. X        }
  580. X    }
  581. X    else if (c == '$') {
  582. X        p= ++pat;
  583. X        while (isalnum(*p) || *p == '_')
  584. X            ++p;
  585. X        q= getenv(makestr(pat, p));
  586. X        if (q != NULL)
  587. X            buf= addstr(buf, q, end);
  588. X        pat= p;
  589. X    }
  590. X    else if (c == QUOTE && (pat[1] == '$' || pat[1] == '~'))
  591. X        ++pat;
  592. X    
  593. X    while (buf < end && (c= *pat++) != EOS) {
  594. X        switch (c) {
  595. X        
  596. X        case QUOTE:
  597. X            if ((c= *pat++) != EOS && index("\\*?[", c) != NULL)
  598. X                *buf++ = c;
  599. X            else {
  600. X                *buf++ = QUOTE;
  601. X                --pat;
  602. X            }
  603. X            break;
  604. X        
  605. X        case '*':
  606. X            *buf++ = M_ALL;
  607. X            break;
  608. X        
  609. X        case '?':
  610. X            *buf++ = M_ONE;
  611. X            break;
  612. X        
  613. X        case '[':
  614. X            if (*pat == EOS || index(pat+1, ']') == NULL) {
  615. X                *buf++ = c;
  616. X                break;
  617. X            }
  618. X            *buf++ = M_SET;
  619. X            c= *pat++;
  620. X            do {
  621. X                *buf++ = c;
  622. X                if (*pat == '-' && (c= pat[1]) != ']') {
  623. X                    *buf++ = M_RNG;
  624. X                    *buf++= c;
  625. X                    pat += 2;
  626. X                }
  627. X            } while ((c= *pat++) != ']');
  628. X            *buf++ = M_END;
  629. X            break;
  630. X        
  631. X        default:
  632. X            *buf++ = c;
  633. X            break;
  634. X
  635. X        }
  636. X    }
  637. X    *buf= EOS;
  638. X    
  639. X    glob1(start, &files);
  640. X    
  641. X    if (files.len == 0) {
  642. X        /* Change meta characters back to printing characters */
  643. X        for (buf= start; *buf != EOS; ++buf) {
  644. X            if (*buf & 0200)
  645. X                *buf &= ~0200;
  646. X        }
  647. X        return 0; /* No match */
  648. X    }
  649. X    else {
  650. X        int i, len;
  651. X        
  652. X        qsort((char*)files.item, files.len, sizeof(char*), compare);
  653. X        buf= start;
  654. X        *buf= EOS;
  655. X        for (i= 0; i < files.len; ++i) {
  656. X            len= strlen(files.item[i]);
  657. X            if (len+1 > size)
  658. X                break;
  659. X            strcpy(buf, files.item[i]);
  660. X            buf += len+1;
  661. X            size -= len+1;
  662. X        }
  663. X        discard(&files);
  664. X        return i;
  665. X    }
  666. X}
  667. EOF
  668. echo 'x - main.c'
  669. sed 's/^X//' > 'main.c' << 'EOF'
  670. X/*
  671. X * Main program to test 'glob' routine.
  672. X */
  673. X
  674. X#include <stdio.h>
  675. X
  676. Xextern int glob();
  677. X
  678. Xmain(argc, argv)
  679. X    int argc;
  680. X    char **argv;
  681. X{
  682. X    int i, j, n;
  683. X    char *p;
  684. X    char buffer[10000];
  685. X
  686. X    if (argc < 2) {
  687. X        fprintf(stderr, "usage: %s pattern ...\n", argv[0]);
  688. X        exit(2);
  689. X    }
  690. X    for (i= 1; i < argc; ++i) {
  691. X        printf("%s: ", argv[i]);
  692. X        n= glob(argv[i], buffer, sizeof buffer);
  693. X        printf("%d\n", n);
  694. X        p= buffer;
  695. X        if (n == 0)
  696. X            ++n;
  697. X        for (j= 0; j < n; ++j) {
  698. X            printf("\t%s\n", p);
  699. X            p += strlen(p) + 1;
  700. X        }
  701. X    }
  702. X    exit(0);
  703. X}
  704. EOF
  705. echo 'Part 01 out of 01 of glob -- expand meta-characters complete.'
  706. exit 0
  707.