home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / c / dirlib.c < prev    next >
C/C++ Source or Header  |  1994-03-04  |  12KB  |  677 lines

  1.  
  2.             DIRLIB for MS-DOS
  3.             -----------------
  4.  
  5. Enclosed is an implementation of the `dirlib' package for MS-DOS.  
  6. The implementation is targeted for MS-C, although any reasonably
  7. competent C compiler should manage.  The package consists of:
  8.  
  9.     dir.h        the header file
  10.     dir.c        the functions
  11.     testdir.c    a q&d test program
  12.     
  13. The package tries to view directory naming in a Un*x light; in particular,
  14. directories such as '/.' and '/..' (as well as `.' and `..' if your
  15. current directory is root) are understood.   Indefinite paths like 
  16. `/../.././../..' will correctly refer to the root (of the particular disk).
  17. Names such as `a:////./../' are okay too.
  18.  
  19. I've tried to be as sensible about DTA's as possible, since you never
  20. know who will be using one; they are set before use, and reset afterwards.
  21.  
  22. There is some cruft in the package, namely the way `seekdir' and
  23. `telldir' are done.  The code was derived from a little experimentation, 
  24. and may not work after a certain point (although I believe the 2.x version
  25. to be solid).  Caveat utilitor.
  26.  
  27. Documentation for the package is available in the public domain; the
  28. package's functionality was derived from this documentation.
  29.  
  30. Bug reports and comments are welcome.  Enjoy!
  31.  
  32.                 - Matt
  33.  
  34. -------
  35. UUCP:    {ucbvax,ihnp4,randvax,trwrb!trwspp,ism780}!ucla-cs!matt
  36. ARPA:    matt@LOCUS.UCLA.EDU
  37. Ph:    (213) 825-2756
  38.  
  39. ----------------------------  dirlib.h -------------------------------
  40.  
  41. /* DIRLIB.H by M. J. Weinstein   Released to public domain 1-Jan-89 */
  42.  
  43. #define MAXNAMLEN    15
  44.  
  45. struct direct {
  46.     long        d_ino;
  47.     unsigned short     d_reclen;
  48.     unsigned short     d_namlen;
  49.     char        d_name[MAXNAMLEN+1];
  50. };
  51.  
  52. typedef struct {
  53.     struct {
  54.         char    fcb[21];
  55.         char    attr;
  56.         short    time;
  57.         short    date;
  58.         long    size;
  59.         char    name[13];
  60.     } dd_dta;
  61.     short    dd_dosver;    /* which version of dos? */
  62.     short    dd_stat;    /* status return from last lookup */
  63.     char    dd_name[1];    /* full name of file -- struct is extended */
  64. } DIR;
  65.  
  66. #ifdef LINT_ARGS
  67.     DIR *opendir(char *);
  68.     struct direct *readdir(DIR *);
  69.     long telldir(DIR *);
  70.     void seekdir(DIR *, long);
  71.     void closedir(DIR *);
  72. #else
  73.     DIR *opendir();
  74.     struct direct *readdir();
  75.     long telldir();
  76. #endif LINT_ARGS
  77.  
  78. #define rewinddir(dirp)    seekdir(dirp,0L)
  79.  
  80. --------------------------- dirlib.c ---------------------------------
  81.  
  82. /*
  83.     Directory Access Library
  84.  
  85.                     DIRLIB.C by M. J. Weinstein
  86.          Released to public domain 1-Jan-89
  87.  
  88.     The author may be contacted at: 
  89.     matt@cs.ucla.edu -or- POB 84524, L.A., CA  90073
  90.  */
  91.  
  92.  
  93. /*
  94.  * revision history:
  95.  *
  96.  *    VER    MM/DD/YY    COMMENTS
  97.  *    ----    --------    --------
  98.  *    0.99    02/24/86    Beta release to INTERNET
  99.  */
  100.  
  101. #define LINT_ARGS
  102.  
  103. #include <stdlib.h>
  104. #include <ctype.h>
  105. #include <errno.h>
  106. #include <string.h>
  107. #include <dos.h>
  108. #include <malloc.h>
  109.  
  110. #include "dir.h"
  111.  
  112. #ifdef DEBUG
  113. #    define    PRIVATE
  114. #else
  115. #    define    PRIVATE    static
  116. #endif    DEBUG
  117.  
  118. PRIVATE _err;
  119.  
  120. PRIVATE DIR *_findfirst(char *, DIR *);
  121. PRIVATE DIR *_findnext(DIR *);
  122. PRIVATE char far *_getsetdta(char far *);
  123.  
  124. #ifdef DEBUG
  125.  
  126. #include <stdio.h>
  127.  
  128. PRIVATE void _dumpdir(DIR *);
  129.  
  130. PRIVATE void _dumpdir(dirp)
  131. DIR *dirp;
  132. {
  133.     int i;
  134.     char *cp;
  135.  
  136.     printf("\ndump of DIR at %xH: fcb:", dirp);
  137.     for (i = 0, cp = (char *)&dirp->dd_dta; 
  138.         i < sizeof(dirp->dd_dta.fcb); i++, cp++) {
  139.         if ((i%16) == 0)
  140.             printf("\n%3.3d (%3.3xH):  ", i);
  141.         printf("%3d ", (unsigned) *cp & 0xff);
  142.     }
  143.     printf("\nattr %xH  time %d  date %d  size %ld  name %s\n", 
  144.         dirp->dd_dta.attr, dirp->dd_dta.time, 
  145.         dirp->dd_dta.date, dirp->dd_dta.size, dirp->dd_dta.name);
  146.     printf("ver %d  stat %d  pattern %s\n\n", 
  147.         dirp->dd_dosver, dirp->dd_stat, dirp->dd_name);
  148. }
  149.  
  150. #endif DEBUG
  151.  
  152. /*
  153.  * return dos major version #
  154.  */
  155.  
  156. PRIVATE    int dosver()
  157. {
  158.     union REGS r;
  159.     r.h.ah = 0x30;
  160.     intdos(&r, &r);
  161.     return (int)r.h.al;
  162. }
  163.  
  164. /*
  165.  * get/set dta address
  166.  */
  167.  
  168. PRIVATE char far *_getsetdta(newdta)
  169. char far *newdta;
  170. {
  171.     char far *olddta;
  172.     union REGS r;
  173.     struct SREGS s;
  174.  
  175.     /* get old dta */         
  176.     r.h.ah = 0x2f;
  177.     intdos(&r, &r);
  178.     segread(&s);
  179.     FP_SEG(olddta) = s.es;
  180.     FP_OFF(olddta) = r.x.bx;
  181.  
  182.     /* conditionally set new dta */
  183.     if (newdta) {
  184.         r.h.ah = 0x1a;
  185.         s.ds    = FP_SEG(newdta);
  186.         r.x.dx    = FP_OFF(newdta);    
  187.         intdosx(&r, &r, &s);
  188.     }
  189.  
  190.     return olddta;
  191. }
  192.  
  193. /*
  194.  * dos findfirst
  195.  */
  196.  
  197. PRIVATE DIR *_findfirst(name, dirp)
  198. char *name;
  199. DIR *dirp;
  200. {
  201.     union REGS r;  
  202.     struct SREGS s;
  203.     char far *dtasave;
  204.  
  205.     dtasave = _getsetdta((char far *)dirp);
  206.     
  207.     /* do directory lookup */
  208.     segread(&s);
  209.     r.h.ah    = 0x4e;
  210.     r.x.cx    = 0x10;
  211.     r.x.dx    = FP_OFF((char far *)name);
  212.     s.es    = FP_SEG((char far *)name);
  213.     intdosx(&r, &r, &s);
  214.     /* restore dta */
  215.     _getsetdta(dtasave);
  216.     _err = r.x.ax;
  217.     if (r.x.cflag)
  218.         return (DIR *) 0;
  219. #ifdef    DEBUG
  220.     _dumpdir(dirp);
  221. #endif    DEBUG
  222.     return dirp;
  223. }
  224.  
  225. /*
  226.  * dos findnext
  227.  */
  228.  
  229. PRIVATE DIR *_findnext(dirp)
  230. DIR *dirp;
  231. {
  232.     union REGS r;  
  233.     struct SREGS s;
  234.     char far *dtasave;
  235.  
  236.     dtasave = _getsetdta((char far *)dirp);
  237.  
  238.     /* do directory lookup */
  239.     r.h.ah = 0x4f;
  240.     intdos(&r, &r);
  241.     /* restore old dta */
  242.     _getsetdta(dtasave);
  243.     _err = r.x.ax;
  244.     if (r.x.cflag)
  245.         return (DIR *) 0;
  246. #ifdef    DEBUG
  247.     _dumpdir(dirp);
  248. #endif    DEBUG
  249.     return dirp;
  250. }
  251.  
  252. /*
  253.  * get working directory for a particular drive
  254.  */
  255.  
  256. PRIVATE char *getdcwd(drive)
  257. int drive;
  258. {
  259.     union REGS r;
  260.     struct SREGS s;
  261.     static char xcwd[64];
  262.     char far *cwd = xcwd;
  263.  
  264.     r.h.ah = 0x47;
  265.     r.h.dl = drive;
  266.     r.x.si = FP_OFF(cwd);
  267.     s.ds = FP_SEG(cwd);
  268.     intdosx(&r, &r, &s);
  269.     _err = r.x.ax;
  270.     if (r.x.cflag)
  271.         return (char *) 0;
  272.     return xcwd;
  273. }
  274.  
  275. /*
  276.  * opendir
  277.  */
  278.  
  279. #define SUFFIX    "\\*.*"
  280. #define    SLASH    "\\"
  281. #define streq(a,b)    (strcmp(a,b)==0)
  282.  
  283. DIR *opendir(name)
  284. char *name;
  285. {
  286.     register DIR *nd;
  287.     char *cwd;
  288.     char drive[3];
  289.     int atroot = 0;
  290.     int rooted = 0;
  291.  
  292.     /*
  293.      * hack off drive designator if present
  294.      */
  295.  
  296.     if (name[1] == ':') {
  297.         cwd = getdcwd(toupper(name[0]) - 'A' + 1);
  298.         drive[0] = name[0]; drive[1] = ':'; drive[2] = '\0';
  299.         name += 2;
  300.     }
  301.     else {
  302.         cwd = getdcwd(0);
  303.         drive[0] = '\0';
  304.     }
  305.  
  306. #ifdef    DEBUG
  307.     printf("working on drive %s = /%s\n", drive, cwd);
  308. #endif    DEBUG
  309.  
  310.     /* is the name 'rooted'? */
  311.     if ((*name == '/') || (*name == '\\')) ++rooted;
  312.  
  313.     /* see if we are at the root directory for this device */
  314.     if (!*cwd) ++atroot;
  315.  
  316.     /* 
  317.      * MSDOS '/' doesn't have a '.' or '..'
  318.      * also, double '/' sequences don't make sense.
  319.      * many ported programs expect them to work, so we fix it up...
  320.      */
  321.  
  322.     /* chop off leading . and .. if at root */
  323.     if (atroot && (*name == '.')) {
  324.         switch (*++name) {
  325.         case '\0': case '/': case '\\':
  326.             break;
  327.         case '.':
  328.             switch (*++name) {
  329.             case '\0': case '/': case '\\':
  330.                 break;
  331.             default:
  332.                 --name; 
  333.                 --name;
  334.             }
  335.             break;
  336.         default:
  337.             --name;
  338.         }
  339.     }
  340.  
  341. #ifdef DEBUG
  342.     printf("after chopping leading .'s: %s\n", name);
  343. #endif DEBUG
  344.  
  345.     /* chop off leading /'s, /.'s and /..'s to make naming sensible */
  346.     while (*name && ((*name == '/') || (*name == '\\'))) {
  347.         if (*++name == '.') {
  348.             switch (*++name) {
  349.             case '\0': case '/': case '\\':
  350.                 break;
  351.             case '.':
  352.                 switch (*++name) {
  353.                 case '\0': case '/': case '\\':
  354.                     break;
  355.                 default:
  356.                     --name; 
  357.                     --name;
  358.                 }
  359.                 break;
  360.             default:
  361.                 --name;
  362.             }
  363.         }
  364.     }
  365.  
  366. #ifdef DEBUG
  367.     printf("after chopping /'s: %s\n", name);
  368. #endif DEBUG
  369.  
  370.     /*
  371.      * name should now look like: path/path/path 
  372.      * we must now construct name based on whether or not it
  373.      * was 'rooted' (started with a /)
  374.      */
  375.  
  376.     if (rooted) cwd = "";
  377.  
  378.     /* construct DIR */
  379.     if (!(nd = (DIR *)malloc(
  380.         sizeof(DIR)+strlen(drive)+strlen(cwd)+strlen(SLASH)+
  381.         strlen(name)+strlen(SUFFIX))))
  382.         return (DIR *) 0;
  383.     /* create long name */
  384.     strcpy(nd->dd_name, drive);
  385.     if (*cwd) {
  386.         strcat(nd->dd_name, SLASH);
  387.         strcat(nd->dd_name, cwd);
  388.     }
  389.     if (*name) {
  390.         strcat(nd->dd_name, SLASH);
  391.         strcat(nd->dd_name, name);
  392.     }
  393.     strcat(nd->dd_name, SUFFIX);
  394.  
  395. #ifdef    DEBUG
  396.     printf("calling findfirst(%s)\n", nd->dd_name);
  397. #endif    DEBUG
  398.  
  399.     /* search */
  400.     if (!_findfirst(nd->dd_name, nd)) {
  401.         free((char *)nd);
  402.         errno = ENOENT;
  403.         return (DIR *) 0;
  404.     }
  405.     nd->dd_stat = 0;
  406.     nd->dd_dosver = dosver();
  407.     return nd;
  408. }
  409.  
  410. struct direct *readdir(dirp)
  411. DIR *dirp;
  412. {
  413.     static struct direct dir;
  414.  
  415.     if (dirp->dd_stat)
  416.         return (struct direct *) 0;
  417.  
  418.     /* format structure */
  419.     dir.d_ino = 0; /* not valid for DOS */
  420.     dir.d_reclen = 0;
  421.     strcpy(dir.d_name, dirp->dd_dta.name);
  422.     dir.d_namlen = strlen(dir.d_name);
  423.     strlwr(dir.d_name); /* DOSism */
  424.  
  425.     /* read ahead */
  426.     if (_findnext(dirp))
  427.         dirp->dd_stat = 0;
  428.     else        
  429.         dirp->dd_stat = _err;
  430.  
  431.     return &dir;
  432. }
  433.  
  434. void closedir(dirp)
  435. DIR *dirp;
  436. {
  437.     free((char *)dirp);
  438. }
  439.  
  440. /*
  441.  * fake seek for DOS 2.x
  442.  */
  443.  
  444. PRIVATE void dos2seek(dirp, pos)
  445. DIR *dirp;
  446. long pos;
  447. {
  448.     /*
  449.      * check against DOS limits
  450.      */
  451.  
  452.     if ((pos < 0) || (pos > 4095)) {
  453.         dirp->dd_stat = 1;
  454.         return;
  455.     }
  456.  
  457.     if (pos == 0) {
  458.         if (_findfirst(dirp->dd_name, dirp))
  459.             dirp->dd_stat = 0;
  460.         else
  461.             dirp->dd_stat = _err;
  462.     }
  463.     else {
  464.         pos--;
  465.         dirp->dd_dta.fcb[1] = 2 - (pos % 3);
  466.         *(short *)&dirp->dd_dta.fcb[13] = pos;
  467.         /* read ahead */
  468.         if (_findnext(dirp))
  469.             dirp->dd_stat = 0;
  470.         else        
  471.             dirp->dd_stat = _err;
  472.     }
  473. }
  474.  
  475. /*
  476.  * fake seek for DOS 3.x
  477.  */
  478.  
  479. PRIVATE void dos3seek(dirp, pos)
  480. DIR *dirp;
  481. long pos;
  482. {
  483.     /*
  484.      * check against DOS limits
  485.      */
  486.  
  487.     if ((pos < 0) || (pos > 4095)) {
  488.         dirp->dd_stat = 1;
  489.         return;
  490.     }
  491.  
  492.     *(short *)&dirp->dd_dta.fcb[13] = pos + 1;
  493.     /* read ahead */
  494.     if (_findnext(dirp))
  495.         dirp->dd_stat = 0;
  496.     else        
  497.         dirp->dd_stat = _err;
  498. }
  499.  
  500. void seekdir(dirp, newpos)
  501. DIR *dirp;
  502. long newpos;
  503. {
  504.     switch (dirp->dd_dosver) {
  505.     case 2:
  506.         dos2seek(dirp, newpos);
  507.         break;
  508.     case 3:
  509.         dos3seek(dirp, newpos);
  510.         break;
  511.     default:
  512.         abort();
  513.         break;
  514.     }
  515. }
  516.  
  517. PRIVATE long dos2tell(dirp)
  518. DIR *dirp;
  519. {
  520.     return (long) *(short *)&dirp->dd_dta.fcb[13];
  521.  
  522. }
  523.  
  524. PRIVATE long dos3tell(dirp)
  525. DIR *dirp;
  526. {
  527.     return (long) (*(short *)&dirp->dd_dta.fcb[13] - 2);
  528. }
  529.  
  530. long telldir(dirp)
  531. DIR *dirp;
  532. {
  533.     switch (dirp->dd_dosver) {
  534.     case 2:
  535.         return dos2tell(dirp);
  536.     case 3:
  537.         return dos3tell(dirp);
  538.     default:
  539.         abort();
  540.     }
  541. }
  542.  
  543. ----------------------- testdir.c follows ------------------
  544. /*
  545.     Directory Access Library Test Program
  546.  
  547.                TESTDIR.C by M. J. Weinstein
  548.         Released to public domain 1-Jan-89
  549.  
  550.     The author may be contacted at: 
  551.     matt@cs.ucla.edu -or- POB 84524, L.A., CA  90073
  552.  */
  553.  
  554. #define    LINT_ARGS
  555.  
  556. #include <math.h>
  557. #include <stdio.h>
  558. #include <stdlib.h>
  559. #include <string.h>
  560.  
  561. #include "dir.h"
  562.  
  563. #define    streqi(a,b)    (strcmpi(a,b) == 0)
  564.  
  565. main()
  566. {
  567.     char *token;
  568.     char buff[128];
  569.     DIR *dir = 0;
  570.     struct direct *dp;
  571.  
  572.     while (1) {
  573.         printf("> ");
  574.         if (!gets(buff)) break;
  575.         if (!(token = strtok(buff, " \t\n"))) {
  576.             printf("eh?\n");
  577.         }
  578.         else if (streqi("open", token)) {
  579.             if (dir) {
  580.                 closedir(dir);
  581.                 dir = 0;
  582.                 printf("previous directory closed\n");
  583.             }
  584.             if (!(token = strtok(NULL, "\n"))) {
  585.                 printf("please specify directory name\n");
  586.             }
  587.             else if (!(dir = opendir(token))) {
  588.                 perror(token);
  589.             }
  590.             else {
  591.                 printf("directory %s opened successfully\n", token);
  592.             }
  593.         }
  594.         else if (streqi("close", token)) {
  595.             if (!dir) {
  596.                 printf("no directory open\n");
  597.             }
  598.             else {
  599.                 closedir(dir);
  600.                 dir = 0;
  601.                 printf("directory closed\n");
  602.             }
  603.         }
  604.         else if (streqi("rew", token)) {
  605.             if (!dir) {
  606.                 printf("no directory open\n");
  607.             }
  608.             else {
  609.                 rewinddir(dir);
  610.                 printf("directory rewind completed\n");
  611.             }
  612.         }
  613.         else if (streqi("seek", token)) {
  614.             if (!dir) {
  615.                 printf("no directory open\n");
  616.             }
  617.             else if (!(token = strtok(NULL, "\n"))) {
  618.                 printf("specify an entry number\n");
  619.             }
  620.             else {
  621.                 seekdir(dir, atol(token));
  622.                 printf("seek completed\n");
  623.             }
  624.         }
  625.         else if (streqi("read", token)) {
  626.             if (!dir) {
  627.                 printf("no directory open\n");
  628.             }
  629.             else if (!(dp = readdir(dir))) {
  630.                 perror("couldn't read entry");
  631.             }
  632.             else {
  633.                 printf("ino=%2ld reclen=%2d namlen=%2d name=%s\n",
  634.                     dp->d_ino, dp->d_reclen, dp->d_namlen,
  635.                     dp->d_name);
  636.             }
  637.         }
  638.         else if (streqi("tell", token)) {
  639.             if (!dir) {
  640.                 printf("no directory open\n");
  641.             }
  642.             else {
  643.                 printf("positioned at %ld\n", telldir(dir));
  644.             }
  645.         }
  646.         else if (streqi("dir", token)) {
  647.             if (!dir) {
  648.                 printf("no directory open\n");
  649.             }
  650.             else {
  651.                 while (dp = readdir(dir)) {
  652.                     printf("%-16.16s", dp->d_name);
  653.                 }
  654.                 putchar('\n');
  655.             }
  656.         }
  657.         else if (streqi("cd", token)) {
  658.             if (!(token = strtok(NULL, "\n"))) {
  659.                 printf("specify a new directory\n");
  660.             }
  661.             else if (chdir(token) < 0) {
  662.                 perror(token);
  663.             }
  664.             else {
  665.                 char buff[128];
  666.                 printf("new directory is %s\n", getcwd(buff, sizeof(buff)));
  667.             }
  668.         }
  669.         else if (streqi("quit", token) || streqi("exit", token)) {
  670.             break;
  671.         }
  672.         else {
  673.             printf("unknown command: %s\n", token);
  674.         }
  675.     }
  676. }
  677.