home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 December / simtel1292_SIMTEL_1292_Walnut_Creek.iso / msdos / c / du.arc / MSD_DIR.C < prev    next >
C/C++ Source or Header  |  1987-12-26  |  8KB  |  307 lines

  1. /*
  2.  * @(#)msd_dir.c 1.4 87/11/06    Public Domain.
  3.  *
  4.  *  A public domain implementation of BSD directory routines for
  5.  *  MS-DOS.  Written by Michael Rendell ({uunet,utai}michael@garfield),
  6.  *  August 1897
  7.  *
  8.  *  Extended by Peter Lim (lim@mullian.oz) to overcome some MS DOS quirks
  9.  *  and returns 2 more pieces of information - file size & attribute.
  10.  *  Plus a little reshuffling of #define's positions    December 1987
  11.  */
  12.  
  13. #include    <stdio.h>
  14. #include    <sys/types.h>
  15. #include    <sys/stat.h>
  16. #include    "msd_dir.h"
  17. #include    <malloc.h>
  18. #include    <string.h>
  19. #include    <ctype.h>
  20. #include    <dos.h>
  21. #include    <direct.h>
  22.  
  23. #ifndef    NULL
  24. # define    NULL    0
  25. #endif    /* NULL */
  26.  
  27. /* dos call values */
  28. #define    DOSI_FINDF    0x4e
  29. #define    DOSI_FINDN    0x4f
  30. #define    DOSI_SDTA    0x1a
  31. #define DOSI_GCDIR    0x47
  32.  
  33. #define    Newisnull(a, t)        ((a = (t *) malloc(sizeof(t))) == (t *) NULL)
  34.  
  35. /* what find first/next calls look use */
  36. typedef struct {
  37.     char        d_buf[21];
  38.     char        d_attribute;
  39.     unsigned short    d_time;
  40.     unsigned short    d_date;
  41.     unsigned long    d_size;
  42.     char        d_name[13];
  43. } Dta_buf;
  44.  
  45. static    char    *getdirent();
  46. static    void    setdta();
  47. static    void    free_dircontents();
  48. static    char    *extgetcwd();
  49.  
  50. static    Dta_buf        dtabuf;
  51. static    Dta_buf        *dtapnt = &dtabuf;
  52. static    union REGS    reg, nreg;
  53.  
  54. #if    defined(M_I86LM)
  55. static    struct SREGS    sreg;
  56. #endif
  57.  
  58. DIR    *
  59. opendir(pathname, att_mask)
  60.     char    *pathname;
  61.     unsigned int    att_mask;
  62. {
  63.     struct    stat        statb;
  64.     DIR            *dirp;
  65.     char            c;
  66.     char            *s;
  67.     struct _dircontents    *dp;
  68.     char            nbuf[MAXPATHLEN + 1];
  69.     char            name[MAXPATHLEN + 1];
  70.     unsigned char        drive_id;
  71.     char            *phead;
  72.     
  73.     strcpy (name, pathname);
  74.         /* Work on temporary buffer only. Never write it back,
  75.            the calling argument may not have room for it ! */
  76.     if (stat(name, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFDIR) {
  77.         /* Give it a second try, after modifying input pathname,
  78.            before giving up to counter some MS DOS quirks.
  79.            This kludge is fairly simple and will not handle weird
  80.            though valid path name correctly such as multiple ../.. and
  81.            other mix which eventually end up on the root directory. */
  82.         if (isalpha (*name) && (name[1] == ':')) {
  83.             drive_id = *name -  ((islower(*name)) ? 'a' : 'A') + 1;
  84.             phead = pathname+2;
  85.         }
  86.         else {
  87.             drive_id = 0;
  88.             phead = pathname;
  89.         }
  90.         if ((c = name[strlen(name) - 1]) == '\\' || c == '/')
  91.             name[strlen(name) - 1] = NULL;
  92.             /* Try removing one trailing / or \ */
  93.         if (*phead == '.' || *phead == '\0') {
  94.             /* If . or nothing specified, assume current directory
  95.                and go get the directory. */
  96.             if (extgetcwd (drive_id, name, MAXPATHLEN) == NULL)
  97.                 return (DIR *) NULL;
  98.             strcpy (nbuf, name);
  99.             /* There is an undocumented BUG in MSC 4.0 such that
  100.                stat (root, ..) will cause the current directory on the
  101.                specified drive to be changed to root if the current
  102.                directory in question is exactly one level deep !
  103.                So, keep current directory for chdir() back after doing
  104.                stat (root, ..).  lim@mullian.oz */
  105.             if (*(phead+1) == '.') {
  106.                 /* i.e. ".." specified. Then backup one level. Firstly
  107.                     check that we are not already at the root. */
  108.                 if (name[strlen(name) - 1] == '\\')
  109.                     return (DIR *) NULL;
  110.                 while (name[strlen(name) - 1] != '\\')
  111.                     name[strlen(name) - 1] = NULL;
  112.                 if (*(phead+2) == '\\' || *(phead+2) == '/')
  113.                     /* Make sure we don't have a '\' double up. */
  114.                     strcat (name, phead+3);
  115.             }
  116.             else if (*(phead) == '.')
  117.                 /* Just plain "." specified. */
  118.                 strcat (name, phead+1);
  119.         }
  120.         else
  121.             *nbuf = NULL;        /* Don't chdir() wrongly. */
  122.         if (stat(name, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFDIR)
  123.             return (DIR *) NULL;
  124.         if (*nbuf)
  125.             (void) chdir (nbuf);    /* Fixing the stat() BUG ! */
  126.     }
  127.     if (Newisnull(dirp, DIR))
  128.         return (DIR *) NULL;
  129.     if (*name && (c = name[strlen(name) - 1]) != '\\' && c != '/')
  130.         (void) strcat(strcpy(nbuf, name), "\\*.*");
  131.     else
  132.         (void) strcat(strcpy(nbuf, name), "*.*");
  133.     dirp->dd_loc = 0;
  134.     setdta();
  135.     dirp->dd_contents = dirp->dd_cp = (struct _dircontents *) NULL;
  136.     if ((s = getdirent(nbuf, att_mask | A_DIR)) == (char *) NULL)
  137.         return dirp;
  138.     do {
  139.         if (Newisnull(dp, struct _dircontents) || (dp->_d_entry =
  140.             (char *) malloc((unsigned) (strlen(s) + 1))) == (char *) NULL)
  141.         {
  142.             if (dp)
  143.                 free((char *) dp);
  144.             free_dircontents(dirp->dd_contents);
  145.             return (DIR *) NULL;
  146.         }
  147.         if (dirp->dd_contents)
  148.             dirp->dd_cp = dirp->dd_cp->_d_next = dp;
  149.         else
  150.             dirp->dd_contents = dirp->dd_cp = dp;
  151.         (void) strcpy(dp->_d_entry, s);
  152.         dp->d_attribute = dtabuf.d_attribute;
  153.         dp->d_size = dtabuf.d_size;
  154.         /* A SUPER Kludge ! Using 'dtabuf' as global variable. lim@mullian.oz */
  155.         dp->_d_next = (struct _dircontents *) NULL;
  156.     } while ((s = getdirent((char *) NULL, att_mask | A_DIR)) != (char *) NULL);
  157.     dirp->dd_cp = dirp->dd_contents;
  158.  
  159.     return dirp;
  160. }
  161.  
  162. void
  163. closedir(dirp)
  164.     DIR    *dirp;
  165. {
  166.     free_dircontents(dirp->dd_contents);
  167.     free((char *) dirp);
  168. }
  169.  
  170. struct direct    *
  171. readdir(dirp)
  172.     DIR    *dirp;
  173. {
  174.     static    struct direct    dp;
  175.     
  176.     if (dirp->dd_cp == (struct _dircontents *) NULL)
  177.         return (struct direct *) NULL;
  178.     dp.d_namlen = dp.d_reclen =
  179.         strlen(strcpy(dp.d_name, dirp->dd_cp->_d_entry));
  180.     dp.d_ino = 0;
  181.     dp.d_attribute = dirp->dd_cp->d_attribute;
  182.     dp.d_size = dirp->dd_cp->d_size;
  183.     dirp->dd_cp = dirp->dd_cp->_d_next;
  184.     dirp->dd_loc++;
  185.  
  186.     return &dp;
  187. }
  188.  
  189. void
  190. seekdir(dirp, off)
  191.     DIR    *dirp;
  192.     long    off;
  193. {
  194.     long            i = off;
  195.     struct _dircontents    *dp;
  196.  
  197.     if (off < 0)
  198.         return;
  199.     for (dp = dirp->dd_contents ; --i >= 0 && dp ; dp = dp->_d_next)
  200.         ;
  201.     dirp->dd_loc = off - (i + 1);
  202.     dirp->dd_cp = dp;
  203. }
  204.  
  205. long
  206. telldir(dirp)
  207.     DIR    *dirp;
  208. {
  209.     return dirp->dd_loc;
  210. }
  211.  
  212. static    void
  213. free_dircontents(dp)
  214.     struct    _dircontents    *dp;
  215. {
  216.     struct _dircontents    *odp;
  217.  
  218.     while (dp) {
  219.         if (dp->_d_entry)
  220.             free(dp->_d_entry);
  221.         dp = (odp = dp)->_d_next;
  222.         free((char *) odp);
  223.     }
  224. }
  225.  
  226. static    char    *
  227. getdirent(dir, att_mask)
  228.     char    *dir;
  229.     unsigned int    att_mask;
  230. {
  231.     if (dir != (char *) NULL) {        /* get first entry */
  232.         reg.h.ah = DOSI_FINDF;
  233.         reg.h.cl = att_mask;
  234. #if    defined(M_I86LM)
  235.         reg.x.dx = FP_OFF(dir);
  236.         sreg.ds = FP_SEG(dir);
  237. #else
  238.         reg.x.dx = (unsigned) dir;
  239. #endif
  240.     } else {                /* get next entry */
  241.         reg.h.ah = DOSI_FINDN;
  242. #if    defined(M_I86LM)
  243.         reg.x.dx = FP_OFF(dtapnt);
  244.         sreg.ds = FP_SEG(dtapnt);
  245. #else
  246.         reg.x.dx = (unsigned) dtapnt;
  247. #endif
  248.     }
  249. #if    defined(M_I86LM)
  250.     intdosx(®, &nreg, &sreg);
  251. #else
  252.     intdos(®, &nreg);
  253. #endif
  254.     if (nreg.x.cflag)
  255.         return (char *) NULL;
  256.  
  257.     return dtabuf.d_name;
  258. }
  259.  
  260. static    void
  261. setdta()
  262. {
  263.     reg.h.ah = DOSI_SDTA;
  264. #if    defined(M_I86LM)
  265.     reg.x.dx = FP_OFF(dtapnt);
  266.     sreg.ds = FP_SEG(dtapnt);
  267.     intdosx(®, &nreg, &sreg);
  268. #else
  269.     reg.x.dx = (int) dtapnt;
  270.     intdos(®, &nreg);
  271. #endif
  272. }
  273.  
  274. static    char    *extgetcwd(drive_id, buffer, buffer_size)
  275. /* Extended get current directory on specified drive.  Peter Lim 07-Dec-87. */
  276. unsigned char drive_id;
  277. char *buffer;
  278. int  buffer_size;
  279. {
  280.     char    tmpbuffer[MAXPATHLEN+1];
  281.  
  282.     if (!drive_id)
  283.         return (getcwd (buffer, buffer_size));
  284.     /* If it is current drive, use the standard getcwd() */
  285.  
  286.     reg.h.ah = DOSI_GCDIR;
  287.     reg.h.dl = drive_id;
  288. #if    defined(M_I86LM)
  289.     reg.x.si = FP_OFF(tmpbuffer);
  290.     sreg.ds = FP_SEG(tmpbuffer);
  291.     intdosx(®, &nreg, &sreg);
  292. #else
  293.     reg.x.si = (int) tmpbuffer;
  294.     intdos(®, &nreg);
  295. #endif
  296.     if (nreg.x.ax == 0xf)
  297.         return ((char *) NULL);
  298.         /* Invalid drive specification. */
  299.     else {
  300.         if (drive_id)
  301.             sprintf (buffer, "%c:\\%s", drive_id+'A'-1, tmpbuffer);
  302.         else
  303.             sprintf (buffer, "\\%s", tmpbuffer);
  304.         return (buffer);
  305.     }
  306. }
  307.