Subversion Repositories SvarDOS

Rev

Rev 2064 | Rev 2066 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2019 mateusz.vi 1
/****************************************************************************
2
 
3
  TREE - Graphically displays the directory structure of a drive or path
4
 
5
****************************************************************************/
6
 
7
#define VERSION "1.04"
8
 
9
/****************************************************************************
10
 
11
  Written by: Kenneth J. Davis
12
  Date:       August, 2000
13
  Updated:    September, 2000; October, 2000; November, 2000; January, 2001;
14
              May, 2004; Sept, 2005
15
  Contact:    jeremyd@computer.org
16
 
17
 
18
Copyright (c): Public Domain [United States Definition]
19
 
20
Permission is hereby granted, free of charge, to any person obtaining a copy
21
of this software and associated documentation files (the "Software"), to deal
22
in the Software without restriction, including without limitation the rights
23
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
24
copies of the Software, and to permit persons to whom the Software is
25
furnished to do so, subject to the following conditions:
26
 
27
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
29
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30
NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR AUTHORS BE
31
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
32
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
33
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
34
DEALINGS IN THE SOFTWARE.
35
 
36
****************************************************************************/
37
 
38
 
39
/* Include files */
2034 mateusz.vi 40
#include <dos.h>
2019 mateusz.vi 41
#include <stdlib.h>
42
#include <stdio.h>
43
#include <string.h>
44
 
45
#include "stack.h"
46
 
47
/* The default extended forms of the lines used. */
48
#define VERTBAR_STR  "\xB3   "                 /* |    */
49
#define TBAR_HORZBAR_STR "\xC3\xC4\xC4\xC4"    /* +--- */
50
#define CBAR_HORZBAR_STR "\xC0\xC4\xC4\xC4"    /* \--- */
51
 
52
/* Global flags */
53
#define SHOWFILESON    1  /* Display names of files in directories       */
54
#define SHOWFILESOFF   0  /* Don't display names of files in directories */
55
 
56
#define ASCIICHARS     1  /* Use ASCII [7bit] characters                 */
57
#define EXTENDEDCHARS  0  /* Use extended ASCII [8bit] characters        */
58
 
59
#define NOPAUSE        0  /* Default, don't pause after screenfull       */
60
#define PAUSE          1  /* Wait for keypress after each page           */
61
 
62
 
63
/* Global variables */
64
short showFiles = SHOWFILESOFF;
65
short charSet = EXTENDEDCHARS;
66
short pause = NOPAUSE;
67
 
68
short dspAll = 0;  /* if nonzero includes HIDDEN & SYSTEM files in output */
69
short dspSize = 0; /* if nonzero displays filesizes                       */
70
short dspAttr = 0; /* if nonzero displays file attributes [DACESHRBP]     */
71
short dspSumDirs = 0; /* show count of subdirectories  (per dir and total)*/
72
 
73
 
74
/* maintains total count, for > 4billion dirs, use a __int64 */
75
unsigned long totalSubDirCnt = 0;
76
 
77
 
78
/* text window size, used to determine when to pause,
79
   Note: rows is total rows available - 2
2022 mateusz.vi 80
   1 is for pause message and then one to prevent auto scroll up
2019 mateusz.vi 81
*/
82
short cols=80, rows=23;   /* determined these on startup (when possible)  */
83
 
84
 
85
 
86
/* Global constants */
87
#define SERIALLEN 16      /* Defines max size of volume & serial number   */
2055 mateusz.vi 88
#define VOLLEN 16
2019 mateusz.vi 89
 
2064 mateusz.vi 90
static char path[PATH_MAX];   /* Path to begin search from, default=current   */
2019 mateusz.vi 91
 
2055 mateusz.vi 92
#define MAXPADLEN (PATH_MAX*2) /* Must be large enough to hold the maximum padding */
93
/* (PATH_MAX/2)*4 == (max path len / min 2chars dirs "?\") * 4chars per padding    */
2019 mateusz.vi 94
 
95
/* The maximum size any line of text output can be, including room for '\0'*/
96
#define MAXLINE 160        /* Increased to fit two lines for translations  */
97
 
98
 
99
/* The hard coded strings used by the following show functions.            */
100
 
101
/* common to many functions [Set 1] */
102
char newLine[MAXLINE] = "\n";
103
 
104
/* showUsage [Set 2] - Each %c will be replaced with proper switch/option */
105
char treeDescription[MAXLINE] = "Graphically displays the directory structure of a drive or path.\n";
106
char treeUsage[MAXLINE] =       "TREE [drive:][path] [%c%c] [%c%c]\n";
107
char treeFOption[MAXLINE] =     "   %c%c   Display the names of the files in each directory.\n";
108
char treeAOption[MAXLINE] =     "   %c%c   Use ASCII instead of extended characters.\n";
109
 
110
/* showInvalidUsage [Set 3] */
111
char invalidOption[MAXLINE] = "Invalid switch - %s\n";  /* Must include the %s for option given. */
112
char useTreeHelp[MAXLINE] =   "Use TREE %c? for usage information.\n"; /* %c replaced with switch */
113
 
114
/* showVersionInfo [Set 4] */
115
/* also uses treeDescription */
116
char treeGoal[MAXLINE] =      "Written to work with FreeDOS\n";
117
char treePlatforms[MAXLINE] = "Win32(c) console and DOS with LFN support.\n";
118
char version[MAXLINE] =       "Version %s\n"; /* Must include the %s for version string. */
119
char writtenBy[MAXLINE] =     "Written by: Kenneth J. Davis\n";
120
char writtenDate[MAXLINE] =   "Date:       2000, 2001, 2004\n";
121
char contact[MAXLINE] =       "Contact:    jeremyd@computer.org\n";
122
char copyright[MAXLINE] =     "Copyright (c): Public Domain [United States Definition]\n";
123
 
124
/* showInvalidDrive [Set 5] */
125
char invalidDrive[MAXLINE] = "Invalid drive specification\n";
126
 
