home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / files / telecomm / nhclb120 / dirutil.c < prev    next >
C/C++ Source or Header  |  1993-09-26  |  16KB  |  693 lines

  1. /* dirutil.c - MS-DOS directory reading routines
  2.  *
  3.  * Bdale Garbee, N3EUA, Dave Trulli, NN2Z, and Phil Karn, KA9Q
  4.  * Directory sorting by Mike Chepponis, K3MC
  5.  * adapted for ATARI ST & cleaned up by Rob Janssen, PE1CHL
  6.  * adapted for Lattice C by Walter Doerr, DG2KK
  7.  */
  8.  
  9. #include <stdio.h>
  10. static    void dir_sort(), format_dir(),format_fname(), diskfree(), free_clist();
  11. static getdir_nosort();
  12. static getdir();
  13.  
  14. #ifndef GNUC
  15. #define Fsetdta(a) bdos(SET_DTA,a)
  16. #endif
  17.  
  18. #ifndef LATTICE
  19. #include <ctype.h>        /* DG2KK: force Lattice to use library func. */
  20. #endif
  21.  
  22. #if defined( LATTICE ) || ( defined(ATARI_ST) && defined(GNUC))
  23. #define    S_IJHID        2
  24. #define S_IJSYS        4
  25. #define S_IJDIR        0x10
  26. struct stat {
  27.     char st_mode;            /* that's all we need */
  28. };    
  29.  
  30. #else                        /* not LATTICE */
  31.  
  32. #ifdef __TURBOC__
  33. #include <dos.h>
  34. #include <sys\stat.h>
  35. #else                        /* not LATTICE and not TURBOC */
  36. #include <stat.h>
  37. #endif
  38. #endif
  39.  
  40. #ifdef MSDOS
  41. #define  IS_ERROR    == -1        /* error return from MSDOS */
  42. #endif
  43.  
  44. #ifdef __TURBOC__
  45. #define ST_RDONLY    0x01        /* Read only attribute */
  46. #define ST_HIDDEN    0x02        /* Hidden file */
  47. #define ST_SYSTEM    0x04        /* System file */
  48. #define ST_LABEL    0x08        /* Volume label */
  49. #define ST_DIRECT    0x10        /* Directory */
  50. #define ST_ARCH        0x20        /* Archive */
  51.  
  52. int dos(unsigned ah,
  53.     unsigned bx,
  54.     unsigned cx,
  55.     void *dx,
  56.     unsigned si,
  57.     unsigned di)
  58. {
  59.     union REGS regs;
  60.  
  61.     regs.h.ah = ah;
  62.     regs.x.bx = bx;
  63.     regs.x.cx = cx;
  64.     regs.x.dx = dx;
  65.     regs.x.si = si;
  66.     regs.x.di = di;
  67.  
  68.     intdos(®s, ®s);
  69.  
  70.     if (regs.x.cflag) return -1;
  71.     return 0;
  72. }
  73.  
  74. #define bdos(x, y)    bdos(x, (unsigned) y, 0)
  75.  
  76. #endif  /* TURBOC */
  77.  
  78.  
  79. #ifdef ATARI_ST
  80. #define  IS_ERROR    < 0        /* error return from gemdos */
  81. #include <osbind.h>            /* os interface defines */
  82. #define  bdos        gemdos        /* Atari OS call */
  83. #ifndef GNUC
  84. #define  dos(a,b,c,d,e,f) gemdos(a,d,c) /* only valid for FIND func */
  85. #else
  86. #define  dos(a,b,c,d,e,f) gnudos(a,d,c)
  87. short gnudos();
  88. #endif
  89.  
  90. #define  st_attr    st_mode     /* what's in a name? */
  91. #define  ST_HIDDEN    S_IJHID     /* Hidden from search */
  92. #define  ST_SYSTEM    S_IJSYS     /* System, hidden from search */
  93. #define  ST_DIRECT    S_IJDIR     /* Directory */
  94. #endif
  95.  
  96. #include "global.h"
  97.  
  98. #ifndef FALSE
  99. #define FALSE    (0)
  100. #endif
  101.  
  102. #ifndef TRUE
  103. #define TRUE    !(FALSE)
  104. #endif
  105.  
  106. #define REGFILE (ST_HIDDEN|ST_SYSTEM|ST_DIRECT)
  107. #define SET_DTA     0x1a
  108. #define FIND_FIRST    0x4e
  109. #define FIND_NEXT    0x4f
  110.  
  111. struct dirent {
  112.     char rsvd[21];
  113.     char attr;
  114.     short ftime;
  115.     short fdate;
  116.     long fsize;
  117.     char fname[13];
  118. };
  119. #define NULLENT (struct dirent *)0
  120.  
  121. struct dirsort {
  122.     struct dirsort *prev;
  123.     struct dirsort *next;
  124.     struct dirent *direntry;
  125. };
  126. #define NULLSORT (struct dirsort *)0
  127.  
  128. /* Create a directory listing in a temp file and return the resulting file
  129.  * descriptor. If full == 1, give a full listing; else return just a list
  130.  * of names.
  131.  */
  132. FILE *
  133. dir(path,full)
  134. char *path;
  135. int full;
  136. {
  137.     FILE *fp,*tmpfile();
  138.  
  139.     if ((fp = tmpfile()) != NULLFILE)
  140.     {
  141.         getdir(path,full,fp);
  142.         /* This should be rewind(), but Aztec doesn't have it */
  143. #if    (ATARI_ST && MWC)
  144.         vseek(fp,0L,0);
  145. #else
  146.         fseek(fp,0L,0);
  147. #endif
  148.     }
  149.     return fp;
  150. }
  151.  
  152. /* wildcard filename lookup */
  153. filedir(name,times,ret_str)
  154. char *name;
  155. int times;
  156. char *ret_str;
  157. {
  158.     register char *cp,*cp1;
  159.     static struct dirent sbuf;
  160.  
  161.     Fsetdta(&sbuf);    /* Set disk transfer address */
  162.  
  163.     /* Find matching file */
  164.     if(dos(times == 0 ? FIND_FIRST:FIND_NEXT,0,REGFILE,name,0,0) IS_ERROR) 
  165.         sbuf.fname[0] = '\0';
  166.  
  167.     /* Copy result to output, forcing to lower case */
  168.     for(cp = ret_str,cp1 = sbuf.fname; cp1 < &sbuf.fname[13] && *cp1 != '\0';)
  169.         *cp++ = (char)tolower(*cp1++);    /* DG2KK: added (char) */
  170.     *cp = '\0';
  171. }
  172.  
  173. /* Change working directory */
  174. docd(argc,argv)
  175. int argc;
  176. char *argv[];
  177. {
  178.     char dirname[128];
  179. #ifdef MSDOS
  180.     char *getcwd();
  181. #endif
  182.  
  183.     if(argc > 1){
  184.         if(chdir(argv[1]) IS_ERROR){
  185.             printf("Can't change directory\n");
  186.             return 1;
  187.         }
  188.     }
  189.  
  190. #ifdef MSDOS
  191.     if(getcwd(dirname,0) != NULLCHAR){
  192.         printf("\\%s\n",dirname);
  193.     }
  194. #endif
  195. #ifdef ATARI_ST
  196.     if (Dgetpath(dirname,0) == 0){
  197.         printf("%c:%s%s\n",(char) Dgetdrv()+'A',
  198.             (*dirname? "" : "\\"),dirname);
  199.     }
  200. #endif
  201.     return 0;
  202. }
  203.  
  204. /* List directory to console. [-/]w option selects "wide" format */
  205. dodir(argc,argv)
  206. int argc;
  207. char *argv[];
  208. {
  209.     char *path;
  210.     int full = 1;
  211.  
  212.     if (argc > 1 &&
  213.         (argv[1][0] == '-' || argv[1][0] == '/') && argv[1][1] == 'w')
  214.     {
  215.         full = -1;
  216.         argv++;
  217.         argc--;
  218.     }
  219.  
  220.     if(argc >= 2){
  221.         path = argv[1];
  222.     } else {
  223.         path = "*.*";
  224.     }
  225.     getdir(path,full,stdout);
  226.     return 0;
  227. }
  228.  
  229. /* do a directory list to the stream 
  230.  * full = 0 -> short form, 1 is long, -1 is multi-column short
  231. */
  232. static
  233. getdir(path,full,file)
  234. char *path;
  235. int full;
  236. FILE *file;
  237. {
  238.     struct dirent sbuf;
  239.     struct stat statbuf;
  240.     char *cp,*cp1;                /* !!!!!!! was: register */
  241.     char dirtmp[20];
  242.     int command = FIND_FIRST;
  243.     int i = 0;
  244.     int cflag = 0;
  245.     int n = 0;
  246.     char line_buf[50];        /* for long dirlist */
  247.     
  248.     struct dirsort *head, *here, *new;
  249.     struct dirent *de;
  250.  
  251.     int malloc_lost = FALSE;
  252.  
  253.     /* Root directory is a special case */
  254.     if(path == NULLCHAR || *path == '\0' || strcmp(path,"\\") == 0)
  255.         path = "\\*.*";
  256.  
  257.     /* If arg is a directory, append "\*.*" to it.
  258.      * This is tricky, since the "stat" system call actually
  259.      * calls the DOS "find matching file" function. The stat
  260.      * call therefore returns the attributes for the first matching
  261.      * entry in the directory. If the arg already ends in *.*,
  262.      * stat will match the . entry in the directory and indicate
  263.      * that the argument is a valid directory name. Hence the
  264.      * heuristic check for '*' in the file name. Kludge...
  265.      */
  266. #ifdef __TURBOC__
  267.     else if(stat(path,&statbuf) != -1
  268.      && (statbuf.st_mode & S_IFDIR)
  269.      && index(path,'*') == NULLCHAR) {
  270. #else
  271.     else if(index(path,'*') == NULLCHAR
  272.      && stat(path,&statbuf) != -1
  273.      && (statbuf.st_attr & ST_DIRECT)) {
  274. #endif
  275.         if((cp = malloc(strlen(path) + 10)) == NULLCHAR)
  276.             return -1;
  277.         sprintf(cp,"%s%c%s",path,'\\',"*.*");
  278.         path = cp;
  279.         cflag = 1;
  280.     }
  281.     head = NULLSORT;    /* No head of chain yet... */
  282.     for(;;){
  283.         Fsetdta(&sbuf);    /* Set disk transfer address */
  284.         if(dos(command, 0, REGFILE, path, 0, 0) IS_ERROR)
  285.             break;
  286.  
  287.         command = FIND_NEXT;    /* Got first one already... */
  288.         if(sbuf.fname[0] != '.'){
  289.             /* nuke "." and ".." */
  290.             n++;    /* One more entry */
  291.             new = (struct dirsort *) malloc(sizeof(struct dirsort));
  292.             if(new == NULLSORT)
  293.                 malloc_lost = TRUE;
  294.             de  = (struct dirent *)malloc(sizeof(struct dirent));
  295.             if(de == NULLENT)
  296.                 malloc_lost = TRUE;
  297.             if(malloc_lost){
  298.                 /* Clean up and call other routine */
  299.                 if(new)free(new);
  300.                 free_clist(head);
  301.                 return getdir_nosort(path,full,file);
  302.             }
  303.             *de = sbuf;    /* Copy contents of directory entry struct */
  304.  
  305.             /* Fix up names for easier sorting... pain! */
  306.             strcpy(de->fname,"           ");    /* 11 blanks */
  307.             cp  = sbuf.fname;
  308.             cp1 = de->fname;
  309.  
  310.             do *cp1++ = *cp++; while (*cp && *cp != '.');
  311.  
  312.             if(*cp++){
  313.                 /* If there is an extension */
  314.                 cp1 = &(de->fname[8]);
  315.                 do *cp1++ = *cp++; while (*cp);
  316.             }
  317.             if(!(int)head){
  318.                 /* Make the first one */
  319.                 here = head = new;
  320.                 head->prev = head->next = NULLSORT;
  321.             } else {
  322.                 /* Link on next one */
  323.                 new->next = NULLSORT;
  324.                 new->prev = here;
  325.                 here->next = new;
  326.                 here = new;
  327.             }
  328.             new->direntry = de;
  329.         } /* IF on "." */
  330.     } /* infinite FOR loop */
  331.  
  332.     if(head)
  333.         dir_sort(head);     /* Make a nice, sorted list */
  334.  
  335.     here = head;
  336.     if(here)
  337.         if(full > 0){
  338.         do {
  339.             format_dir(line_buf,here->direntry);
  340.             fprintf(file,"%s%s",line_buf,(i^=1) ? "   " : "\r\n");
  341.         } while (here = here->next);
  342.         if(i & 1)
  343.             fprintf(file,"\r\n");
  344.         }
  345.         else {
  346.         /* This is the short form */
  347.         do {
  348.             format_fname(dirtmp,here->direntry->fname,
  349.                         here->direntry->attr);
  350.             fprintf(file,"%-15s%s",dirtmp,((full && ++i % 5)?"":"\r\n"));
  351.         } while (here = here->next);
  352.         if(full && i % 5)
  353.             fprintf(file,"\r\n");
  354.         }
  355.  
  356.     /* Give back all the memory we temporarily needed... */
  357.     free_clist(head);
  358.  
  359.     if(full > 0){
  360.         /* Provide additional information only on DIR */
  361.  
  362.         if (isalpha(*path) && path[1] == ':')    /* detect A: drivespec */
  363.             diskfree(file,*path & 0x1f,n);
  364.         else
  365.             diskfree(file,0,n);
  366.     }
  367.     if(cflag)
  368.         free(path);
  369.     return 0;
  370. } /* getdir */
  371.  
  372. static
  373. getdir_nosort(path,full,file)
  374. char *path;
  375. int full;
  376. FILE *file;
  377. {
  378.     struct dirent sbuf;
  379.     struct stat statbuf;
  380.     register char *cp;
  381.     char dirtmp[20];
  382.     int command = FIND_FIRST;
  383.     int i = 0;
  384.     int cflag = 0;
  385.  
  386.     char    line_buf[50];        /* for long dirlist */
  387.     
  388.     void format_fname(),format_dir(),diskfree();
  389.     int n = 0;    /* Number of directory entries */
  390.     
  391.     /* Root directory is a special case */
  392.     if(path == NULLCHAR || *path == '\0' || strcmp(path,"\\") == 0)
  393.         path = "\\*.*";
  394.  
  395.     /* If arg is a directory, append "\*.*" to it.
  396.      * This is tricky, since the "stat" system call actually
  397.      * calls the DOS "find matching file" function. The stat
  398.      * call therefore returns the attributes for the first matching
  399.      * entry in the directory. If the arg already ends in *.*,
  400.      * stat will match the . entry in the directory and indicate
  401.      * that the argument is a valid directory name. Hence the
  402.      * heuristic check for '*' in the file name. Kludge...
  403.      */
  404. #ifdef __TURBOC__
  405.     else if(stat(path,&statbuf) != -1
  406.      && (statbuf.st_mode & S_IFDIR)
  407.      && index(path,'*') == NULLCHAR) {
  408. #else
  409.     else if(index(path,'*') == NULLCHAR
  410.      && stat(path,&statbuf) != -1
  411.      && (statbuf.st_attr & ST_DIRECT)) {
  412. #endif
  413.         if((cp = malloc(strlen(path) + 10)) == NULLCHAR)
  414.             return -1;
  415.         sprintf(cp,"%s%c%s",path,'\\',"*.*");
  416.         path = cp;
  417.         cflag = 1;
  418.     }
  419.     for(;;){
  420.         Fsetdta(&sbuf);    /* Set disk transfer address */
  421.         if(dos(command,0,REGFILE,path,0,0) IS_ERROR)
  422.             break;
  423.  
  424.         command = FIND_NEXT;    /* Got first one already... */
  425.         if(sbuf.fname[0] != '.'){    /* nuke "." and ".." */
  426.             if(full > 0){
  427.                 n++;    /* Count 'em */
  428.                 format_dir(line_buf,&sbuf);
  429.                 fprintf(file,"%s%s",line_buf,(i^=1) ? "   " : "\n");
  430.             } else    {    /* is short form */
  431.                 format_fname(dirtmp,sbuf.fname,sbuf.attr);
  432.                 fprintf(file,"%-15s%s",dirtmp,(full && (++i % 5)?"":"\n"));
  433.             }
  434.         }
  435.     }
  436.     if(full > 0){
  437.         if(i)
  438.             fprintf(file,"\n");
  439.  
  440.         if (isalpha(*path) && path[1] == ':')    /* detect A: drivespec */
  441.             diskfree(file,*path & 0x1f,n);
  442.         else
  443.             diskfree(file,0,n);
  444.     }
  445.     else
  446.         if(i % 5)
  447.             fprintf(file,"\n");
  448.  
  449.     if(cflag)
  450.         free(path);
  451.     return 0;
  452. }
  453.  
  454. static
  455. void
  456. diskfree (file,drv,nfiles)
  457. FILE *file;
  458. int drv;
  459. int nfiles;
  460.  
  461. {
  462. #ifdef MSDOS
  463.     unsigned short ax,bx,cx,dx;
  464.     void isfree();
  465. #endif
  466. #ifdef ATARI_ST
  467.     struct { unsigned long di_free,di_many,di_ssize,di_spau; } disk_info;
  468. #endif
  469.     unsigned long free_bytes = 0, total_bytes = 0;
  470.     char s_free[11], s_total[11];
  471.     void commas();
  472.  
  473.     fflush(stdout);         /* function takes a short while... */
  474.  
  475. #ifdef MSDOS
  476.     /* Provide additional information  */
  477.     ax = 0x3600;    /* AH = 36h, AL = 0 (AL not used) */
  478.     dx = drv;    /* Default drive */
  479.     isfree(&ax,&bx,&cx,&dx);
  480.  
  481.     if (ax != 0xffff)
  482.     {
  483.         free_bytes  = (unsigned long)ax * (unsigned long)cx;
  484.         total_bytes = free_bytes * (unsigned long)dx;
  485.         free_bytes *= (unsigned long)bx;
  486.     }
  487. #endif
  488. #ifdef ATARI_ST
  489.     if (Dfree(&disk_info,drv) == 0)
  490.     {
  491.         free_bytes  = disk_info.di_spau * disk_info.di_ssize;
  492.         total_bytes = free_bytes * disk_info.di_many;
  493.         free_bytes *= disk_info.di_free;
  494.     }
  495. #endif
  496.  
  497.     sprintf(s_free,"%ld",free_bytes);    commas(s_free);
  498.     sprintf(s_total,"%ld",total_bytes);    commas(s_total);
  499.  
  500.     if(nfiles)
  501.         fprintf(file,"%d",nfiles);
  502.     else
  503.         fprintf(file,"No");
  504.  
  505.     fprintf(file," file%s. %s bytes free. Disk size %s bytes.\r\n",
  506.               (nfiles==1? "":"s"),s_free,s_total);
  507. }
  508.  
  509.  
  510. /*
  511.  * Return a string with commas every 3 positions.
  512.  * If malloc() fails, return original string unmodified.
  513.  * else the original string is replace with the string with commas.
  514.  *
  515.  * The caller must be sure that there is enough room for the resultant
  516.  * string.
  517.  *
  518.  *
  519.  * k3mc 4 Dec 87
  520.  * pe1chl 3 Feb 88     europeans would use periods...
  521.  */
  522.  
  523. #ifndef THSEP
  524. #define THSEP ','
  525. #endif
  526. void
  527. commas(dest)
  528. char *dest;
  529. {
  530.     char *src, *core;    /* Place holder for malloc */
  531.     unsigned cc;        /* The comma counter */
  532.     unsigned len;
  533.     
  534.     len = strlen(dest);
  535.     if( (core = src = (char *)malloc(len+1)) == NULLCHAR)
  536.         return;
  537.  
  538.     strcpy(src,dest);    /* Make a copy, so we can muck around */
  539.     cc = (len-1)%3 + 1;    /* Tells us when to insert a comma */
  540.     
  541.     while(*src != '\0'){
  542.         *dest++ = *src++;
  543.         if( ((--cc) == 0) && *src ){
  544.             *dest++ = THSEP; cc = 3;
  545.         }
  546.     }
  547.     free(core);
  548.     *dest = '\0';
  549. }
  550. /*
  551.  * This insertion sort adapted from "Writing Efficient Programs" by Jon Louis
  552.  * Bentley, Prentice-Hall 1982, ISBN 0-13-070244-X (paperback) p. 65
  553.  *
  554.  * Run Time (sec) = K * N^2, where K = 21e-6 on my turbo XT clone (SI=2.6).
  555.  * This could be improved to perhaps K * N * log2(N) using Quicksort, but,
  556.  * as Bentley points out, this insertion sort is actually faster for small
  557.  * values of N.  His "best" sorting algorithm uses an insertion sort/Quicksort
  558.  * hybrid, with the "cutoff" value being about 30 elements.
  559.  *
  560.  * I have opted for the straight insertion sort because it is quite simple,
  561.  * provably correct, and not a terrible performer when N < 1000, which is the
  562.  * case for most directories.
  563.  */
  564. static
  565. void
  566. dir_sort(head)
  567. struct dirsort *head;
  568. {
  569.     struct dirsort *here, *backtrack;
  570.     struct dirent *de_temp;
  571.     
  572.     for(here = head->next; here != NULLSORT; here = here->next){
  573.         backtrack = here;
  574.         de_temp = here->direntry;
  575.         while(backtrack->prev
  576.          && strcmp(de_temp->fname,backtrack->prev->direntry->fname)<0){
  577.             backtrack->direntry = backtrack->prev->direntry;
  578.             backtrack = backtrack->prev;
  579.         }
  580.         backtrack->direntry = de_temp;
  581.     }
  582. }
  583.  
  584. static
  585. void
  586. format_dir (line_buf,sbuf)
  587.     char *line_buf;
  588.     struct dirent *sbuf;
  589.  
  590. {
  591.     char dirtmp[20];
  592.     char cbuf[20],cbuf1[20];
  593.  
  594.     format_fname(dirtmp,sbuf->fname,sbuf->attr);
  595.  
  596.     sprintf(line_buf,"%-13s",dirtmp);
  597.     if(sbuf->attr & ST_DIRECT)
  598.         strcat(line_buf,"           ");/* 11 spaces */
  599.     else {
  600.         sprintf(cbuf,"%ld",sbuf->fsize);
  601.         commas(cbuf);
  602.         sprintf(cbuf1,"%10s ",cbuf);
  603.         strcat(line_buf,cbuf1); /* Do filesize */
  604.     }
  605.  
  606.     sprintf(cbuf,"%2d:%02d %2d/%02d/%02d",
  607.       (sbuf->ftime >> 11) & 0x1f,     /* hour */
  608.       (sbuf->ftime >> 5) & 0x3f,     /* minute */
  609.       (sbuf->fdate >> 5) & 0xf,     /* month */
  610.       (sbuf->fdate ) & 0x1f,     /* day */
  611.       (sbuf->fdate >> 9) + 80);     /* year */
  612.  
  613.     strcat(line_buf,cbuf);
  614. }
  615.  
  616. static
  617. void
  618. format_fname(dest,src,attr)
  619. char    *dest, *src;
  620. char    attr;
  621. {
  622.     char    *cp = src+8;
  623.     int    loop_counter;
  624.  
  625.     for(loop_counter=0; loop_counter<8; loop_counter++){
  626.         *dest++ = (char)tolower(*src++);    /* DG2KK:  (char) */
  627.         if(*src == ' ')break;
  628.     }
  629.     if(cp[0] != ' ' || cp[1] != ' ' || cp[2] != ' '){ /* There is an extension */
  630.         *dest++ = '.';
  631.         for(loop_counter=0; loop_counter<3; loop_counter++){
  632.         *dest++ = (char)tolower(*cp++);        /* DG2KK:  (char) */
  633.             if(*cp == ' ')break;
  634.         }
  635.     }
  636.     if(attr & ST_DIRECT)*dest++ = '\\';
  637.     *dest = '\0';
  638. }
  639.  
  640. static
  641. void
  642. free_clist(head)
  643. struct dirsort *head;
  644. {
  645.     struct dirsort *here;
  646.     
  647.     here = head;
  648.     if(here)do{
  649.         free(here->direntry);
  650.         if(head != here){
  651.             free(head);
  652.             head = here;
  653.         }
  654.     } while (here = here->next);
  655.     if(head != here){
  656.         free(head);
  657.         head = here;
  658.     }
  659. }
  660.  
  661. #ifdef LATTICE        /* DG2KK: Lattice doesn't know a thing about stat */
  662. int
  663. stat(name,buf)
  664. char *name;
  665. struct stat *buf;
  666. {
  667.     static struct dirent sbuf;
  668.     static struct stat statbuf;
  669.     int error;
  670.  
  671.     gemdos(SET_DTA, &sbuf);
  672.  
  673.     error = gemdos(FIND_FIRST, name, REGFILE);    /* look for directories */
  674.     if (error)
  675.         return(-1);            /* caller insists on -1 */
  676.  
  677.     statbuf.st_attr = sbuf.attr;
  678.     return 0;
  679. }
  680. #endif
  681.  
  682. #ifdef GNUC
  683. short gnudos(a,d,c)
  684. short a;
  685. long d;
  686. short c;
  687. {
  688.   if(a==FIND_FIRST) return(Fsfirst(d,c));
  689.   else
  690.     return (Fsnext());
  691. }
  692. #endif /* GNUC */
  693.