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