127
/* showInvalidPath [Set 6] */
128
char invalidPath[MAXLINE] = "Invalid path - %s\n"; /* Must include %s for the invalid path given. */
129
 
130
/* Misc Error messages [Set 7] */
2052 mateusz.vi 131
 
2019 mateusz.vi 132
/* showOutOfMemory */
133
/* %s required to display what directory we were processing when ran out of memory. */
134
char outOfMemory[MAXLINE] = "Out of memory on subdirectory: %s\n";
135
 
136
/* main [Set 1] */
137
char pathListingNoLabel[MAXLINE] = "Directory PATH listing\n";
138
char pathListingWithLabel[MAXLINE] = "Directory PATH listing for Volume %s\n"; /* %s for label */
139
char serialNumber[MAXLINE] = "Volume serial number is %s\n"; /* Must include %s for serial #   */
140
char noSubDirs[MAXLINE] = "No subdirectories exist\n\n";
141
char pauseMsg[MAXLINE]  = " --- Press any key to continue ---\n";
142
 
143
 
144
 
145
/* Procedures */
146
 
147
 
2044 mateusz.vi 148
/* returns the current drive (A=0, B=1, etc) */
149
static unsigned char getdrive(void);
150
#pragma aux getdrive = \
151
"mov ah, 0x19" \
152
"int 0x21" \
153
modify [ah] \
154
value [al]
155
 
156
 
2049 mateusz.vi 157
/* waits for a keypress, flushes keyb buffer, returns nothing */
158
static void waitkey(void);
159
#pragma aux waitkey = \
160
"mov ah, 0x08" \
161
"int 0x21" \
162
/* flush keyb buffer in case it was an extended key */ \
163
"mov ax, 0x0C0B" \
164
"int 0x21" \
165
modify [ax]
166
 
167
 
2050 mateusz.vi 168
/* checks if stdout appears to be redirected. returns 0 if not, non-zero otherwise. */
2051 mateusz.vi 169
static unsigned char is_stdout_redirected(void);
170
#pragma aux is_stdout_redirected = \
171
"mov ax, 0x4400"    /* DOS 2+, IOCTL Get Device Info */            \
172
"mov bx, 0x0001"    /* file handle (stdout) */                     \
173
"int 0x21" \
174
"jc DONE"           /* on error AL contains a non-zero err code */ \
175
"and dl, 0x80"      /* bit 7 of DL is the "CHAR" flag */           \
176
"xor dl, 0x80"      /* return 0 if CHAR bit is set */              \
177
"mov al, dl" \
178
"DONE:" \
179
modify [ax bx dx] \
180
value [al]
2034 mateusz.vi 181
 
182
 
2061 mateusz.vi 183
static _Packed struct {
184
  unsigned short infolevel;
185
  unsigned short serial2;
186
  unsigned short serial1;
187
  char label[11];
188
  short fstype[8];
189
} glob_drv_info;
190
 
191
/* drv is 1-based (A=1, B=2, ...) */
192
static void getdrvserial(unsigned char drv);
193
#pragma aux getdrvserial = \
194
"push ds" \
195
"xor bh, bh" \
196
"mov dx, offset glob_drv_info" \
197
"mov ax, seg glob_drv_info" \
198
"mov ds, ax" \
199
"mov ax, 0x6900" \
200
"int 0x21" \
201
"pop ds" \
202
parm [bl] \
203
modify [ax bx dx]
204
 
205
 
2052 mateusz.vi 206
static int truename(char *path, const char *origpath) {
207
  unsigned short origpath_seg = FP_SEG(origpath);
208
  unsigned short origpath_off = FP_OFF(origpath);
209
  unsigned short dstpath_seg = FP_SEG(path);
210
  unsigned short dstpath_off = FP_OFF(path);
211
  unsigned char cflag = 0;
212
 
213
  /* resolve path with truename */
214
  _asm {
215
    push ax
216
    push si
217
    push di
218
    push es
219
    push ds
220
 
221
    mov ah, 0x60          /* AH = 0x60 -> TRUENAME */
222
    mov di, dstpath_off   /* ES:DI -> dst buffer */
223
    mov es, dstpath_seg
224
    mov si, origpath_off  /* DS:SI -> src path */
225
    mov ds, origpath_seg
226
    int 0x21
227
    jnc DONE
228
    mov cflag, 1
229
    DONE:
230
 
231
    pop ds
232
    pop es
233
    pop di
234
    pop si
235
    pop ax
236
  }
237
 
238
  return(cflag);
239
}
240
 
241
 
2019 mateusz.vi 242
/* sets rows & cols to size of actual console window
243
 * force NOPAUSE if appears output redirected to a file or
244
 * piped to another program
245
 * Uses hard coded defaults and leaves pause flag unchanged
246
 * if unable to obtain information.
247
 */
2034 mateusz.vi 248
static void getConsoleSize(void) {
249
  unsigned short far *bios_cols = (unsigned short far *)MK_FP(0x40,0x4A);
250
  unsigned short far *bios_size = (unsigned short far *)MK_FP(0x40,0x4C);
2019 mateusz.vi 251
 
2050 mateusz.vi 252
  if (is_stdout_redirected() != 0) {
253
    /* e.g. redirected to a file, tree > filelist.txt */
254
    /* Output to a file or program, so no screen to fill (no max cols or rows) */
2034 mateusz.vi 255
      pause = NOPAUSE;   /* so ignore request to pause */
2050 mateusz.vi 256
  } else { /* e.g. the console */
257
    if ((*bios_cols == 0) || (*bios_size == 0)) { /* MDA does not report size */
258
      cols = 80;
259
      rows = 23;
260
    } else {
261
      cols = *bios_cols;
262
      rows = *bios_size / cols / 2;
263
      if (rows > 2) rows -= 2; /* necessary to keep screen from scrolling */
264
    }
2019 mateusz.vi 265
  }
266
}
267
 
2034 mateusz.vi 268
 
