home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume3 / msdir < prev    next >
Text File  |  1986-11-30  |  11KB  |  342 lines

  1. Subject: MSDOS directory access routine
  2. Newsgroups: mod.sources
  3. Approved: jpn@panda.UUCP
  4.  
  5. Mod.sources:  Volume 3, Issue 60
  6. Submitted by: genrad!decvax!ihnp4!homebru!ignatz
  7.  
  8. Several weeks ago, James Gillogly submitted this routine, directory(),
  9. for manipulating MS/PC-DOS directories.  While it was quite useful,
  10. I've found that there were some things that I really needed; the
  11. result is the modified directory routine below.  It's been in use for
  12. some time now, so I think all the bugs are out.  Specifically, the
  13. changes I added are:
  14.  
  15.     - Provided a calling convention for the release of the
  16.       acquired memory. (If processing multiple wildcard entries
  17.       on the same command line, the old version ate memory)
  18.  
  19.     - Converted to run under both Aztec and Lattice 'C'
  20.  
  21.     - Modified returned namelist so that it's a null-terminated
  22.       list of pointers, a la 'argv'
  23.  
  24.     - Provided an externally defined flag that allows the savvy
  25.       user to specify inclusion or exclusion of different file
  26.       types; most useful for finding subdirectories.
  27.  
  28.     - Modified so that it returns the full path, and the drive
  29.       specifier if provided, rather than just the filename.  This
  30.       makes the returned filename strings directly usable by
  31.       routines such as 'open', 'creat', etc.
  32.  
  33. I hope you find these useful; I have.
  34.  
  35.             Dave Ihnat
  36.             Analysts International Corporation
  37.             ihnp4!aicchi!ignatz || ihnp4!homebru!ignatz
  38.  
  39. ====================== Hier Schneiden =============================
  40. /* direct.c - Search a directory for files matching the description, and
  41.  *      return a sorted list of the matching files.  Supports drive name,
  42.  *      path, and wildcards.  Expand your argv and amaze your friends.
  43.  *
  44.  * For use with Lattice C; last compiled with version 2.15.
  45.  * Requires STDIO.H and DOS.H .
  46.  *
  47.  * IF LATTICE:
  48.  * Allocates space for the filenames with getmem, so be careful with
  49.  * your rlsmem's.
  50.  *
  51.  * ELSE    uses malloc(),free()
  52.  *
  53.  * (c) 1984 James J. Gillogly.  All rights reserved.
  54.  * Copyright abandoned 28 Sep 1985.
  55.  *
  56.  * Modified 10/6/85 by D. M. Ihnat
  57.  *
  58.  * Usage: n = directory(filename, names);
  59.  *   integer n;       Returns number of files found. 
  60.  *   char *filename;  May include drive name, path, *, and ? 
  61.  *   char ***names;   Address of the char ** you want pointing to the list. 
  62.  *
  63.  * sel_disk(letter); char letter;
  64.  *      Sets current drive to this one (A, B, etc); returns NO on error.
  65.  * n = directory(filename, names); char *filename, **names;
  66.  *      Search current directory for this filename (may include ?'s), and
  67.  *      allocate enough space for all the resulting filenames.
  68.  *      Returns number of files found.
  69.  * first_file(filename, buffer)
  70.  *      Looks for the first file set up in the global file control block;
  71.  *      returns NO if none.
  72.  * next_file(buffer)
  73.  *      Looks for the next file; must be preceded by a first_file().
  74.  * set_dta(adr); char *adr;
  75.  *      Sets the disk transfer address used by the directory functions.
  76.  *
  77.  * 29 Aug 84: sel_disk(letter), directory(), first_file(), next_file(),
  78.  *            set_dta(adr)
  79.  *
  80.  * 17 Sep 85: changed the calling sequence for directory:
  81.  *     n = directory(filename, names); char *filename, ***names;
  82.  *     It now takes the address of a char ** pointer pointer.
  83.  *   Support drive letters and paths (requires DOS 2.0 or higher).
  84.  *
  85.  *  6 Oct 85:    Modified by Dave Ihnat
  86.  *    - Supports both Lattice and non-Lattice compilers (notably Aztec)
  87.  *    - Now returns a pointer to a NULL-terminated namelist , similar to **argv
  88.  *    - Supports a call of the form directory((char *)NULL,names) to
  89.  *        properly release used memory.  Returns 0 if release OK, else -1.
  90.  *    - Added extern unsigned _dir_mode_ to select type of search.  Changes
  91.  *        to this flag are only valid when starting a search; it is a
  92.  *        logical OR of:
  93.  *        0x0    :    Normal files (default)
  94.  *        0x1    :    Read-only files
  95.  *        0x2    :    Hidden files
  96.  *        0x4    :    System files
  97.  *        0x8    :    Volume ID (not recommended)
  98.  *        0x10    :    Directories
  99.  *
  100.  *    24 Oct 85:    Modified by Dave Ihnat
  101.  *    - Modified to check for existence of a drive designation or a path
  102.  *        prefix; if one exists, remember it and prepend it to every filename
  103.  *        returned.  This makes the returned file list directly usable by
  104.  *        in calls such as open(), close().
  105.  */
  106.  
  107. #include <stdio.h>
  108. #include <ctype.h>
  109.  
  110. #ifdef    LATTICE
  111. #include <dos.h>
  112. #define    MEMALLOC    getmem
  113. #define    MEMFREE(a,b)    rlsmem(a,b)
  114. #else
  115. #include <ctype.h>
  116. #define    MEMALLOC    malloc
  117. #define    MEMFREE(a,b)    free(a)
  118. #endif
  119.  
  120. /*
  121.  * Both Aztec 3.20d and Lattice 2.15 have strchr/strrchr; this is for any
  122.  * others who may NOT.
  123.  */
  124. #define    RTCHAR    strrchr
  125.  
  126. #define SEL_DISK  0x0E  /* BDOS: select current disk drive */
  127. #define SET_DTA   0x1A  /* BDOS: set disk transfer address */
  128. #define FIND_FRST 0x4E  /* BDOS: find first file, long form */
  129. #define FIND_NEXT 0x4F  /* BDOS: find next file, long form */
  130.  
  131. #define ERROR     0xFF  /* BDOS: error return */
  132. #define NOTFOUND   2    /* Error return: file not found */
  133. #define NOMORE    18    /* Error return: no more files */
  134.  
  135. #define YES 1
  136. #define NO 0
  137.  
  138. #define MAXNAMES 100            /* temp - will get cleverer later */
  139.  
  140. struct dirstruct                /* temporary holding area for entries */
  141. {       struct dirstruct *next; /* pointer to next entry */
  142.     char *fname;            /* filename of this entry */
  143. };
  144.  
  145. struct EFCB                     /* FCB for path-type calls */
  146. {       char reserved[21];
  147.     char attrib_found;
  148.     char file_time[2];
  149.     char file_date[2];
  150.     char low_size[2];
  151.     char high_size[2];
  152.     char file_name[13];
  153. };
  154.  
  155. unsigned _dir_mode_ = 0;    /* Initialize to use only normal files */
  156.  
  157. directory(filename, names)      /* look up filename in the directory */
  158. char *filename, ***names;
  159. {       char buffer[80];
  160.     struct EFCB dta;
  161.     register struct dirstruct *p,*q;/* trundle down the directory list */
  162.     struct dirstruct *dir_root;     /* root of directory list */
  163.     register int n;                 /* number of entries */
  164.     register int i, j;
  165.     char *ptr_prefix;        /* For storage of any prefix data */
  166.     int len_prefix;
  167.  
  168.     char *MEMALLOC();
  169.     char *RTCHAR();
  170.  
  171.     /* If filename is a null path, then user is trying to release memory */
  172.     if (filename == (char *)NULL)
  173.         return(free_direct(names));
  174.  
  175.     /*
  176.      * If there is a drive designator or a path in the filename,
  177.      * save it.  Check first for a path; if there is one, then
  178.      * the drive designator, if any, will be saved as a side effect.
  179.      * Otherwise, explicitly save the descriptor.  Notice that, as either
  180.      * the MS-DOS directory delimiter '\', or the Unix-style '/' may
  181.      * be given to MS-DOS from within a program, BOTH must be tested!
  182.      */
  183.     len_prefix = 0;
  184.  
  185.     if(((ptr_prefix = RTCHAR(filename,'\\')) != (char *)NULL) ||
  186.        ((ptr_prefix = RTCHAR(filename,'/')) != (char *)NULL)   )
  187.     {
  188.         /* Path; get storage, and copy it */
  189.  
  190.         char *srcptr,*dstptr,*endptr; /* Local block storage */
  191.  
  192.         len_prefix = (ptr_prefix - filename) +2; /* Allow for null */
  193.  
  194.         endptr = ptr_prefix+1;    /* Point just after the delimiter */
  195.  
  196.         ptr_prefix = MEMALLOC(len_prefix);
  197.         for(srcptr=filename,dstptr=ptr_prefix;srcptr < endptr;dstptr++,srcptr++)
  198.                 *dstptr = _toupper(*srcptr);
  199.  
  200.         *dstptr = '\0';    /* Null terminate */
  201.  
  202.     }else if(filename[1] == ':')
  203.     {
  204.         /* Only a drive designator; save it. */
  205.         ptr_prefix = MEMALLOC(3);    /* 2 characters + null */
  206.  
  207.         ptr_prefix[0] = _toupper(filename[0]);
  208.         ptr_prefix[1] = filename[1];
  209.         ptr_prefix[2] = '\0';
  210.         len_prefix = 2;
  211.     }
  212.  
  213.     if (!first_file(filename, buffer, &dta))
  214.         return 0;       /* no files found */
  215.     n = 1;                  /* one entry found  so far */
  216.     p->next = 0;
  217.  
  218.     dir_root = (struct dirstruct *) MEMALLOC(sizeof (struct dirstruct));
  219.     p = dir_root;           /* point at top of directory list */
  220.  
  221.     p->fname = (char *) MEMALLOC(strlen(buffer) + 1);
  222.     strcpy(p->fname, buffer); /* save the first filename */
  223.  
  224.     while (next_file(buffer, &dta))
  225.     {   n++;        /* looks like another winner */
  226.         p->next = (struct dirstruct *) MEMALLOC(sizeof (struct dirstruct));
  227.         p = p->next;    /* point at the next entry */
  228.         p->fname = (char *) MEMALLOC(strlen(buffer) + 1);
  229.         strcpy(p->fname, buffer);       /* save it */
  230.     }
  231.     p->next = 0;    /* end of the line */
  232.  
  233.     /* (n names) + NULL ptr */
  234.     *names = (char **) MEMALLOC((n+1) * sizeof (char *));
  235.  
  236.     for (i = 0, p = dir_root; i < n; i++)   /* insertion sort on pointers */
  237.     {       for (j = i; j > 0; j--)    /* where does this one go? */
  238.         {       if (strcmp((*names)[j - 1], p->fname) < 0) break;
  239.             (*names)[j] = (*names)[j - 1];
  240.         }
  241.         (*names)[j] = (char *)MEMALLOC(strlen(p->fname)+len_prefix+1);
  242.         if(len_prefix)
  243.         {
  244.             strcpy((*names)[j],ptr_prefix);
  245.             strcat((*names)[j], p->fname);
  246.         }else
  247.             strcpy((*names)[j], p->fname);
  248.  
  249. #ifdef VERBOSE
  250.         printf("%s goes into slot %d.\n", p->fname, j);
  251. #endif VERBOSE
  252.         q = p;          /* save it so we can delete it */
  253.         p = p->next;    /* after pressing on to the next entry */
  254.         MEMFREE(q, sizeof (struct dirstruct));
  255.     }
  256.  
  257.     (*names)[n] = (char *)NULL;    /* Provide a null terminator */
  258.  
  259.     if(len_prefix)
  260.         MEMFREE(ptr_prefix,strlen(ptr_prefix));    /* Return the path memory */
  261.  
  262. #ifdef VERBOSE
  263.     printf("Here they are:\n");
  264.     for (i = 0; i < n; i++)
  265.         printf("\t%s\n", (*names)[i]);
  266.  
  267.     printf("Should be null terminator: 0x%x\n",(unsigned)names[n]);
  268. #endif VERBOSE
  269.     return n;
  270. }
  271.  
  272. first_file(filename, buffer, dta) /* set up FCB and get first file */
  273. char *filename, *buffer;
  274. struct EFCB *dta;
  275. {       register char *s, *t;
  276.     register int i, ret;
  277.  
  278.     for (s = filename; *s; s++) if (islower(*s)) *s = toupper(*s);
  279. #ifdef    LARGEDATA
  280.     ret = bdosx(SET_DTA, dta, 0);   /* set up data transfer area */
  281.     ret = bdosx(FIND_FRST, filename, _dir_mode_);
  282. #else
  283.     ret = bdos(SET_DTA, dta, 0);   /* set up data transfer area */
  284.     ret = bdos(FIND_FRST, filename, _dir_mode_);
  285. #endif
  286.     if (ret == NOTFOUND || ret == NOMORE) return NO;
  287.     getname(buffer, dta);   /* transfer the filename found to the caller */
  288.     return YES;
  289. }
  290.  
  291. next_file(buffer, dta) /* look up next file with the same attributes */
  292. char *buffer;
  293. struct EFCB *dta;
  294. {       int ret;
  295.  
  296.     if (bdos(FIND_NEXT, 0, 0) == NOMORE) return NO;
  297.     getname(buffer, dta);
  298.     return YES;
  299. }
  300.  
  301. getname(buffer, dta)    /* transfer filename from dta to the buffer area */
  302. char *buffer;
  303. struct EFCB *dta;
  304. {       strcpy(buffer, dta->file_name);
  305. }
  306.  
  307.  
  308. sel_disk(letter)        /* make this disk the current one */
  309. char letter;
  310. {       int ret;
  311.  
  312.     ret = bdos(SEL_DISK, letter - 'A', 0);
  313.     return ret;
  314. }
  315.  
  316. free_direct(names)
  317. char ***names;
  318. {
  319.     int index;
  320.  
  321.     /*
  322.      * Release each of the filename strings. There's room for
  323.      * some twiddling here to avoid unnecessary strlen calls,
  324.      * but this should be so rarely called that the overhead should
  325.      * be acceptable.  Feel free to change it, however, if you wish...
  326.      */
  327.     for(index = 0;(*names)[index] != (char *)NULL;index++)
  328.         if(MEMFREE((*names)[index],(strlen((*names)[index])+1)))
  329.             return(-1);    /* Corrupted--don't trust anything */
  330.  
  331.     /* Now release the pointer structure */
  332.     if(MEMFREE(*names,(index * sizeof(char *))))
  333.         return(-1);
  334.  
  335.     /* Finally, remove any temptation to touch that released memory */
  336.     *names = (char **)NULL;
  337.  
  338.     return(0);
  339. }
  340.  
  341.  
  342.