home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / me34src.zip / me3 / util / fxpand.c < prev    next >
C/C++ Source or Header  |  1995-01-14  |  15KB  |  532 lines

  1. /* fxpand.c : fxpand(): expand file names
  2.  * Input:
  3.  *   name:  A file name which can contain: ?, \, [], [^] and *
  4.  *     If name ends with "." (eg *.) then only names with no extensions will
  5.  *       be matched.
  6.  *     If name begins with ~ then:  "~/" expands to "$(HOME)/" and "~name"
  7.  *     expands to "<home directory of name>".
  8.  *     MS-DOS:  \ is same as /.  Does not quote.
  9.  *     UNIX: \ quotes the next character.
  10.  *     ATARI (from jwahar r.  bammi (bammi@cadence.com)):
  11.  *       Like MS-DOS, \ == /, except that we have the POSIX opendir() etc.
  12.  *   onlyone: If TRUE return the first match otherwise all matches are
  13.  *     returned.  For example, this controls how "*" is expanded.
  14.  *   nopath:  If TRUE, only return the matching file names.  Otherwise
  15.  *     return the path and name.  For example, "/foo/bar" => "bar".
  16.  *   slash_dir:  If TRUE, append a slash to the end of a file name if the
  17.  *     file is a directory.  For example, input of "dir" would generate
  18.  *     "dir/".
  19.  *   name_heap:
  20.  *     NULL: Call process_fname.
  21.  *     Pointer to char[]:  Stuff the file names in here.  The names are
  22.  *    separated by a blank and there are no trailing blanks.
  23.  *   process_fname:  A function which is passed a char *.  The string
  24.  *       contains the expanded file name.
  25.  *     Returns:  0 if all OK, >1 an error to be returned by fxpand().
  26.  * Output:
  27.  *   0:  All OK
  28.  *   1:  Something screwed up.  Most likely the name is bad.
  29.  *   n:  An error code returned by process_fname().
  30.  * 
  31.  * Notes:
  32.  *   When compiled on Apollo, this routine also works with the Domain/OS
  33.  *     "//" notation.  This is mostly luck - I don't collapse "/"s and a
  34.  *     relaxed check lets this work.
  35.  *   Input error checking is pretty grim.
  36.  * Unix Notes:
  37.  *   When wildcard matching, hidden files (those that start with ".")  are
  38.  *     skipped unless you ask to see them.  To match ".fred", you could use
  39.  *     ".f*".  To match "fred/.sam/geo", you would need something like
  40.  *     "fred/.s* /g*".
  41.  *   When appending slashes (slash_dir), expanding something like "*" can be
  42.  *     very slow.  This is because I have to stat() to find out if the file
  43.  *     is a directory and stat() can take a long time to stat files over nfs
  44.  *     mounts, follow links, etc.
  45.  * 
  46.  * Craig Durland
  47.  */
  48.  
  49. /* Copyright 1989, 1990, 1991 Craig Durland
  50.  *   Distributed under the terms of the GNU General Public License.
  51.  *   Distributed "as is", without warranties of any kind, but comments,
  52.  *     suggestions and bug reports are welcome.
  53.  */
  54.  
  55. #ifdef __STDC__
  56.  
  57. #ifdef __hpux        /* for ANSI C on HP-UX */
  58. #define _HPUX_SOURCE
  59. #endif  /* __hpux */
  60.  
  61. #ifdef __apollo            /* for ANSI C on Apollo BSD */
  62. #define _BSD_SOURCE
  63. #endif    /* __apollo */
  64.  
  65. #ifdef _AIX            /* for ANSI C on IBM AIX */
  66. #define _POSIX_SOURCE
  67. #endif  /* _AIX */
  68.  
  69. #endif    /*  __STDC__ */
  70.  
  71. #include <stdio.h>
  72. #include "os.h"
  73. #include "const.h"
  74.  
  75. extern char *strchr(), *getenv(), *strcpy(), *strcat();
  76.  
  77. static char *name_list;
  78. static int prepend_blank;
  79. static int stuff_name(name) char *name;
  80. {
  81.   if (prepend_blank) strcat(name_list," ");
  82.   else prepend_blank = TRUE;
  83.  
  84.   strcat(name_list,name);
  85.   return 0;
  86. }
  87.  
  88. #if MSDOZ
  89.  
  90. #define SLASH    "/"        /* "/" or "\\", your preference */
  91.  
  92.     /* A note about MS-DOS:  Its file find routines are brain dead:  If you
  93.      *   ask for a directory, you will get all file types.  Also, since
  94.      *   there is a file type==0, you can't just use AND to filter out
  95.      *   unwanted types.  What a pain in the butt!
  96.      */
  97.  
  98. #include <dos.h>
  99.  
  100. #ifdef LATTICE        /* from dos.h */
  101.  
  102. typedef struct FILEINFO File_Info;    /* in dos.h */
  103.  
  104. #define FIND_DIRECTORY(info, name, attr)    dfind(info, name, attr)
  105. #define NEXT_DIRECTORY(info)            dnext(info)
  106.  
  107. #define ATTR        attr
  108. #define NAME        name
  109.  
  110. #else        /* JPI Topspeed C, Turbo C, hopefully others */
  111.  
  112. #include <dir.h>
  113.  
  114. typedef struct ffblk File_Info;        /* in dos.h */
  115.  
  116. #define FIND_DIRECTORY(info, name, attr)    findfirst(name, info, attr)
  117. #define NEXT_DIRECTORY(info)            findnext(info)
  118.  
  119. #define ATTR        ff_attrib
  120. #define NAME        ff_name
  121.  
  122. #endif /* LATTICE */
  123.  
  124.  
  125. #define FATTR (FA_ARCHIVE | FA_RD_ONLY | FA_DIRECTORY)    /* file attributes */
  126.  
  127.  
  128. fxpand(name, onlyone,nopath,slash_dir, name_heap,process_fname)
  129.   char *name,*name_heap; pfi process_fname;
  130. {
  131.   char path[128], word[100], *ptr, *qtr, tbuf[128];
  132.   File_Info de;
  133.   int atend, eval, found, needadot, path_len, s;
  134.  
  135.   if (name_heap)    /* store the found file names in name_heap */
  136.   {
  137.     process_fname = stuff_name;
  138.     *(name_list = name_heap) = '\0';
  139.     prepend_blank = FALSE;
  140.   }
  141.  
  142.   *path = '\0';
  143.   if (*name == '~')
  144.   {
  145.     name++;
  146.     if (ptr = getenv("HOME")) strcpy(path,ptr);
  147.   }
  148.   else
  149.     if (name[1] == ':')
  150.     { strncpy(path,name,2); path[2] = '\0'; name += 2; }
  151.  
  152.  
  153.   atend = FALSE; needadot = FALSE;
  154.   while (!atend)
  155.   {
  156.     atend = get_dpart(&name,word,&eval);
  157.     path_len = strlen(path);
  158. uppercase(word);    /* Since the directory entries are uppercase */
  159.     if (eval)            /* wildcards in need of expansion */
  160.     {
  161. if (word[strlen(word)-1] == '.') needadot = TRUE;
  162.       found = FALSE;
  163.       ptr = path +path_len; strcpy(ptr,"*.*");
  164.  
  165.       if (FIND_DIRECTORY(&de,path,FATTR)) return 1;    /* No files found */
  166.       *ptr = '\0';        /* get rid of "*.*" */
  167.       do        /* look at all entries in this directory */
  168.       {
  169.     if (!atend)            /* only care about directories */
  170.       { if (de.ATTR != FA_DIRECTORY) continue; }
  171.         else
  172.       if (de.ATTR &&    /* if de.ATTR==0, its a regular old file */
  173.           (de.ATTR & FATTR) == 0) continue;
  174.  
  175.     ptr = qtr = de.NAME;
  176. if (needadot && !strchr(ptr,'.')) ptr = strcat(strcpy(tbuf,ptr),".");
  177.     if (*qtr != '.' && wildmat(ptr,word))    /* ignore . & .. */
  178.     {
  179.       found = TRUE;
  180.       if (!atend)        /* something like foo/<you are here>/bar */
  181.       {
  182.         strcat(strcat(path,qtr), SLASH);
  183.         break;
  184.       }
  185.       else            /* something like foo/<you are here> */
  186.       {
  187.         strcpy(path +path_len, qtr);
  188.         s = procz(process_fname, slash_dir && (de.ATTR == FA_DIRECTORY),
  189.             path, nopath ? path_len : 0);
  190.         path[path_len] = '\0';
  191.         if (s) return s;
  192.         if (onlyone) break;
  193.       }
  194.     }
  195.       } while (!NEXT_DIRECTORY(&de));
  196.       if (!found) return 1;
  197.     }
  198.     else
  199.     {
  200.       strcpy(path + path_len, word);
  201.       if (atend)    /* all done */
  202.     return procz(process_fname, slash_dir && is_dir(path),
  203.             path, nopath ? path_len : 0);
  204. /* ??? if (!is_dir(path)) return 1; /* Make sure path is real */
  205.     } 
  206.   }    /* end while */
  207.   return 0;
  208. }
  209.  
  210. static int procz(process_fname, slash_it, path, path_len)
  211.   pfi process_fname; char *path;
  212. {
  213.   if (slash_it && (path[path_len] != '\0')) strcat(path, "/");
  214.   return (*process_fname)(path + path_len);
  215. }
  216.  
  217.     /* 
  218.      * Notes:
  219.      *   For some (unknown to me) reason, FA_DIRECTORY matches everything,
  220.      *     not just directories.
  221.      * WARNING:
  222.      *   This routine changes the DTA.  If you are in the middle of a
  223.      *     FIND_DIRECTORY/NEXT_DIRECTORY loop, this will mess that up.
  224.      */
  225. static int is_dir(path) char *path;
  226. {
  227.   File_Info de;
  228.  
  229.   return (!FIND_DIRECTORY(&de,path,FA_DIRECTORY) && (de.ATTR == FA_DIRECTORY));
  230. }
  231.  
  232.  
  233.  
  234.     /* MS-DOS stuff for get_dpart() */
  235. #define ASLASH        case '/': case '\\'
  236. #define GOTTA_EVAL    case '?': case '[': case '*'
  237.  
  238. #endif    /* MSDOZ */
  239.  
  240.  
  241. #if ATARI    /* Atari has Posix opendir(), etc */
  242. #undef  POSIX_OS
  243. #define POSIX_OS 1    /* turn on POSIX and UX_OS */
  244. #endif    /* ATARI */
  245.  
  246.  
  247.  
  248.  
  249.  
  250. #if UX_OS    /* and Atari */
  251.  
  252. #define BADGETPW 1    /* 1 if system getpw... routines are screwed up */
  253.  
  254. #include <sys/types.h>
  255. #include <pwd.h>
  256. #include <sys/stat.h>
  257.  
  258.     /* Posix, SysV: HP-UX, Apollo SysV, DEC, Atari  */
  259.     /* defined(POSIX) is a DECism */
  260. #if POSIX_OS || SYSV_OS || defined(POSIX)
  261. #include <dirent.h>
  262. #else        /* Pure BSD: Apollo bsd4.3 */
  263. #include <sys/dir.h>
  264. #endif
  265.  
  266.  
  267. static int get_dpart(), getpwhome(), procz(), is_dir();
  268.  
  269.  
  270.     /* cases to check:
  271.      *   "~", "~fred", "/", "~/", ""
  272.      */
  273. fxpand(name, onlyone,nopath,slash_dir, name_heap,process_fname)
  274.   char *name,*name_heap; pfi process_fname;
  275. {
  276.   char path[512], word[256], *ptr, *qtr, tbuf[256];
  277.   DIR *dir;
  278. #if POSIX_OS || SYSV_OS || defined(POSIX)
  279.   struct dirent *dtr;
  280. #else        /* Apollo bsd4.3, (some)DEC */
  281.   struct direct *dtr;
  282. #endif
  283.   int
  284.     atend, needadot,
  285.     eval, found, path_len, s,
  286.     skip_dot_files;        /* ignore names starting with "." */
  287.   struct passwd *pd;
  288.  
  289.   if (name_heap)    /* store the found file names in name_heap */
  290.   {
  291.     process_fname = stuff_name;
  292.     *(name_list = name_heap) = '\0';
  293.     prepend_blank = FALSE;
  294.   }
  295.  
  296.   *path = '\0';
  297.   if (*name == '~')        /* csh/ksh home directory expansion */
  298.   {
  299.     name++;
  300.     if (ISSLASH(*name) || *name == '\0')    /* ~/foo/bar or ~ */
  301.     {
  302.       if (ptr = getenv("HOME")) strcpy(path,ptr);
  303.       else        /* no $HOME, see if the OS knows */
  304.       {
  305. #if BADGETPW
  306.     return 1;    /* !!! a sleeze */
  307. #else
  308.     if ((pd = getpwuid(getuid())) == NULL) return 1;
  309.     strcpy(path,pd->pw_dir);
  310. #endif    /* BADGETPW */
  311.       }
  312.     }
  313.     else            /* ~fred => user freds' home directory */
  314.     {
  315.       atend = get_dpart(&name,word,&eval);
  316.       if (eval) return 1;        /* no wildcards allowed in user name */
  317.       name--;
  318.       if (!atend) word[strlen(word)-1] = '\0';    /* remove "/" from  "~fred/" */
  319. #if BADGETPW
  320.       if (!getpwhome(word)) return 1;
  321.       strcpy(path,word);
  322. #else
  323.       if ((pd = getpwnam(word)) == NULL) return 1;
  324.       strcpy(path,pd->pw_dir);
  325. #endif    /* BADGETPW */
  326.     }
  327.   }
  328.  
  329.     /* at this point, maybe: strlen(path)!=0 && strlen(name)==0 */
  330.  
  331.   atend = FALSE; needadot = FALSE;
  332.   while (!atend)
  333.   {
  334.     atend = get_dpart(&name,word,&eval);
  335.     skip_dot_files = (*word != '.');    /* ".fred" means look at dot files */
  336.     path_len = strlen(path);
  337.     if (eval)            /* wildcards in need of expansion */
  338.     {
  339. if (word[strlen(word)-1] == '.') needadot = TRUE;
  340.       found = FALSE;
  341.       if ((dir = opendir(path_len == 0 ? "." : path)) == NULL) return 1;
  342.       while (TRUE)        /* look at all entries in this directory */
  343.       {
  344.     if ((dtr = readdir(dir)) == NULL) break;
  345.     ptr = qtr = dtr->d_name;
  346.     if (skip_dot_files && *ptr == '.') continue;
  347. if (needadot && !strchr(ptr,'.')) ptr = strcat(strcpy(tbuf,ptr),".");
  348.     if (wildmat(ptr,word))
  349.     {
  350.       if (!atend)        /* something like foo/<you are here>/bar */
  351.       {
  352.         strcpy(path + path_len, qtr);
  353.             /* make sure its a real directory */
  354.         if (is_dir(path)) { strcat(path,"/"); found = TRUE; break; }
  355.       }
  356.       else            /* something like foo/<you are here> */
  357.       {
  358.         found = TRUE;
  359.         strcpy(path +path_len, qtr);
  360.         s = procz(process_fname, slash_dir, path, nopath ? path_len : 0);
  361.         path[path_len] = '\0';
  362.         if (s) { closedir(dir); return s; }
  363.         if (onlyone) break;
  364.       }
  365.     }
  366.       }        /* end while (TRUE) */
  367.       closedir(dir);
  368.       if (!found) return 1;
  369.     }
  370.     else    /* No wildcards: something like: .../bar/... or .../bar */
  371.     {        /* word may == "" (for input like "~fred") */
  372.       strcpy(path + path_len, word);
  373.       if (atend)    /* all done */
  374.     return procz(process_fname, slash_dir, path, nopath ? path_len : 0);
  375.  
  376. /* ??? if (!is_dir(path)) return 1; /* Make sure path is real */
  377.     }
  378.   }        /* end while */
  379.   return 0;
  380. }
  381.  
  382.  
  383.     /* Input:
  384.      *   process_fname:  pointer to function to call
  385.      *   slash_dir: TRUE:  if path is a directory, append a slash
  386.      *   path:  Full path name
  387.      *   path_len:  offset in path.  Used if what to pass just part of path
  388.      *     to process_fname.
  389.      * Returns:
  390.      *   What ever process_fname returns.
  391.      * Notes:
  392.      *   Need to check path[path_len] before appending a slash because:  if
  393.      *     don't want a path (path_len != 0) and no name (eg expanding "~"),
  394.      *     would process_fname("/") which is not what is expected.
  395.      */
  396. static int procz(process_fname, slash_dir, path, path_len)
  397.   pfi process_fname; char *path;
  398. {
  399.   if (slash_dir && (path[path_len] != '\0') && is_dir(path))
  400.     strcat(path, "/");
  401.   return (*process_fname)(path + path_len);
  402. }
  403.  
  404.  
  405. static int is_dir(path) char *path;
  406. {
  407.   struct stat sbuf;
  408.  
  409.   if (stat(path,&sbuf)) return FALSE;
  410.     /* make sure its a directory */
  411. #ifdef S_ISDIR
  412.   return S_ISDIR(sbuf.st_mode);
  413. #else
  414.   return ((sbuf.st_mode & 0170000) == 040000);
  415. #endif    /* S_ISDIR */
  416. }
  417.  
  418.  
  419. #if BADGETPW
  420.     /* Get the home directory out of the password file.
  421.      * Only use this if the system getpw... routines are
  422.      *   screwed up.
  423.      */
  424. static int getpwhome(name) char *name;
  425. {
  426.   char buf[256], *ptr;
  427.   FILE *fptr;
  428.   int n, s = FALSE;
  429.  
  430.   if ((fptr = fopen("/etc/passwd","r")) == NULL) return FALSE;
  431.   while (fgets(buf,255,fptr))
  432.   {
  433.     for (ptr = buf; *ptr != ':'; ptr++) ; *ptr = '\0';
  434.     if (strcmp(name,buf)) continue;
  435.     for (n = 0; n < 4; ptr++) if (*ptr == ':') n++;
  436.     while (*ptr != ':') *name++ = *ptr++;   *name = '\0';
  437.     s = TRUE;
  438.     break;
  439.   }
  440.   fclose(fptr);
  441.   return s;
  442. }
  443. #endif    /* BADGETPW */
  444.  
  445. #if ATARI    /* Atari stuff for get_dpart(), same as MS-DOS */
  446. #define ASLASH        case '/': case '\\'
  447. #define GOTTA_EVAL    case '?': case '[': case '*'
  448. #else
  449.     /* UNIX stuff for get_dpart() */
  450. #define ASLASH        case '/'
  451. #define GOTTA_EVAL    case '?': case '\\': case '[': case '*'
  452. #endif    /* ATARI */
  453.  
  454.  
  455. #endif    /* UX_OS || ATARI */
  456.  
  457.  
  458.  
  459.  
  460.  
  461.  
  462.  
  463.    /* Get the next part of the filename (ie the stuff between "/"s).  The
  464.     *   parts of "/foo/*.c" are: "/", "foo", "*.c".
  465.     * Input:
  466.     * Output:
  467.     *   eval:  TRUE if part contains wildcards needing expansion.
  468.     *   word:  The part.
  469.     *   start: Points after the part (after the "/" or '\0').
  470.     * Returns:
  471.     *   TRUE: Hit the end of the filename else FALSE.
  472.     */
  473. static int get_dpart(start,word,eval) char **start, *word; int *eval;
  474. {
  475.   register char *ptr = *start;
  476.   int atend = TRUE;
  477.  
  478.   *eval = FALSE;
  479.   while (TRUE)
  480.   {
  481.     switch (*word++ = *ptr++)
  482.     {
  483.       ASLASH:
  484.         atend = FALSE;
  485.         if (*eval) word--;            /* remove trailing "/" */
  486.       case '\0': *word = '\0'; *start = ptr; return atend;
  487.       GOTTA_EVAL: *eval = TRUE; break;
  488.     }
  489.   }
  490. }
  491.  
  492.  
  493. #ifdef TEST
  494. /* **************** TEST ******************************************** */
  495.  
  496. #include "dtable.h"
  497. declare_and_init_dTable(ntable,char *);
  498.  
  499. extern char *savestr();
  500.  
  501. add_to_table(name) char *name;
  502. {
  503.   static int n = 0;
  504.  
  505.   xpand_dTable(&ntable, 1, 50, 25);
  506.   ntable.table[n++] = savestr(name);
  507.   return 0;
  508. }
  509.  
  510. char name_heap[3000];
  511. main()
  512. {
  513.   char buf[80], *zim = NULL;
  514.   int j, nopath;
  515.  
  516.   printf("Use name_heap? "); gets(buf); if (atoi(buf)) zim = name_heap;
  517.   printf("No path? "); gets(buf); nopath = atoi(buf);
  518.  
  519.   printf(": "); gets(buf);
  520.   if (fxpand(buf,FALSE,nopath,zim,add_to_table)) puts("blew up");
  521.   else
  522.   {
  523.     if (zim) printf(">%s<\n",zim);
  524.     else
  525.     {
  526.       for (j = 0; j < sizeof_dTable(&ntable); j++)
  527.     printf("table[%d]: %s\n",j,ntable.table[j]);
  528.     }
  529.   }
  530. }
  531. #endif
  532.