2019 mateusz.vi 269
/* when pause == NOPAUSE then identical to printf,
270
   otherwise counts lines printed and pauses as needed.
271
   Should be used for all messages printed that do not
272
   immediately exit afterwards (else printf may be used).
273
   May display N too many lines before pause if line is
274
   printed that exceeds cols [N=linelen%cols] and lacks
275
   any newlines (but this should not occur in tree).
276
*/
277
#include <stdarg.h>  /* va_list, va_start, va_end */
2037 mateusz.vi 278
static int pprintf(const char *msg, ...) {
2030 mateusz.vi 279
  static int lineCnt = -1;
2019 mateusz.vi 280
  static int lineCol = 0;
281
  va_list argptr;
282
  int cnt;
2055 mateusz.vi 283
  char buffer[MAXLINE];
2019 mateusz.vi 284
 
2030 mateusz.vi 285
  if (lineCnt == -1) lineCnt = rows;
286
 
2019 mateusz.vi 287
  va_start(argptr, msg);
288
  cnt = vsprintf(buffer, msg, argptr);
289
  va_end(argptr);
290
 
291
  if (pause == PAUSE)
292
  {
2030 mateusz.vi 293
    char *l = buffer;
294
    char *t;
2019 mateusz.vi 295
    /* cycle through counting newlines and lines > cols */
2030 mateusz.vi 296
    for (t = strchr(l, '\n'); t != NULL; t = strchr(l, '\n'))
2019 mateusz.vi 297
    {
2030 mateusz.vi 298
      char c;
2019 mateusz.vi 299
      t++;             /* point to character after newline */
2030 mateusz.vi 300
      c = *t;          /* store current value */
2019 mateusz.vi 301
      *t = '\0';       /* mark as end of string */
302
 
303
      /* print all but last line of a string that wraps across rows */
304
      /* adjusting for partial lines printed without the newlines   */
305
      while (strlen(l)+lineCol > cols)
306
      {
307
        char c = l[cols-lineCol];
308
        l[cols-lineCol] = '\0';
309
        printf("%s", l);
310
        l[cols-lineCol] = c;
311
        l += cols-lineCol;
312
 
313
        lineCnt--;  lineCol = 0;
2049 mateusz.vi 314
        if (!lineCnt) { lineCnt= rows;  fflush(NULL);  fprintf(stderr, "%s", pauseMsg);  waitkey(); }
2019 mateusz.vi 315
      }
316
 
317
      printf("%s", l); /* print out this line */
318
      *t = c;          /* restore value */
319
      l = t;           /* mark beginning of next line */
320
 
321
      lineCnt--;  lineCol = 0;
2049 mateusz.vi 322
      if (!lineCnt) { lineCnt= rows;  fflush(NULL);  fprintf(stderr, "%s", pauseMsg);  waitkey(); }
2019 mateusz.vi 323
    }
324
    printf("%s", l);   /* print rest of string that lacks newline */
325
    lineCol = strlen(l);
326
  }
327
  else  /* NOPAUSE */
328
    printf("%s", buffer);
329
 
330
  return cnt;
331
}
332
 
333
 
334
/* Displays to user valid options then exits program indicating no error */
2037 mateusz.vi 335
static void showUsage(void) {
2019 mateusz.vi 336
  printf("%s%s%s%s", treeDescription, newLine, treeUsage, newLine);
337
  printf("%s%s%s", treeFOption, treeAOption, newLine);
338
  exit(1);
339
}
340
 
341
 
342
/* Displays error message then exits indicating error */
2037 mateusz.vi 343
static void showInvalidUsage(char * badOption) {
2019 mateusz.vi 344
  printf(invalidOption, badOption);
345
  printf("%s%s", useTreeHelp, newLine);
346
  exit(1);
347
}
348
 
349
 
350
/* Displays author, copyright, etc info, then exits indicating no error. */
2037 mateusz.vi 351
static void showVersionInfo(void) {
2019 mateusz.vi 352
  printf("%s%s%s%s%s", treeDescription, newLine, treeGoal, treePlatforms, newLine);
353
  printf(version, VERSION);
354
  printf("%s%s%s%s%s", writtenBy, writtenDate, contact, newLine, newLine);
355
  printf("%s%s", copyright, newLine);
356
  exit(1);
357
}
358
 
359
 
360
/* Displays error messge for invalid drives and exits */
2037 mateusz.vi 361
static void showInvalidDrive(void) {
2019 mateusz.vi 362
  printf(invalidDrive);
363
  exit(1);
364
}
365
 
366
 
367
/**
368
 * Takes a given path, strips any \ or / that may appear on the end.
369
 * Returns a pointer to its static buffer containing path
370
 * without trailing slash and any necessary display conversions.
371
 */
2037 mateusz.vi 372
static char *fixPathForDisplay(char *path);
2019 mateusz.vi 373
 
374
/* Displays error message for invalid path; Does NOT exit */
2065 mateusz.vi 375
static void showInvalidPath(const char *badpath) {
376
  pprintf("%s\n", badpath);
377
  pprintf(invalidPath, badpath);
2019 mateusz.vi 378
}
379
 
380
/* Displays error message for out of memory; Does NOT exit */
2064 mateusz.vi 381
static void showOutOfMemory(const char *path) {
2019 mateusz.vi 382
  pprintf(outOfMemory, path);
383
}
384
 
385
 
