home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / sosutl11.zip / sostree.c < prev    next >
C/C++ Source or Header  |  1993-08-04  |  13KB  |  410 lines

  1. /* ---------
  2.  * sostree.c (C) SuperOscar Softwares, Tommi Nieminen 1993.
  3.  * ---------
  4.  * SOS Tree directory tree drawer for OS/2.
  5.  * Programming language: Borland C++ for OS/2 1.0.
  6.  * Programmer: Tommi Nieminen.
  7.  *
  8.  * 4-Jul-1993   v1.0: first PD version ready.
  9.  *
  10.  */
  11.  
  12. #include <stdio.h>              /* Standard I/O */
  13. #include <stdarg.h>             /* va_ functions */
  14. #include <dir.h>                /* findfirst(), findnext(), chdir() */
  15. #include <string.h>             /* String handling */
  16. #include <alloc.h>              /* malloc(), free() */
  17. #include <dos.h>                /* Symbolic names for file attributes */
  18. #include <ctype.h>              /* toupper() */
  19. #define INCL_DOSNLS
  20. #include <os2.h>                /* Load NLS support */
  21.  
  22.   /* Official program name string for messages */
  23. const char *program_name = "SOS Tree v1.0";
  24. const char *copyright = "(C) SuperOscar Softwares, Tommi Nieminen 1993";
  25.  
  26.   /* Program flags */
  27. #define F_SHOW_TREE         0x0001
  28. #define F_SHOW_FILES        0x0002
  29. #define F_SHOW_DATES        0x0004
  30. #define F_SHOW_SIZES        0x0008
  31. #define F_SHOW_ATTRS        0x0010
  32. #define F_SHOW_HIDDEN       0x0020
  33. #define F_QUIET             0x0040
  34. #define F_SHOW_HELP         0x8000
  35.  
  36.   /* Error codes */
  37. typedef enum {
  38.     NO_ERROR,
  39.     ERR_PATH_NOT_FOUND,
  40.     ERR_UNKNOWN_SWITCH,
  41.     ERR_CMDLINE_SYNTAX,
  42.     ERR_INTERNAL
  43. } ERROR;
  44.  
  45.   /* Error messages */
  46. const char *errormsg[] = {
  47. #if defined(LANG_SUOMI)
  48.     "Polkua \"%s\" ei löydy",
  49.     "Tuntematon valitsin \"-%c\"",
  50.     "Virheellinen komentorivi (\"-?\" antaa apua)",
  51.     "Sisäinen ohjelmavirhe"
  52. #else
  53.     "Path not found \"%s\"",
  54.     "Unknown switch \"-%c\"",
  55.     "Invalid command line syntax (use \"-?\" to get help)",
  56.     "Program internal error"
  57. #endif
  58. };
  59.  
  60. #if defined(LANG_SUOMI)
  61. const char *header_line = "Hakemistopuu \"%s\"\n";
  62. #else
  63. const char *header_line = "Directory tree of \"%s\"\n";
  64. #endif
  65.  
  66.   /* Global variables */
  67. ERROR status = NO_ERROR;                /* Global error status */
  68. int datefmt, timefmt;                   /* Date and time formats */
  69. char datesep, timesep;                  /* Date and time separators */
  70. unsigned flags = F_SHOW_TREE;           /* Program flags */
  71.  
  72.   /* Macroes */
  73. #define ISDIR(f)        (f.ff_attrib & FA_DIREC)
  74. #define ISDOTDIR(f)     (!strcmp(f.ff_name, ".") || !strcmp(f.ff_name, ".."))
  75. #define D_DAY(date)     ((date) & 31)
  76. #define D_MON(date)     (((date) & 480) / 32)
  77. #define D_YEAR(date)    (((date) & 65024) / 512 + 80)
  78. #define T_MIN(time)     (((time) & 2016) / 32)
  79. #define T_HOUR(time)    (((time) & 63488) / 2048)
  80.  
  81.   /* Function declarations */
  82. char *cmdline_parse(int argc, char *argv[]);
  83. int dir_exists(char *pathname);
  84. void country_get(void);
  85. void tree_draw(const char *mother);
  86. char *path_cat(const char *part1, const char *part2);
  87. char *date_format(unsigned short filedate);
  88. char *time_format(unsigned short filetime);
  89. void error_raise(ERROR err, ...);
  90. void help(void);
  91.  
  92. int main(int argc, char *argv[])
  93. {
  94.     char *base;
  95.  
  96.     country_get();
  97.     base = cmdline_parse(argc, argv);
  98.  
  99.     if (flags & F_SHOW_HELP)
  100.         help();
  101.     else if (!status)
  102.         if (!dir_exists(base))
  103.             error_raise(ERR_PATH_NOT_FOUND, base);
  104.         else {
  105.             if (!(flags & F_QUIET))
  106.                 printf(header_line, base);
  107.             tree_draw(base);
  108.         }
  109.     free(base);
  110.  
  111.     return(status);
  112. }
  113.  
  114. char *cmdline_parse(int argc, char *argv[])
  115.   /* Parse command line. Return base directory name. */
  116. {
  117.     char *dirname;
  118.     int ind = 1;
  119.  
  120.     dirname = (char *) malloc(sizeof(char) * (MAXPATH + 1));
  121.     strcpy(dirname, ".");
  122.  
  123.     if (-- argc) {
  124.         char *p;
  125.  
  126.         if (argv[1][0] == '-') {
  127.             ind = 2;
  128.             for (p = &argv[1][1]; *p; p ++)
  129.                 switch (*p) {
  130.                     case 'a':   flags |= F_SHOW_ATTRS;
  131.                                 break;
  132.                     case 'd':   flags |= F_SHOW_DATES;
  133.                                 break;
  134.                     case 'f':   flags |= F_SHOW_FILES;
  135.                                 break;
  136.                     case 'h':   flags |= F_SHOW_HIDDEN;
  137.                                 break;
  138.                     case 'n':   flags &= ~F_SHOW_TREE;
  139.                                 break;
  140.                     case 'q':   flags |= F_QUIET;
  141.                                 break;
  142.                     case 'z':   flags |= F_SHOW_SIZES;
  143.                                 break;
  144.                     case '?':   flags |= F_SHOW_HELP;
  145.                                 break;
  146.                     default:    error_raise(ERR_UNKNOWN_SWITCH, *p);
  147.                 }
  148.         }
  149.         if (argc == ind) {
  150.             strncpy(dirname, argv[ind], MAXPATH);
  151.             dirname[MAXPATH] = '\0';
  152.         }
  153.         else if (argc > ind)
  154.             error_raise(ERR_CMDLINE_SYNTAX);
  155.      }
  156.  
  157.     return(dirname);
  158. }
  159.  
  160. int dir_exists(char *pathname)
  161.   /* Check if 'fname' is a directory. Return NULL if it isn't, and
  162.    * absolute path name if it is.
  163.    */
  164. {
  165.     char current_path[MAXDIR + 1],
  166.         drivestr[MAXDRIVE + 1];
  167.     int drive = 0,
  168.         current_drive,
  169.         exists = 0;
  170.  
  171.     /* Note: 0 means A: drive in getdisk() and setdisk(), but `default'
  172.      * drive in getcurdir() (where A: is 1).
  173.      */
  174.  
  175.       /* Does 'fname' contain a drive letter? */
  176.     fnsplit(pathname, drivestr, NULL, NULL, NULL);
  177.     drive = (*drivestr) ? toupper(*drivestr) - '@' : 0;
  178.  
  179.       /* Store current drive, and current directory on drive 'drive'. */
  180.     current_drive = getdisk();
  181.     getcurdir(drive, current_path);
  182.  
  183.       /* Try to cd to 'fname' on drive 'drive'. */
  184.     if (drive)
  185.         setdisk(drive - 1);
  186.     if (!chdir(pathname)) {
  187.         char tmp[MAXDIR + 1];
  188.  
  189.         getcurdir(drive, tmp);
  190.         strcpy(pathname, " :\\");
  191.         pathname[0] = (char) ((drive) ? drive + '@' : current_drive + 'A');
  192.         strcat(pathname, tmp);
  193.         exists = 1;
  194.     }
  195.  
  196.       /* Restore directory if changed. */
  197.     chdir(current_path);
  198.     if (drive)
  199.         setdisk(current_drive);
  200.  
  201.     return(exists);
  202. }
  203.  
  204. void country_get(void)
  205.   /* Query current system date and time formats and separators. */
  206. {
  207.     ULONG len;
  208.     COUNTRYCODE query;
  209.     COUNTRYINFO info;
  210.     APIRET rc;
  211.  
  212.     query.country = 0;
  213.     query.codepage = 850;
  214.     rc = DosQueryCtryInfo(sizeof(info), &query, &info, &len);
  215.     if (!rc) {
  216.         datesep = *(info.szDateSeparator);
  217.         timesep = *(info.szTimeSeparator);
  218.         datefmt = info.fsDateFmt;
  219.         timefmt = info.fsTimeFmt;
  220.     }
  221.     else
  222.         error_raise(ERR_INTERNAL);
  223. }
  224.  
  225. void tree_draw(const char *mother)
  226.   /* Recurse to all subdirectories of 'mother'. */
  227. {
  228.     static char sequel_buffer[MAXPATH] = "";
  229.     char *search, *fullname;
  230.     struct ffblk this, newest;
  231.     int ok;
  232.  
  233.     search = path_cat(mother, "*");
  234.  
  235.     ok = findfirst(search, &newest, (flags & F_SHOW_HIDDEN) ?
  236.         FA_DIREC | FA_HIDDEN | FA_SYSTEM : FA_DIREC);
  237.     while (!ok) {
  238.         this = newest;
  239.         fullname = path_cat(mother, this.ff_name);
  240.  
  241.           /* First check whether this directory is the last on its
  242.            * level or not.
  243.            */
  244.         if (flags & F_SHOW_FILES)
  245.             ok = findnext(&newest);
  246.         else {
  247.               /* When not displaying ordinary files, more work is needed
  248.                * because findnext() always finds files too: we have to
  249.                * loop until we find a directory or run out of files.
  250.                */
  251.             ok = findnext(&newest);
  252.             while (!ISDIR(newest) && !ok)
  253.                 ok = findnext(&newest);
  254.         }
  255.  
  256.           /* Discard "." and ".." */
  257.         if (!ISDOTDIR(this)) {
  258.               /* If 'this' is not a directory and F_SHOW_FILES flag isn't
  259.                * set, start next cycle
  260.                */
  261.             if (!ISDIR(this) && !(flags & F_SHOW_FILES))
  262.                 continue;
  263.  
  264.             if (flags & F_SHOW_DATES)
  265.                 printf("%s  %s  ", date_format(this.ff_fdate),
  266.                     time_format(this.ff_ftime));
  267.  
  268.             if (flags & F_SHOW_SIZES)
  269.                 printf("%8lu  ", this.ff_fsize);
  270.  
  271.             if (flags & F_SHOW_ATTRS)
  272.                 printf("%c%c%c%c%c  ",
  273.                     (this.ff_attrib & FA_ARCH) ? 'A' : '-',
  274.                     (this.ff_attrib & FA_HIDDEN) ? 'H' : '-',
  275.                     (this.ff_attrib & FA_RDONLY) ? 'R' : '-',
  276.                     (this.ff_attrib & FA_SYSTEM) ? 'S' : '-',
  277.                     (this.ff_attrib & FA_DIREC) ? 'D' : '-');
  278.  
  279.             if (flags & F_SHOW_TREE)
  280.                 printf("%s%c──%s\n", sequel_buffer, (ok) ? '└' : '├',
  281.                     this.ff_name);
  282.             else
  283.                 printf("%s\n", fullname);
  284.  
  285.               /* If 'this' is a directory, try to recurse to sub-
  286.                * directories.
  287.                */
  288.             if (ISDIR(this)) {
  289.                   /* Update upper-level sequel buffer. */
  290.                 strcat(sequel_buffer, (ok) ? "   " : "│  ");
  291.  
  292.                 tree_draw(fullname);
  293.  
  294.                   /* Truncate sequel buffer to its previous value. */
  295.                 sequel_buffer[strlen(sequel_buffer) - 3] = '\0';
  296.             }
  297.         }
  298.         free(fullname);
  299.     }
  300.     free(search);
  301. }
  302.  
  303. char *path_cat(const char *part1, const char *part2)
  304.   /* Combine two path name components into one. Add a `\' between
  305.    * the parts if necessary.
  306.    */
  307. {
  308.     char *newpath;
  309.  
  310.     newpath = (char *) malloc(sizeof(char) * (MAXPATH + 1));
  311.     strcpy(newpath, part1);
  312.     if (part1[strlen(part1) - 1] != '\\' && *part2 != '\\')
  313.         strcat(newpath, "\\");
  314.     strcat(newpath, part2);
  315.  
  316.     return(newpath);
  317. }
  318.  
  319. char *date_format(unsigned short filedate)
  320.   /* Format file date to a string of correct national format. */
  321. {
  322.     static char outstr[11];
  323.     unsigned day, mon, year;
  324.  
  325.     day = D_DAY(filedate);
  326.     mon = D_MON(filedate);
  327.     year = D_YEAR(filedate);
  328.  
  329.     switch (datefmt) {
  330.         case 0: case 1:
  331.             sprintf(outstr, "%2d%c%2d%c%02d", (datefmt == 0) ? mon : day,
  332.                 datesep, (datefmt == 0) ? day : mon, datesep, year);
  333.             break;
  334.         case 2:
  335.             sprintf(outstr, "%02d%c%02d%c%02d", year, datesep, mon, datesep,
  336.                 day);
  337.             break;
  338.     }
  339.  
  340.     return(outstr);
  341. }
  342.  
  343. char *time_format(unsigned short filetime)
  344.   /* Format file time to a string of correct national format. */
  345. {
  346.     static char outstr[8];
  347.     unsigned hour, min;
  348.  
  349.     hour = T_HOUR(filetime);
  350.     if (!hour)
  351.         hour = 24;
  352.     min = T_MIN(filetime);
  353.  
  354.     switch (timefmt) {
  355.         case 0:
  356.             sprintf(outstr, "%2d%c%02d%s", (hour <= 12) ? hour : hour - 12,
  357.                 timesep, min, (hour <= 12) ? "am" : "pm");
  358.             break;
  359.         case 1:
  360.             sprintf(outstr, "%2d%c%02d", hour, timesep, min);
  361.             break;
  362.     }
  363.  
  364.     return(outstr);
  365. }
  366.  
  367. void error_raise(ERROR err, ...)
  368. {
  369.     va_list argv;
  370.  
  371.     fprintf(stderr, "%s: ", program_name);
  372.     va_start(argv, err);
  373.     vfprintf(stderr, errormsg[err - 1], argv);
  374.     va_end(argv);
  375.     fprintf(stderr, "\n");
  376.  
  377.     status = err;
  378. }
  379.  
  380. void help(void)
  381.   /* Display help text. */
  382. {
  383.     printf("%s %s\n\n", program_name, copyright);
  384. #if defined(LANG_SUOMI)
  385.     puts("Näytä hakemistorakenne kaaviona tai luettelona. Käyttö:");
  386.     puts("\n    [D:\\] sostree [ -adfhnqz | -? ] [ HAK ]\n");
  387.     puts("Valitsimien merkitykset ovat seuraavat:");
  388.     puts("    -a  Näytä myös tiedostomääreet.");
  389.     puts("    -d  Näytä myös päiväykset ja ajat.");
  390.     puts("    -f  Näytä myös tiedostot (lehtinä).");
  391.     puts("    -h  Näytä myös piilotetut ja järjestelmätiedostot.");
  392.     puts("    -n  Näytä täydet polkunimet puukaaviotta.");
  393.     puts("    -q  Älä näytä \"Hakemistopuu ...\" -tunnisteriviä.");
  394.     puts("    -z  Näytä myös tiedostojen koot.\n");
  395.     puts("    -?  Näytä tämä avustusnäyttö.");
  396. #else
  397.     puts("Display directory tree with or without graphics. Usage:");
  398.     puts("\n    [D:\\] sostree [ -adfhnqz | -? ] [ DIR ]\n");
  399.     puts("The meanings of the switches are the following:");
  400.     puts("    -a  Show file attributes too.");
  401.     puts("    -d  Show file dates and times too.");
  402.     puts("    -f  Show files also (as leaves).");
  403.     puts("    -h  Show hidden and system files too.");
  404.     puts("    -n  Show full path names without graphics.");
  405.     puts("    -q  Do not show \"Directory tree of ...\" header.");
  406.     puts("    -z  Show file sizes too.\n");
  407.     puts("    -?  Show this help screen.");
  408. #endif
  409. }
  410.