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