386
/* Parses the command line and sets global variables. */
2064 mateusz.vi 387
static void parseArguments(int argc, char **argv) {
388
  int i;
2019 mateusz.vi 389
 
390
  /* if no drive specified on command line, use current */
2052 mateusz.vi 391
  if (truename(path, ".") != 0) showInvalidDrive();
2019 mateusz.vi 392
 
2064 mateusz.vi 393
  for (i = 1; i < argc; i++) {
394
 
2019 mateusz.vi 395
    /* Check if user is giving an option or drive/path */
2064 mateusz.vi 396
    if ((argv[i][0] != '/') && (argv[i][0] != '-') ) {
397
      if (truename(path, argv[i]) != 0) showInvalidPath(argv[i]);
398
      continue;
399
    }
2019 mateusz.vi 400
 
2064 mateusz.vi 401
    /* must be an option then */
402
    /* check multi character options 1st */
403
    if (argv[i][1] & 0xDF == 'D') {
404
      switch(argv[i][2] & 0xDF) {
405
        case 'A' :       /*  /DA  display attributes */
406
          dspAttr = 1;
407
          break;
408
        case 'F' :       /*  /DF  display filesizes  */
409
          dspSize = 1;
410
          break;
411
        case 'H' :       /*  /DH  display hidden & system files (normally not shown) */
412
          dspAll = 1;
413
          break;
414
        case 'R' :       /*  /DR  display results at end */
415
          dspSumDirs = 1;
416
          break;
417
        default:
2019 mateusz.vi 418
          showInvalidUsage(argv[i]);
419
      }
2064 mateusz.vi 420
      continue;
2019 mateusz.vi 421
    }
2064 mateusz.vi 422
 
423
    /* a 1 character option (or invalid) */
424
    if (argv[i][2] != 0) showInvalidUsage(argv[i]);
425
 
426
    switch(argv[i][1] & 0xDF) { /* upcase */
427
      case 'F': /* show files */
428
        showFiles = SHOWFILESON; /* set file display flag appropriately */
429
        break;
430
      case 'A': /* use ASCII only (7-bit) */
431
        charSet = ASCIICHARS;    /* set charset flag appropriately      */
432
        break;
433
      case 'V': /* Version information */
434
        showVersionInfo();       /* show version info and exit          */
435
        break;
436
      case 'P': /* wait for keypress after each page (pause) */
437
        pause = PAUSE;
438
        break;
439
      case '?':
440
        showUsage();             /* show usage info and exit            */
441
        break;
442
      default: /* Invalid or unknown option */
443
        showInvalidUsage(argv[i]);
444
    }
2019 mateusz.vi 445
  }
446
}
447
 
448
 
449
/**
450
 * Fills in the serial and volume variables with the serial #
451
 * and volume found using path.
452
 */
2037 mateusz.vi 453
static void GetVolumeAndSerial(char *volume, char *serial, char *path) {
2061 mateusz.vi 454
  getdrvserial((path[0] & 0xDF) - '@');
455
  memcpy(volume, glob_drv_info.label, 12);
456
  volume[11] = 0;
2019 mateusz.vi 457
 
2061 mateusz.vi 458
  sprintf(serial, "%04X:%04X", glob_drv_info.serial1, glob_drv_info.serial2);
2019 mateusz.vi 459
}
460
 
461
 
462
/**
463
 * Stores directory information obtained from FindFirst/Next that
464
 * we may wish to make use of when displaying directory entry.
465
 * e.g. attribute, dates, etc.
466
 */
2054 mateusz.vi 467
typedef struct DIRDATA {
468
  unsigned long subdirCnt;  /* how many subdirectories we have */
469
  unsigned long fileCnt;    /* how many [normal] files we have */
2056 mateusz.vi 470
  unsigned int attrib;      /* Directory attributes            */
2019 mateusz.vi 471
} DIRDATA;
472
 
473
/**
474
 * Contains the information stored in a Stack necessary to allow
475
 * non-recursive function to display directory tree.
476
 */
2062 mateusz.vi 477
struct SUBDIRINFO {
478
  struct SUBDIRINFO *parent; /* points to parent subdirectory                */
2019 mateusz.vi 479
  char *currentpath;    /* Stores the full path this structure represents     */
480
  char *subdir;         /* points to last subdir within currentpath           */
481
  char *dsubdir;        /* Stores a display ready directory name              */
482
  long subdircnt;       /* Initially a count of how many subdirs in this dir  */
2060 mateusz.vi 483
  struct find_t *findnexthnd; /* The handle returned by findfirst, used in findnext */
2019 mateusz.vi 484
  struct DIRDATA ddata; /* Maintain directory information, eg attributes      */
2062 mateusz.vi 485
};
2019 mateusz.vi 486
 
487
 
488
/**
489
 * Returns 0 if no subdirectories, count if has subdirs.
490
 * Path must end in slash \ or /
491
 * On error (invalid path) displays message and returns -1L.
492
 * Stores additional directory data in ddata if non-NULL
493
 * and path is valid.
494
 */
2037 mateusz.vi 495
static long hasSubdirectories(char *path, DIRDATA *ddata) {
2060 mateusz.vi 496
  struct find_t findData;
497
  char buffer[PATH_MAX + 4];
2063 mateusz.vi 498
  unsigned short hasSubdirs = 0;
2019 mateusz.vi 499
 
500
  /* get the handle to start with (using wildcard spec) */
501
  strcpy(buffer, path);
2060 mateusz.vi 502
  strcat(buffer, "*.*");
2019 mateusz.vi 503
 
2060 mateusz.vi 504
  if (_dos_findfirst(buffer, 0x37, &findData) != 0) {
2019 mateusz.vi 505
    showInvalidPath(path); /* Display error message */
2048 mateusz.vi 506
    return(-1);
2019 mateusz.vi 507
  }
508
 
509
  /*  cycle through entries counting directories found until no more entries */
510
  do {
2063 mateusz.vi 511
    if ((findData.attrib & _A_SUBDIR) == 0) continue; /* not a DIR */
512
      /* filter out system and hidden files, unless dspAll is on */
513
    if (dspAll == 0) {
514
      if (findData.attrib & _A_HIDDEN) continue;
515
      if (findData.attrib & _A_SYSTEM) continue;
2019 mateusz.vi 516
    }
2063 mateusz.vi 517
    if (findData.name[0] != '.') { /* ignore '.' and '..' */
518
      hasSubdirs++;      /* subdir of initial path found, so increment counter */
519
    }
2060 mateusz.vi 520
  } while(_dos_findnext(&findData) == 0);
2019 mateusz.vi 521
 
522
  /* prevent resource leaks, close the handle. */
2060 mateusz.vi 523
  _dos_findclose(&findData);
2019 mateusz.vi 524
 
525
  if (ddata != NULL)  // don't bother if user doesn't want them
526
  {
527
    /* The root directory of a volume (including non root paths
528
       corresponding to mount points) may not have a current (.) and
529
       parent (..) entry.  So we can't get attributes for initial
2022 mateusz.vi 530
       path in above loop from the FindFile call as it may not show up
2019 mateusz.vi 531
       (no . entry).  So instead we explicitly get them here.
532
    */
2056 mateusz.vi 533
    if (_dos_getfileattr(path, &(ddata->attrib)) != 0) {
2019 mateusz.vi 534
      //printf("ERROR: unable to get file attr, %i\n", GetLastError());
2047 mateusz.vi 535
      ddata->attrib = 0;
2019 mateusz.vi 536
    }
537
 
538
    /* a curiosity, for showing sum of directories process */
539
    ddata->subdirCnt = hasSubdirs;
540
  }
541
  totalSubDirCnt += hasSubdirs;
542
 
543
  return hasSubdirs;
544
}
545
 
546
 
547
/**
548
 * Allocates memory and stores the necessary stuff to allow us to
549
 * come back to this subdirectory after handling its subdirectories.
550
 * parentpath must end in \ or / or be NULL, however
551
 * parent should only be NULL for initialpath
552
 * if subdir does not end in slash, one is added to stored subdir
553
 * dsubdir is subdir already modified so ready to display to user
554
 */
2062 mateusz.vi 555
static struct SUBDIRINFO *newSubdirInfo(struct SUBDIRINFO *parent, char *subdir, char *dsubdir) {
2027 mateusz.vi 556
  int parentLen, subdirLen;
2062 mateusz.vi 557
  struct SUBDIRINFO *temp;
2019 mateusz.vi 558
 
559
  /* Get length of parent directory */
560
  if (parent == NULL)
561
    parentLen = 0;
562
  else
563
    parentLen = strlen(parent->currentpath);
564
 
565
  /* Get length of subdir, add 1 if does not end in slash */
566
  subdirLen = strlen(subdir);
567
  if ((subdirLen < 1) || ( (*(subdir+subdirLen-1) != '\\') && (*(subdir+subdirLen-1) != '/') ) )
568
    subdirLen++;
569
 
2062 mateusz.vi 570
  temp = malloc(sizeof(struct SUBDIRINFO));
571
  if (temp == NULL) {
2019 mateusz.vi 572
    showOutOfMemory(subdir);
573
    return NULL;
574
  }
575
  if ( ((temp->currentpath = (char *)malloc(parentLen+subdirLen+1)) == NULL) ||
576
       ((temp->dsubdir = (char *)malloc(strlen(dsubdir)+1)) == NULL) )
577
  {
578
    showOutOfMemory(subdir);
579
    if (temp->currentpath != NULL) free(temp->currentpath);
580
    free(temp);
581
    return NULL;
582
  }
583
  temp->parent = parent;
584
  if (parent == NULL)
585
    strcpy(temp->currentpath, "");
586
  else
587
    strcpy(temp->currentpath, parent->currentpath);
588
  strcat(temp->currentpath, subdir);
589
  /* if subdir[subdirLen-1] == '\0' then we must append a slash */
590
  if (*(subdir+subdirLen-1) == '\0')
591
    strcat(temp->currentpath, "\\");
592
  temp->subdir = temp->currentpath+parentLen;
593
  strcpy(temp->dsubdir, dsubdir);
594
  if ((temp->subdircnt = hasSubdirectories(temp->currentpath, &(temp->ddata))) == -1L)
595
  {
596
    free (temp->currentpath);
597
    free (temp->dsubdir);
598
    free(temp);
599
    return NULL;
600
  }
2048 mateusz.vi 601
  temp->findnexthnd = NULL;
2019 mateusz.vi 602
 
603
  return temp;
604
}
605
 
2048 mateusz.vi 606
 
2019 mateusz.vi 607
/**
608
 * Extends the padding with the necessary 4 characters.
609
 * Returns the pointer to the padding.
2022 mateusz.vi 610
 * padding should be large enough to hold the additional
2019 mateusz.vi 611
 * characters and '\0', moreSubdirsFollow specifies if
612
 * this is the last subdirectory in a given directory
613
 * or if more follow (hence if a | is needed).
614
 * padding must not be NULL
615
 */
2037 mateusz.vi 616
static char * addPadding(char *padding, int moreSubdirsFollow) {
617
  if (moreSubdirsFollow) {
618
    /* 1st char is | or a vertical bar */
619
    if (charSet == EXTENDEDCHARS) {
620
      strcat(padding, VERTBAR_STR);
621
    } else {
622
      strcat(padding, "|   ");
2019 mateusz.vi 623
    }
2037 mateusz.vi 624
  } else {
625
    strcat(padding, "    ");
626
  }
2019 mateusz.vi 627
 
2037 mateusz.vi 628
  return(padding);
2019 mateusz.vi 629
}
630
 
631
/**
2025 mateusz.vi 632
 * Removes the last padding added (last 4 characters added).
633
 * Does nothing if less than 4 characters in string.
2019 mateusz.vi 634
 * padding must not be NULL
635
 * Returns the pointer to padding.
636
 */
2037 mateusz.vi 637
static char *removePadding(char *padding) {
2027 mateusz.vi 638
  size_t len = strlen(padding);
2019 mateusz.vi 639
 
2025 mateusz.vi 640
  if (len < 4) return padding;
641
  *(padding + len - 4) = '\0';
2019 mateusz.vi 642
 
643
  return padding;
644
}
645
 
646
 
647
/**
648
 * Displays the current path, with necessary padding before it.
649
 * A \ or / on end of currentpath is not shown.
650
 * moreSubdirsFollow should be nonzero if this is not the last
651
 * subdirectory to be displayed in current directory, else 0.
652
 * Also displays additional information, such as attributes or
653
 * sum of size of included files.
654
 * currentpath is an ASCIIZ string of path to display
655
 *             assumed to be a displayable path (ie. OEM or UTF-8)
656
 * padding is an ASCIIZ string to display prior to entry.
657
 * moreSubdirsFollow is -1 for initial path else >= 0.
658
 */
2037 mateusz.vi 659
static void showCurrentPath(char *currentpath, char *padding, int moreSubdirsFollow, DIRDATA *ddata) {
2019 mateusz.vi 660
  if (padding != NULL)
661
    pprintf("%s", padding);
662
 
663
  /* print lead padding except for initial directory */
664
  if (moreSubdirsFollow >= 0)
665
  {
666
    if (charSet == EXTENDEDCHARS)
667
    {
668
      if (moreSubdirsFollow)
669
        pprintf("%s", TBAR_HORZBAR_STR);
670
      else
671
        pprintf("%s", CBAR_HORZBAR_STR);
2037 mateusz.vi 672
    } else {
2019 mateusz.vi 673
      if (moreSubdirsFollow)
674
        pprintf("+---");
675
      else
676
        pprintf("\\---");
677
    }
678
  }
679
 
680
  /* optional display data */
681
  if (dspAttr)  /* attributes */
2031 mateusz.vi 682
    pprintf("[%c%c%c%c%c] ",
2061 mateusz.vi 683
      (ddata->attrib & _A_SUBDIR)?'D':' ',  /* keep this one? its always true */
684
      (ddata->attrib & _A_ARCH)?'A':' ',
685
      (ddata->attrib & _A_SYSTEM)?'S':' ',
686
      (ddata->attrib & _A_HIDDEN)?'H':' ',
687
      (ddata->attrib & _A_RDONLY)?'R':' '
2019 mateusz.vi 688
    );
689
 
690
  /* display directory name */
691
  pprintf("%s\n", currentpath);
692
}
693
 
694
 
2022 mateusz.vi 695
/**
2019 mateusz.vi 696
 * Displays summary information about directory.
697
 * Expects to be called after displayFiles (optionally called)
698
 */
2037 mateusz.vi 699
static void displaySummary(char *padding, int hasMoreSubdirs, DIRDATA *ddata) {
2019 mateusz.vi 700
  addPadding(padding, hasMoreSubdirs);
701
 
2037 mateusz.vi 702
  if (dspSumDirs) {
703
    if (showFiles == SHOWFILESON) {
2019 mateusz.vi 704
      /* print File summary with lead padding, add filesize to it */
705
      pprintf("%s%lu files\n", padding, ddata->fileCnt);
706
    }
707
 
708
    /* print Directory summary with lead padding */
709
    pprintf("%s%lu subdirectories\n", padding, ddata->subdirCnt);
710
 
711
    /* show [nearly] blank line after summary */
712
    pprintf("%s\n", padding);
713
  }
714
 
715
  removePadding(padding);
716
}
717
 
2022 mateusz.vi 718
/**
2019 mateusz.vi 719
 * Displays files in directory specified by path.
720
 * Path must end in slash \ or /
721
 * Returns -1 on error,
722
 *          0 if no files, but no errors either,
723
 *      or  1 if files displayed, no errors.
724
 */
2047 mateusz.vi 725
static int displayFiles(const char *path, char *padding, int hasMoreSubdirs, DIRDATA *ddata) {
2060 mateusz.vi 726
  char buffer[PATH_MAX + 4];
727
  struct find_t entry;   /* current directory entry info    */
2019 mateusz.vi 728
  unsigned long filesShown = 0;
729
 
730
  /* get handle for files in current directory (using wildcard spec) */
731
  strcpy(buffer, path);
2060 mateusz.vi 732
  strcat(buffer, "*.*");
733
  if (_dos_findfirst(buffer, 0x37, &entry) != 0) return(-1);
2019 mateusz.vi 734
 
735
  addPadding(padding, hasMoreSubdirs);
736
 
737
  /* cycle through directory printing out files. */
2022 mateusz.vi 738
  do
2019 mateusz.vi 739
  {
740
    /* print padding followed by filename */
2061 mateusz.vi 741
    if ( ((entry.attrib & _A_SUBDIR) == 0) &&
742
         ( ((entry.attrib & (_A_HIDDEN | _A_SYSTEM)) == 0)  || dspAll) )
2019 mateusz.vi 743
    {
744
      /* print lead padding */
745
      pprintf("%s", padding);
746
 
747
      /* optional display data */
748
      if (dspAttr)  /* file attributes */
2031 mateusz.vi 749
        pprintf("[%c%c%c%c] ",
2061 mateusz.vi 750
          (entry.attrib & _A_ARCH)?'A':' ',
751
          (entry.attrib & _A_SYSTEM)?'S':' ',
752
          (entry.attrib & _A_HIDDEN)?'H':' ',
753
          (entry.attrib & _A_RDONLY)?'R':' '
2019 mateusz.vi 754
        );
755
 
2053 mateusz.vi 756
      if (dspSize) { /* file size */
2058 mateusz.vi 757
        if (entry.size < 1048576ul)  /* if less than a MB, display in bytes */
758
          pprintf("%10lu ", entry.size);
2053 mateusz.vi 759
        else                               /* otherwise display in KB */
2058 mateusz.vi 760
          pprintf("%8luKB ", entry.size / 1024ul);
2019 mateusz.vi 761
      }
762
 
763
      /* print filename */
2058 mateusz.vi 764
      pprintf("%s\n", entry.name);
2019 mateusz.vi 765
 
766
      filesShown++;
767
    }
2060 mateusz.vi 768
  } while(_dos_findnext(&entry) == 0);
2019 mateusz.vi 769
 
770
  if (filesShown)
771
  {
772
    pprintf("%s\n", padding);
773
  }
774
 
775
  removePadding(padding);
776
 
777
  /* store for summary display */
778
  if (ddata != NULL) ddata->fileCnt = filesShown;
779
 
780
  return (filesShown)? 1 : 0;
781
}
782
 
783
 
784
/**
785
 * Common portion of findFirstSubdir and findNextSubdir
786
 * Checks current FindFile results to determine if a valid directory
787
 * was found, and if so copies appropriate data into subdir and dsubdir.
788
 * It will repeat until a valid subdirectory is found or no more
789
 * are found, at which point it closes the FindFile search handle and
2048 mateusz.vi 790
 * return NULL.  If successful, returns FindFile handle.
2019 mateusz.vi 791
 */
2060 mateusz.vi 792
static struct find_t *cycleFindResults(struct find_t *entry, char *subdir, char *dsubdir) {
2025 mateusz.vi 793
  /* cycle through directory until 1st non . or .. directory is found. */
2057 mateusz.vi 794
  for (;;) {
2025 mateusz.vi 795
    /* skip files & hidden or system directories */
2061 mateusz.vi 796
    if ((((entry->attrib & _A_SUBDIR) == 0) ||
2058 mateusz.vi 797
         ((entry->attrib &
2061 mateusz.vi 798
          (_A_HIDDEN | _A_SYSTEM)) != 0  && !dspAll) ) ||
2058 mateusz.vi 799
        (entry->name[0] == '.')) {
2060 mateusz.vi 800
      if (_dos_findnext(entry) != 0) {
801
        _dos_findclose(entry);      // prevent resource leaks
2048 mateusz.vi 802
        return(NULL); // no subdirs found
2019 mateusz.vi 803
      }
2048 mateusz.vi 804
    } else {
2025 mateusz.vi 805
      /* set display name */
2058 mateusz.vi 806
      strcpy(dsubdir, entry->name);
2019 mateusz.vi 807
 
2058 mateusz.vi 808
      strcpy(subdir, entry->name);
2025 mateusz.vi 809
      strcat(subdir, "\\");
2057 mateusz.vi 810
      return(entry);
2025 mateusz.vi 811
    }
2057 mateusz.vi 812
  }
2025 mateusz.vi 813
 
2057 mateusz.vi 814
  return entry;
2019 mateusz.vi 815
}
816
 
817
 
818
/**
819
 * Given the current path, find the 1st subdirectory.
820
 * The subdirectory found is stored in subdir.
821
 * subdir is cleared on error or no subdirectories.
822
 * Returns the findfirst search HANDLE, which should be passed to
823
 * findclose when directory has finished processing, and can be
824
 * passed to findnextsubdir to find subsequent subdirectories.
2048 mateusz.vi 825
 * Returns NULL on error.
2019 mateusz.vi 826
 * currentpath must end in \
827
 */
2060 mateusz.vi 828
static struct find_t *findFirstSubdir(char *currentpath, char *subdir, char *dsubdir) {
829
  char buffer[PATH_MAX + 4];
830
  struct find_t *dir;         /* Current directory entry working with      */
2019 mateusz.vi 831
 
2060 mateusz.vi 832
  dir = malloc(sizeof(struct find_t));
2057 mateusz.vi 833
  if (dir == NULL) return(NULL);
834
 
2019 mateusz.vi 835
  /* get handle for files in current directory (using wildcard spec) */
836
  strcpy(buffer, currentpath);
2060 mateusz.vi 837
  strcat(buffer, "*.*");
2019 mateusz.vi 838
 
2060 mateusz.vi 839
  if (_dos_findfirst(buffer, 0x37, dir) != 0) {
2019 mateusz.vi 840
    showInvalidPath(currentpath);
2048 mateusz.vi 841
    return(NULL);
2019 mateusz.vi 842
  }
843
 
844
  /* clear result path */
845
  strcpy(subdir, "");
846
 
2057 mateusz.vi 847
  return cycleFindResults(dir, subdir, dsubdir);
2019 mateusz.vi 848
}
849
 
850
/**
2022 mateusz.vi 851
 * Given a search HANDLE, will find the next subdirectory,
2019 mateusz.vi 852
 * setting subdir to the found directory name.
2045 mateusz.vi 853
 * dsubdir is the name to display
2019 mateusz.vi 854
 * currentpath must end in \
855
 * If a subdirectory is found, returns 0, otherwise returns 1
856
 * (either error or no more files).
857
 */
2060 mateusz.vi 858
static int findNextSubdir(struct find_t *findnexthnd, char *subdir, char *dsubdir) {
2019 mateusz.vi 859
  /* clear result path */
2057 mateusz.vi 860
  subdir[0] = 0;
2019 mateusz.vi 861
 
2060 mateusz.vi 862
  if (_dos_findnext(findnexthnd) != 0) return(1); // no subdirs found
2019 mateusz.vi 863
 
2057 mateusz.vi 864
  if (cycleFindResults(findnexthnd, subdir, dsubdir) == NULL) {
2019 mateusz.vi 865
    return 1;
2048 mateusz.vi 866
  }
867
  return 0;
2019 mateusz.vi 868
}
869
 
870
/**
871
 * Given an initial path, displays the directory tree with
872
 * a non-recursive function using a Stack.
873
 * initialpath must be large enough to hold an added slash \ or /
874
 * if it does not already end in one.
875
 * Returns the count of subdirs in initialpath.
876
 */
2037 mateusz.vi 877
static long traverseTree(char *initialpath) {
2019 mateusz.vi 878
  long subdirsInInitialpath;
879
  char padding[MAXPADLEN] = "";
2055 mateusz.vi 880
  char subdir[PATH_MAX];
881
  char dsubdir[PATH_MAX];
2062 mateusz.vi 882
  struct SUBDIRINFO *sdi;
2019 mateusz.vi 883
 
884
  STACK s;
885
  stackDefaults(&s);
886
  stackInit(&s);
887
 
2048 mateusz.vi 888
  if ( (sdi = newSubdirInfo(NULL, initialpath, initialpath)) == NULL) {
889
    return(0);
890
  }
2019 mateusz.vi 891
  stackPushItem(&s, sdi);
892
 
893
  /* Store count of subdirs in initial path so can display message if none. */
894
  subdirsInInitialpath = sdi->subdircnt;
895
 
896
  do
897
  {
2062 mateusz.vi 898
    sdi = (struct SUBDIRINFO *)stackPopItem(&s);
2019 mateusz.vi 899
 
2048 mateusz.vi 900
    if (sdi->findnexthnd == NULL) { // findfirst not called yet
2019 mateusz.vi 901
      // 1st time this subdirectory processed, so display its name & possibly files
902
      if (sdi->parent == NULL) // if initial path
903
      {
904
        // display initial path
905
        showCurrentPath(/*sdi->dsubdir*/initialpath, NULL, -1, &(sdi->ddata));
906
      }
907
      else // normal processing (display path, add necessary padding)
908
      {
909
        showCurrentPath(sdi->dsubdir, padding, (sdi->parent->subdircnt > 0L)?1 : 0, &(sdi->ddata));
910
        addPadding(padding, (sdi->parent->subdircnt > 0L)?1 : 0);
911
      }
912
 
913
      if (showFiles == SHOWFILESON)  displayFiles(sdi->currentpath, padding, (sdi->subdircnt > 0L)?1 : 0, &(sdi->ddata));
2024 mateusz.vi 914
      displaySummary(padding, (sdi->subdircnt > 0L)?1 : 0, &(sdi->ddata));
2019 mateusz.vi 915
    }
916
 
917
    if (sdi->subdircnt > 0) /* if (there are more subdirectories to process) */
918
    {
919
      int flgErr;
2048 mateusz.vi 920
      if (sdi->findnexthnd == NULL) {
2019 mateusz.vi 921
        sdi->findnexthnd = findFirstSubdir(sdi->currentpath, subdir, dsubdir);
2048 mateusz.vi 922
        flgErr = (sdi->findnexthnd == NULL);
923
      } else {
2019 mateusz.vi 924
        flgErr = findNextSubdir(sdi->findnexthnd, subdir, dsubdir);
925
      }
926
 
927
      if (flgErr) // don't add invalid paths to stack
928
      {
929
        printf("INTERNAL ERROR: subdir count changed, expecting %li more!\n", sdi->subdircnt+1L);
930
 
931
        sdi->subdircnt = 0; /* force subdir counter to 0, none left */
932
        stackPushItem(&s, sdi);
933
      }
934
      else
935
      {
936
        sdi->subdircnt = sdi->subdircnt - 1L; /* decrement subdirs left count */
937
        stackPushItem(&s, sdi);
938
 
939
        /* store necessary information, validate subdir, and if no error store it. */
940
        if ((sdi = newSubdirInfo(sdi, subdir, dsubdir)) != NULL)
941
          stackPushItem(&s, sdi);
942
      }
943
    }
944
    else /* this directory finished processing, so free resources */
945
    {
946
      /* Remove the padding for this directory, all but initial path. */
947
      if (sdi->parent != NULL)
948
        removePadding(padding);
949
 
950
      /* Prevent resource leaks, by ending findsearch and freeing memory. */
2060 mateusz.vi 951
      _dos_findclose(sdi->findnexthnd);
2019 mateusz.vi 952
      if (sdi != NULL)
953
      {
954
        if (sdi->currentpath != NULL)
955
          free(sdi->currentpath);
956
        free(sdi);
957
      }
958
    }
959
  } while (stackTotalItems(&s)); /* while (stack is not empty) */
960
 
961
  stackTerm(&s);
962
 
963
  return subdirsInInitialpath;
964
}
965
 
966
 
2037 mateusz.vi 967
static void FixOptionText(void) {
2019 mateusz.vi 968
  char buffer[MAXLINE];  /* sprintf can have problems with src==dest */
969
 
970
  /* Handle %c for options within messages using Set 8 */
971
  strcpy(buffer, treeUsage);
2064 mateusz.vi 972
  sprintf(treeUsage, buffer, '/', 'f', '/', 'a');
2019 mateusz.vi 973
  strcpy(buffer, treeFOption);
2064 mateusz.vi 974
  sprintf(treeFOption, buffer, '/', 'f');
2019 mateusz.vi 975
  strcpy(buffer, treeAOption);
2064 mateusz.vi 976
  sprintf(treeAOption, buffer, '/', 'a');
2019 mateusz.vi 977
  strcpy(buffer, useTreeHelp);
2064 mateusz.vi 978
  sprintf(useTreeHelp, buffer, '/');
2019 mateusz.vi 979
}
980
 
981
 
2022 mateusz.vi 982
/* Loads all messages from the message catalog. */
2037 mateusz.vi 983
static void loadAllMessages(void) {
2019 mateusz.vi 984
  /* Changes %c in certain lines with proper option characters. */
985
  FixOptionText();
986
}
987
 
988
 
2037 mateusz.vi 989
int main(int argc, char **argv) {
2019 mateusz.vi 990
  char serial[SERIALLEN]; /* volume serial #  0000:0000 */
991
  char volume[VOLLEN];    /* volume name (label), possibly none */
992
 
993
  /* Load all text from message catalog (or uses hard coded text) */
994
  loadAllMessages();
995
 
996
  /* Parse any command line arguments, obtain path */
997
  parseArguments(argc, argv);
998
 
999
  /* Initialize screen size, may reset pause to NOPAUSE if redirected */
1000
  getConsoleSize();
1001
 
1002
  /* Get Volume & Serial Number */
1003
  GetVolumeAndSerial(volume, serial, path);
1004
  if (strlen(volume) == 0)
1005
    pprintf(pathListingNoLabel);
1006
  else
1007
    pprintf(pathListingWithLabel, volume);
1008
  if (serial[0] != '\0')  /* Don't print anything if no serial# found */
1009
    pprintf(serialNumber, serial);
1010
 
1011
  /* now traverse & print tree, returns nonzero if has subdirectories */
1012
  if (traverseTree(path) == 0)
1013
    pprintf(noSubDirs);
1014
  else if (dspSumDirs) /* show count of directories processed */
1015
    pprintf("\n    %lu total directories\n", totalSubDirCnt+1);
1016
 
1017
  return 0;
1018
}