Subversion Repositories SvarDOS

Rev

Rev 2060 | Rev 2062 | 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
 
2055 mateusz.vi 90
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
/* Option Processing - parseArguments [Set 8]      */
144
char optionchar1 = '/';  /* Primary character used to determine option follows  */
145
char optionchar2 = '-';  /* Secondary character used to determine option follows  */
146
const char OptShowFiles[2] = { 'F', 'f' };  /* Show files */
147
const char OptUseASCII[2]  = { 'A', 'a' };  /* Use ASCII only */
148
const char OptVersion[2]   = { 'V', 'v' };  /* Version information */
149
const char OptPause[2]     = { 'P', 'p' };  /* Pause after each page (screenfull) */
150
const char OptDisplay[2]   = { 'D', 'd' };  /* modify Display settings */
151
 
152
 
153
/* Procedures */
154
 
155
 
2044 mateusz.vi 156
/* returns the current drive (A=0, B=1, etc) */
157
static unsigned char getdrive(void);
158
#pragma aux getdrive = \
159
"mov ah, 0x19" \
160
"int 0x21" \
161
modify [ah] \
162
value [al]
163
 
164
 
2049 mateusz.vi 165
/* waits for a keypress, flushes keyb buffer, returns nothing */
166
static void waitkey(void);
167
#pragma aux waitkey = \
168
"mov ah, 0x08" \
169
"int 0x21" \
170
/* flush keyb buffer in case it was an extended key */ \
171
"mov ax, 0x0C0B" \
172
"int 0x21" \
173
modify [ax]
174
 
175
 
2050 mateusz.vi 176
/* checks if stdout appears to be redirected. returns 0 if not, non-zero otherwise. */
2051 mateusz.vi 177
static unsigned char is_stdout_redirected(void);
178
#pragma aux is_stdout_redirected = \
179
"mov ax, 0x4400"    /* DOS 2+, IOCTL Get Device Info */            \
180
"mov bx, 0x0001"    /* file handle (stdout) */                     \
181
"int 0x21" \
182
"jc DONE"           /* on error AL contains a non-zero err code */ \
183
"and dl, 0x80"      /* bit 7 of DL is the "CHAR" flag */           \
184
"xor dl, 0x80"      /* return 0 if CHAR bit is set */              \
185
"mov al, dl" \
186
"DONE:" \
187
modify [ax bx dx] \
188
value [al]
2034 mateusz.vi 189
 
190
 
2061 mateusz.vi 191
static _Packed struct {
192
  unsigned short infolevel;
193
  unsigned short serial2;
194
  unsigned short serial1;
195
  char label[11];
196
  short fstype[8];
197
} glob_drv_info;
198
 
199
/* drv is 1-based (A=1, B=2, ...) */
200
static void getdrvserial(unsigned char drv);
201
#pragma aux getdrvserial = \
202
"push ds" \
203
"xor bh, bh" \
204
"mov dx, offset glob_drv_info" \
205
"mov ax, seg glob_drv_info" \
206
"mov ds, ax" \
207
"mov ax, 0x6900" \
208
"int 0x21" \
209
"pop ds" \
210
parm [bl] \
211
modify [ax bx dx]
212
 
213
 
2052 mateusz.vi 214
static int truename(char *path, const char *origpath) {
215
  unsigned short origpath_seg = FP_SEG(origpath);
216
  unsigned short origpath_off = FP_OFF(origpath);
217
  unsigned short dstpath_seg = FP_SEG(path);
218
  unsigned short dstpath_off = FP_OFF(path);
219
  unsigned char cflag = 0;
220
 
221
  /* resolve path with truename */
222
  _asm {
223
    push ax
224
    push si
225
    push di
226
    push es
227
    push ds
228
 
229
    mov ah, 0x60          /* AH = 0x60 -> TRUENAME */
230
    mov di, dstpath_off   /* ES:DI -> dst buffer */
231
    mov es, dstpath_seg
232
    mov si, origpath_off  /* DS:SI -> src path */
233
    mov ds, origpath_seg
234
    int 0x21
235
    jnc DONE
236
    mov cflag, 1
237
    DONE:
238
 
239
    pop ds
240
    pop es
241
    pop di
242
    pop si
243
    pop ax
244
  }
245
 
246
  return(cflag);
247
}
248
 
249
 
2019 mateusz.vi 250
/* sets rows & cols to size of actual console window
251
 * force NOPAUSE if appears output redirected to a file or
252
 * piped to another program
253
 * Uses hard coded defaults and leaves pause flag unchanged
254
 * if unable to obtain information.
255
 */
2034 mateusz.vi 256
static void getConsoleSize(void) {
257
  unsigned short far *bios_cols = (unsigned short far *)MK_FP(0x40,0x4A);
258
  unsigned short far *bios_size = (unsigned short far *)MK_FP(0x40,0x4C);
2019 mateusz.vi 259
 
2050 mateusz.vi 260
  if (is_stdout_redirected() != 0) {
261
    /* e.g. redirected to a file, tree > filelist.txt */
262
    /* Output to a file or program, so no screen to fill (no max cols or rows) */
2034 mateusz.vi 263
      pause = NOPAUSE;   /* so ignore request to pause */
2050 mateusz.vi 264
  } else { /* e.g. the console */
265
    if ((*bios_cols == 0) || (*bios_size == 0)) { /* MDA does not report size */
266
      cols = 80;
267
      rows = 23;
268
    } else {
269
      cols = *bios_cols;
270
      rows = *bios_size / cols / 2;
271
      if (rows > 2) rows -= 2; /* necessary to keep screen from scrolling */
272
    }
2019 mateusz.vi 273
  }
274
}
275
 
2034 mateusz.vi 276
 
2019 mateusz.vi 277
/* when pause == NOPAUSE then identical to printf,
278
   otherwise counts lines printed and pauses as needed.
279
   Should be used for all messages printed that do not
280
   immediately exit afterwards (else printf may be used).
281
   May display N too many lines before pause if line is
282
   printed that exceeds cols [N=linelen%cols] and lacks
283
   any newlines (but this should not occur in tree).
284
*/
285
#include <stdarg.h>  /* va_list, va_start, va_end */
2037 mateusz.vi 286
static int pprintf(const char *msg, ...) {
2030 mateusz.vi 287
  static int lineCnt = -1;
2019 mateusz.vi 288
  static int lineCol = 0;
289
  va_list argptr;
290
  int cnt;
2055 mateusz.vi 291
  char buffer[MAXLINE];
2019 mateusz.vi 292
 
2030 mateusz.vi 293
  if (lineCnt == -1) lineCnt = rows;
294
 
2019 mateusz.vi 295
  va_start(argptr, msg);
296
  cnt = vsprintf(buffer, msg, argptr);
297
  va_end(argptr);
298
 
299
  if (pause == PAUSE)
300
  {
2030 mateusz.vi 301
    char *l = buffer;
302
    char *t;
2019 mateusz.vi 303
    /* cycle through counting newlines and lines > cols */
2030 mateusz.vi 304
    for (t = strchr(l, '\n'); t != NULL; t = strchr(l, '\n'))
2019 mateusz.vi 305
    {
2030 mateusz.vi 306
      char c;
2019 mateusz.vi 307
      t++;             /* point to character after newline */
2030 mateusz.vi 308
      c = *t;          /* store current value */
2019 mateusz.vi 309
      *t = '\0';       /* mark as end of string */
310
 
311
      /* print all but last line of a string that wraps across rows */
312
      /* adjusting for partial lines printed without the newlines   */
313
      while (strlen(l)+lineCol > cols)
314
      {
315
        char c = l[cols-lineCol];
316
        l[cols-lineCol] = '\0';
317
        printf("%s", l);
318
        l[cols-lineCol] = c;
319
        l += cols-lineCol;
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
 
325
      printf("%s", l); /* print out this line */
326
      *t = c;          /* restore value */
327
      l = t;           /* mark beginning of next line */
328
 
329
      lineCnt--;  lineCol = 0;
2049 mateusz.vi 330
      if (!lineCnt) { lineCnt= rows;  fflush(NULL);  fprintf(stderr, "%s", pauseMsg);  waitkey(); }
2019 mateusz.vi 331
    }
332
    printf("%s", l);   /* print rest of string that lacks newline */
333
    lineCol = strlen(l);
334
  }
335
  else  /* NOPAUSE */
336
    printf("%s", buffer);
337
 
338
  return cnt;
339
}
340
 
341
 
342
/* Displays to user valid options then exits program indicating no error */
2037 mateusz.vi 343
static void showUsage(void) {
2019 mateusz.vi 344
  printf("%s%s%s%s", treeDescription, newLine, treeUsage, newLine);
345
  printf("%s%s%s", treeFOption, treeAOption, newLine);
346
  exit(1);
347
}
348
 
349
 
350
/* Displays error message then exits indicating error */
2037 mateusz.vi 351
static void showInvalidUsage(char * badOption) {
2019 mateusz.vi 352
  printf(invalidOption, badOption);
353
  printf("%s%s", useTreeHelp, newLine);
354
  exit(1);
355
}
356
 
357
 
358
/* Displays author, copyright, etc info, then exits indicating no error. */
2037 mateusz.vi 359
static void showVersionInfo(void) {
2019 mateusz.vi 360
  printf("%s%s%s%s%s", treeDescription, newLine, treeGoal, treePlatforms, newLine);
361
  printf(version, VERSION);
362
  printf("%s%s%s%s%s", writtenBy, writtenDate, contact, newLine, newLine);
363
  printf("%s%s", copyright, newLine);
364
  exit(1);
365
}
366
 
367
 
368
/* Displays error messge for invalid drives and exits */
2037 mateusz.vi 369
static void showInvalidDrive(void) {
2019 mateusz.vi 370
  printf(invalidDrive);
371
  exit(1);
372
}
373
 
374
 
375
/* Takes a fullpath, splits into drive (C:, or \\server\share) and path */
2037 mateusz.vi 376
static void splitpath(char *fullpath, char *drive, char *path);
2019 mateusz.vi 377
 
378
/**
379
 * Takes a given path, strips any \ or / that may appear on the end.
380
 * Returns a pointer to its static buffer containing path
381
 * without trailing slash and any necessary display conversions.
382
 */
2037 mateusz.vi 383
static char *fixPathForDisplay(char *path);
2019 mateusz.vi 384
 
385
/* Displays error message for invalid path; Does NOT exit */
2037 mateusz.vi 386
static void showInvalidPath(char *path) {
2055 mateusz.vi 387
  char partialPath[PATH_MAX], dummy[PATH_MAX];
2019 mateusz.vi 388
 
389
  pprintf("%s\n", path);
390
  splitpath(path, dummy, partialPath);
391
  pprintf(invalidPath, fixPathForDisplay(partialPath));
392
}
393
 
394
/* Displays error message for out of memory; Does NOT exit */
2037 mateusz.vi 395
static void showOutOfMemory(char *path) {
2019 mateusz.vi 396
  pprintf(outOfMemory, path);
397
}
398
 
399
 
400
/**
401
 * Takes a fullpath, splits into drive (C:, or \\server\share) and path
402
 * It assumes a colon as the 2nd character means drive specified,
403
 * a double slash \\ (\\, //, \/, or /\) specifies network share.
404
 * If neither drive nor network share, then assumes whole fullpath
405
 * is path, and sets drive to "".
406
 * If drive specified, then set drive to it and colon, eg "C:", with
407
 * the rest of fullpath being set in path.
408
 * If network share, the slash slash followed by the server name,
409
 * another slash and either the rest of fullpath or up to, but not
410
 * including, the next slash are placed in drive, eg "\\KJD\myshare";
411
 * the rest of the fullpath including the slash are placed in
412
 * path, eg "\mysubdir"; where fullpath is "\\KJD\myshare\mysubdir".
413
 * None of these may be NULL, and drive and path must be large
414
 * enough to hold fullpath.
415
 */
2037 mateusz.vi 416
static void splitpath(char *fullpath, char *drive, char *path) {
2027 mateusz.vi 417
  char *src = fullpath;
418
  char oldchar;
2019 mateusz.vi 419
 
420
  /* If either network share or path only starting at root directory */
421
  if ( (*src == '\\') || (*src == '/') )
422
  {
423
    src++;
424
 
425
    if ( (*src == '\\') || (*src == '/') ) /* network share */
426
    {
427
      src++;
428
 
429
      /* skip past server name */
430
      while ( (*src != '\\') && (*src != '/') && (*src != '\0') )
431
        src++;
432
 
433
      /* skip past slash (\ or /) separating  server from share */
434
      if (*src != '\0') src++;
435
 
436
      /* skip past share name */
437
      while ( (*src != '\\') && (*src != '/') && (*src != '\0') )
438
        src++;
439
 
440
      /* src points to start of path, either a slash or '\0' */
441
      oldchar = *src;
442
      *src = '\0';
443
 
444
      /* copy server name to drive */
445
      strcpy(drive, fullpath);
446
 
447
      /* restore character used to mark end of server name */
448
      *src = oldchar;
449
 
450
      /* copy path */
451
      strcpy(path, src);
452
    }
453
    else /* path only starting at root directory */
454
    {
455
      /* no drive, so set path to same as fullpath */
456
      strcpy(drive, "");
457
      strcpy(path, fullpath);
458
    }
459
  }
460
  else
461
  {
462
    if (*src != '\0') src++;
463
 
464
    /* Either drive and path or path only */
465
    if (*src == ':')
466
    {
467
      /* copy drive specified */
468
      *drive = *fullpath;  drive++;
469
      *drive = ':';        drive++;
470
      *drive = '\0';
471
 
472
      /* copy path */
473
      src++;
474
      strcpy(path, src);
475
    }
476
    else
477
    {
478
      /* no drive, so set path to same as fullpath */
479
      strcpy(drive, "");
480
      strcpy(path, fullpath);
481
    }
482
  }
483
}
484
 
485
 
486
/* Parses the command line and sets global variables. */
2037 mateusz.vi 487
static void parseArguments(int argc, char *argv[]) {
2027 mateusz.vi 488
  int i;     /* temp loop variable */
2019 mateusz.vi 489
 
490
  /* if no drive specified on command line, use current */
2052 mateusz.vi 491
  if (truename(path, ".") != 0) showInvalidDrive();
2019 mateusz.vi 492
 
493
  for (i = 1; i < argc; i++)
494
  {
495
    /* Check if user is giving an option or drive/path */
496
    if ((argv[i][0] == optionchar1) || (argv[i][0] == optionchar2) )
497
    {
498
      /* check multi character options 1st */
499
      if ((argv[i][1] == OptDisplay[0]) || (argv[i][1] == OptDisplay[1]))
500
      {
501
        switch (argv[i][2] & 0xDF)
502
        {
503
          case 'A' :       /*  /DA  display attributes */
504
            dspAttr = 1;
505
            break;
506
          case 'F' :       /*  /DF  display filesizes  */
507
            dspSize = 1;
508
            break;
509
          case 'H' :       /*  /DH  display hidden & system files (normally not shown) */
510
            dspAll = 1;
511
            break;
512
          case 'R' :       /*  /DR  display results at end */
513
            dspSumDirs = 1;
514
            break;
515
          default:
516
            showInvalidUsage(argv[i]);
517
        }
518
      }
519
      else /* a 1 character option (or invalid) */
520
      {
521
        if (argv[i][2] != '\0')
522
          showInvalidUsage(argv[i]);
523
 
524
        /* Must check both uppercase and lowercase                        */
525
        if ((argv[i][1] == OptShowFiles[0]) || (argv[i][1] == OptShowFiles[1]))
526
          showFiles = SHOWFILESON; /* set file display flag appropriately */
527
        else if ((argv[i][1] == OptUseASCII[0]) || (argv[i][1] == OptUseASCII[1]))
528
          charSet = ASCIICHARS;    /* set charset flag appropriately      */
529
        else if (argv[i][1] == '?')
530
          showUsage();             /* show usage info and exit            */
531
        else if ((argv[i][1] == OptVersion[0]) || (argv[i][1] == OptVersion[1]))
532
          showVersionInfo();       /* show version info and exit          */
533
        else if ((argv[i][1] == OptPause[0]) || (argv[i][1] == OptPause[1]))
534
          pause = PAUSE;     /* wait for keypress after each page (pause) */
535
        else /* Invalid or unknown option */
536
          showInvalidUsage(argv[i]);
537
      }
2052 mateusz.vi 538
    } else { /* should be a drive/path */
539
      if (truename(path, argv[i]) != 0) showInvalidDrive();
2019 mateusz.vi 540
    }
541
  }
542
}
543
 
544
 
545
/**
546
 * Fills in the serial and volume variables with the serial #
547
 * and volume found using path.
548
 */
2037 mateusz.vi 549
static void GetVolumeAndSerial(char *volume, char *serial, char *path) {
2061 mateusz.vi 550
  getdrvserial((path[0] & 0xDF) - '@');
551
  memcpy(volume, glob_drv_info.label, 12);
552
  volume[11] = 0;
2019 mateusz.vi 553
 
2061 mateusz.vi 554
  sprintf(serial, "%04X:%04X", glob_drv_info.serial1, glob_drv_info.serial2);
2019 mateusz.vi 555
}
556
 
557
 
558
/**
559
 * Stores directory information obtained from FindFirst/Next that
560
 * we may wish to make use of when displaying directory entry.
561
 * e.g. attribute, dates, etc.
562
 */
2054 mateusz.vi 563
typedef struct DIRDATA {
564
  unsigned long subdirCnt;  /* how many subdirectories we have */
565
  unsigned long fileCnt;    /* how many [normal] files we have */
2056 mateusz.vi 566
  unsigned int attrib;      /* Directory attributes            */
2019 mateusz.vi 567
} DIRDATA;
568
 
569
/**
570
 * Contains the information stored in a Stack necessary to allow
571
 * non-recursive function to display directory tree.
572
 */
573
typedef struct SUBDIRINFO
574
{
575
  struct SUBDIRINFO * parent; /* points to parent subdirectory                */
576
  char *currentpath;    /* Stores the full path this structure represents     */
577
  char *subdir;         /* points to last subdir within currentpath           */
578
  char *dsubdir;        /* Stores a display ready directory name              */
579
  long subdircnt;       /* Initially a count of how many subdirs in this dir  */
2060 mateusz.vi 580
  struct find_t *findnexthnd; /* The handle returned by findfirst, used in findnext */
2019 mateusz.vi 581
  struct DIRDATA ddata; /* Maintain directory information, eg attributes      */
582
} SUBDIRINFO;
583
 
584
 
585
/**
586
 * Returns 0 if no subdirectories, count if has subdirs.
587
 * Path must end in slash \ or /
588
 * On error (invalid path) displays message and returns -1L.
589
 * Stores additional directory data in ddata if non-NULL
590
 * and path is valid.
591
 */
2037 mateusz.vi 592
static long hasSubdirectories(char *path, DIRDATA *ddata) {
2060 mateusz.vi 593
  struct find_t findData;
594
  char buffer[PATH_MAX + 4];
2019 mateusz.vi 595
  int hasSubdirs = 0;
596
 
597
  /* get the handle to start with (using wildcard spec) */
598
  strcpy(buffer, path);
2060 mateusz.vi 599
  strcat(buffer, "*.*");
2019 mateusz.vi 600
 
2060 mateusz.vi 601
  if (_dos_findfirst(buffer, 0x37, &findData) != 0) {
2019 mateusz.vi 602
    showInvalidPath(path); /* Display error message */
2048 mateusz.vi 603
    return(-1);
2019 mateusz.vi 604
  }
605
 
606
  /*  cycle through entries counting directories found until no more entries */
607
  do {
2061 mateusz.vi 608
    if (((findData.attrib & _A_SUBDIR) != 0) &&
2058 mateusz.vi 609
        ((findData.attrib &
2061 mateusz.vi 610
         (_A_HIDDEN | _A_SYSTEM)) == 0 || dspAll) ) {
2058 mateusz.vi 611
      if (findData.name[0] != '.') { /* ignore '.' and '..' */
2019 mateusz.vi 612
        hasSubdirs++;      /* subdir of initial path found, so increment counter */
2047 mateusz.vi 613
      }
2019 mateusz.vi 614
    }
2060 mateusz.vi 615
  } while(_dos_findnext(&findData) == 0);
2019 mateusz.vi 616
 
617
  /* prevent resource leaks, close the handle. */
2060 mateusz.vi 618
  _dos_findclose(&findData);
2019 mateusz.vi 619
 
620
  if (ddata != NULL)  // don't bother if user doesn't want them
621
  {
622
    /* The root directory of a volume (including non root paths
623
       corresponding to mount points) may not have a current (.) and
624
       parent (..) entry.  So we can't get attributes for initial
2022 mateusz.vi 625
       path in above loop from the FindFile call as it may not show up
2019 mateusz.vi 626
       (no . entry).  So instead we explicitly get them here.
627
    */
2056 mateusz.vi 628
    if (_dos_getfileattr(path, &(ddata->attrib)) != 0) {
2019 mateusz.vi 629
      //printf("ERROR: unable to get file attr, %i\n", GetLastError());
2047 mateusz.vi 630
      ddata->attrib = 0;
2019 mateusz.vi 631
    }
632
 
633
    /* a curiosity, for showing sum of directories process */
634
    ddata->subdirCnt = hasSubdirs;
635
  }
636
  totalSubDirCnt += hasSubdirs;
637
 
638
  return hasSubdirs;
639
}
640
 
641
 
642
/**
643
 * Allocates memory and stores the necessary stuff to allow us to
644
 * come back to this subdirectory after handling its subdirectories.
645
 * parentpath must end in \ or / or be NULL, however
646
 * parent should only be NULL for initialpath
647
 * if subdir does not end in slash, one is added to stored subdir
648
 * dsubdir is subdir already modified so ready to display to user
649
 */
2037 mateusz.vi 650
static SUBDIRINFO *newSubdirInfo(SUBDIRINFO *parent, char *subdir, char *dsubdir) {
2027 mateusz.vi 651
  int parentLen, subdirLen;
2032 mateusz.vi 652
  SUBDIRINFO *temp;
2019 mateusz.vi 653
 
654
  /* Get length of parent directory */
655
  if (parent == NULL)
656
    parentLen = 0;
657
  else
658
    parentLen = strlen(parent->currentpath);
659
 
660
  /* Get length of subdir, add 1 if does not end in slash */
661
  subdirLen = strlen(subdir);
662
  if ((subdirLen < 1) || ( (*(subdir+subdirLen-1) != '\\') && (*(subdir+subdirLen-1) != '/') ) )
663
    subdirLen++;
664
 
2032 mateusz.vi 665
  temp = (SUBDIRINFO *)malloc(sizeof(SUBDIRINFO));
2022 mateusz.vi 666
  if (temp == NULL)
2019 mateusz.vi 667
  {
668
    showOutOfMemory(subdir);
669
    return NULL;
670
  }
671
  if ( ((temp->currentpath = (char *)malloc(parentLen+subdirLen+1)) == NULL) ||
672
       ((temp->dsubdir = (char *)malloc(strlen(dsubdir)+1)) == NULL) )
673
  {
674
    showOutOfMemory(subdir);
675
    if (temp->currentpath != NULL) free(temp->currentpath);
676
    free(temp);
677
    return NULL;
678
  }
679
  temp->parent = parent;
680
  if (parent == NULL)
681
    strcpy(temp->currentpath, "");
682
  else
683
    strcpy(temp->currentpath, parent->currentpath);
684
  strcat(temp->currentpath, subdir);
685
  /* if subdir[subdirLen-1] == '\0' then we must append a slash */
686
  if (*(subdir+subdirLen-1) == '\0')
687
    strcat(temp->currentpath, "\\");
688
  temp->subdir = temp->currentpath+parentLen;
689
  strcpy(temp->dsubdir, dsubdir);
690
  if ((temp->subdircnt = hasSubdirectories(temp->currentpath, &(temp->ddata))) == -1L)
691
  {
692
    free (temp->currentpath);
693
    free (temp->dsubdir);
694
    free(temp);
695
    return NULL;
696
  }
2048 mateusz.vi 697
  temp->findnexthnd = NULL;
2019 mateusz.vi 698
 
699
  return temp;
700
}
701
 
2048 mateusz.vi 702
 
2019 mateusz.vi 703
/**
704
 * Extends the padding with the necessary 4 characters.
705
 * Returns the pointer to the padding.
2022 mateusz.vi 706
 * padding should be large enough to hold the additional
2019 mateusz.vi 707
 * characters and '\0', moreSubdirsFollow specifies if
708
 * this is the last subdirectory in a given directory
709
 * or if more follow (hence if a | is needed).
710
 * padding must not be NULL
711
 */
2037 mateusz.vi 712
static char * addPadding(char *padding, int moreSubdirsFollow) {
713
  if (moreSubdirsFollow) {
714
    /* 1st char is | or a vertical bar */
715
    if (charSet == EXTENDEDCHARS) {
716
      strcat(padding, VERTBAR_STR);
717
    } else {
718
      strcat(padding, "|   ");
2019 mateusz.vi 719
    }
2037 mateusz.vi 720
  } else {
721
    strcat(padding, "    ");
722
  }
2019 mateusz.vi 723
 
2037 mateusz.vi 724
  return(padding);
2019 mateusz.vi 725
}
726
 
727
/**
2025 mateusz.vi 728
 * Removes the last padding added (last 4 characters added).
729
 * Does nothing if less than 4 characters in string.
2019 mateusz.vi 730
 * padding must not be NULL
731
 * Returns the pointer to padding.
732
 */
2037 mateusz.vi 733
static char *removePadding(char *padding) {
2027 mateusz.vi 734
  size_t len = strlen(padding);
2019 mateusz.vi 735
 
2025 mateusz.vi 736
  if (len < 4) return padding;
737
  *(padding + len - 4) = '\0';
2019 mateusz.vi 738
 
739
  return padding;
740
}
741
 
742
/**
743
 * Takes a given path, strips any \ or / that may appear on the end.
744
 * Returns a pointer to its static buffer containing path
745
 * without trailing slash and any necessary display conversions.
746
 */
2037 mateusz.vi 747
static char *fixPathForDisplay(char *path) {
2055 mateusz.vi 748
  static char buffer[PATH_MAX];
2027 mateusz.vi 749
  int pathlen;
2019 mateusz.vi 750
 
751
  strcpy(buffer, path);
752
  pathlen = strlen(buffer);
2037 mateusz.vi 753
  if (pathlen > 1) {
2019 mateusz.vi 754
    pathlen--;
2037 mateusz.vi 755
    if ((buffer[pathlen] == '\\') || (buffer[pathlen] == '/')) {
2019 mateusz.vi 756
      buffer[pathlen] = '\0'; // strip off trailing slash on end
2037 mateusz.vi 757
    }
2019 mateusz.vi 758
  }
759
 
760
  return buffer;
761
}
762
 
763
/**
764
 * Displays the current path, with necessary padding before it.
765
 * A \ or / on end of currentpath is not shown.
766
 * moreSubdirsFollow should be nonzero if this is not the last
767
 * subdirectory to be displayed in current directory, else 0.
768
 * Also displays additional information, such as attributes or
769
 * sum of size of included files.
770
 * currentpath is an ASCIIZ string of path to display
771
 *             assumed to be a displayable path (ie. OEM or UTF-8)
772
 * padding is an ASCIIZ string to display prior to entry.
773
 * moreSubdirsFollow is -1 for initial path else >= 0.
774
 */
2037 mateusz.vi 775
static void showCurrentPath(char *currentpath, char *padding, int moreSubdirsFollow, DIRDATA *ddata) {
2019 mateusz.vi 776
  if (padding != NULL)
777
    pprintf("%s", padding);
778
 
779
  /* print lead padding except for initial directory */
780
  if (moreSubdirsFollow >= 0)
781
  {
782
    if (charSet == EXTENDEDCHARS)
783
    {
784
      if (moreSubdirsFollow)
785
        pprintf("%s", TBAR_HORZBAR_STR);
786
      else
787
        pprintf("%s", CBAR_HORZBAR_STR);
2037 mateusz.vi 788
    } else {
2019 mateusz.vi 789
      if (moreSubdirsFollow)
790
        pprintf("+---");
791
      else
792
        pprintf("\\---");
793
    }
794
  }
795
 
796
  /* optional display data */
797
  if (dspAttr)  /* attributes */
2031 mateusz.vi 798
    pprintf("[%c%c%c%c%c] ",
2061 mateusz.vi 799
      (ddata->attrib & _A_SUBDIR)?'D':' ',  /* keep this one? its always true */
800
      (ddata->attrib & _A_ARCH)?'A':' ',
801
      (ddata->attrib & _A_SYSTEM)?'S':' ',
802
      (ddata->attrib & _A_HIDDEN)?'H':' ',
803
      (ddata->attrib & _A_RDONLY)?'R':' '
2019 mateusz.vi 804
    );
805
 
806
  /* display directory name */
807
  pprintf("%s\n", currentpath);
808
}
809
 
810
 
2022 mateusz.vi 811
/**
2019 mateusz.vi 812
 * Displays summary information about directory.
813
 * Expects to be called after displayFiles (optionally called)
814
 */
2037 mateusz.vi 815
static void displaySummary(char *padding, int hasMoreSubdirs, DIRDATA *ddata) {
2019 mateusz.vi 816
  addPadding(padding, hasMoreSubdirs);
817
 
2037 mateusz.vi 818
  if (dspSumDirs) {
819
    if (showFiles == SHOWFILESON) {
2019 mateusz.vi 820
      /* print File summary with lead padding, add filesize to it */
821
      pprintf("%s%lu files\n", padding, ddata->fileCnt);
822
    }
823
 
824
    /* print Directory summary with lead padding */
825
    pprintf("%s%lu subdirectories\n", padding, ddata->subdirCnt);
826
 
827
    /* show [nearly] blank line after summary */
828
    pprintf("%s\n", padding);
829
  }
830
 
831
  removePadding(padding);
832
}
833
 
2022 mateusz.vi 834
/**
2019 mateusz.vi 835
 * Displays files in directory specified by path.
836
 * Path must end in slash \ or /
837
 * Returns -1 on error,
838
 *          0 if no files, but no errors either,
839
 *      or  1 if files displayed, no errors.
840
 */
2047 mateusz.vi 841
static int displayFiles(const char *path, char *padding, int hasMoreSubdirs, DIRDATA *ddata) {
2060 mateusz.vi 842
  char buffer[PATH_MAX + 4];
843
  struct find_t entry;   /* current directory entry info    */
2019 mateusz.vi 844
  unsigned long filesShown = 0;
845
 
846
  /* get handle for files in current directory (using wildcard spec) */
847
  strcpy(buffer, path);
2060 mateusz.vi 848
  strcat(buffer, "*.*");
849
  if (_dos_findfirst(buffer, 0x37, &entry) != 0) return(-1);
2019 mateusz.vi 850
 
851
  addPadding(padding, hasMoreSubdirs);
852
 
853
  /* cycle through directory printing out files. */
2022 mateusz.vi 854
  do
2019 mateusz.vi 855
  {
856
    /* print padding followed by filename */
2061 mateusz.vi 857
    if ( ((entry.attrib & _A_SUBDIR) == 0) &&
858
         ( ((entry.attrib & (_A_HIDDEN | _A_SYSTEM)) == 0)  || dspAll) )
2019 mateusz.vi 859
    {
860
      /* print lead padding */
861
      pprintf("%s", padding);
862
 
863
      /* optional display data */
864
      if (dspAttr)  /* file attributes */
2031 mateusz.vi 865
        pprintf("[%c%c%c%c] ",
2061 mateusz.vi 866
          (entry.attrib & _A_ARCH)?'A':' ',
867
          (entry.attrib & _A_SYSTEM)?'S':' ',
868
          (entry.attrib & _A_HIDDEN)?'H':' ',
869
          (entry.attrib & _A_RDONLY)?'R':' '
2019 mateusz.vi 870
        );
871
 
2053 mateusz.vi 872
      if (dspSize) { /* file size */
2058 mateusz.vi 873
        if (entry.size < 1048576ul)  /* if less than a MB, display in bytes */
874
          pprintf("%10lu ", entry.size);
2053 mateusz.vi 875
        else                               /* otherwise display in KB */
2058 mateusz.vi 876
          pprintf("%8luKB ", entry.size / 1024ul);
2019 mateusz.vi 877
      }
878
 
879
      /* print filename */
2058 mateusz.vi 880
      pprintf("%s\n", entry.name);
2019 mateusz.vi 881
 
882
      filesShown++;
883
    }
2060 mateusz.vi 884
  } while(_dos_findnext(&entry) == 0);
2019 mateusz.vi 885
 
886
  if (filesShown)
887
  {
888
    pprintf("%s\n", padding);
889
  }
890
 
891
  removePadding(padding);
892
 
893
  /* store for summary display */
894
  if (ddata != NULL) ddata->fileCnt = filesShown;
895
 
896
  return (filesShown)? 1 : 0;
897
}
898
 
899
 
900
/**
901
 * Common portion of findFirstSubdir and findNextSubdir
902
 * Checks current FindFile results to determine if a valid directory
903
 * was found, and if so copies appropriate data into subdir and dsubdir.
904
 * It will repeat until a valid subdirectory is found or no more
905
 * are found, at which point it closes the FindFile search handle and
2048 mateusz.vi 906
 * return NULL.  If successful, returns FindFile handle.
2019 mateusz.vi 907
 */
2060 mateusz.vi 908
static struct find_t *cycleFindResults(struct find_t *entry, char *subdir, char *dsubdir) {
2025 mateusz.vi 909
  /* cycle through directory until 1st non . or .. directory is found. */
2057 mateusz.vi 910
  for (;;) {
2025 mateusz.vi 911
    /* skip files & hidden or system directories */
2061 mateusz.vi 912
    if ((((entry->attrib & _A_SUBDIR) == 0) ||
2058 mateusz.vi 913
         ((entry->attrib &
2061 mateusz.vi 914
          (_A_HIDDEN | _A_SYSTEM)) != 0  && !dspAll) ) ||
2058 mateusz.vi 915
        (entry->name[0] == '.')) {
2060 mateusz.vi 916
      if (_dos_findnext(entry) != 0) {
917
        _dos_findclose(entry);      // prevent resource leaks
2048 mateusz.vi 918
        return(NULL); // no subdirs found
2019 mateusz.vi 919
      }
2048 mateusz.vi 920
    } else {
2025 mateusz.vi 921
      /* set display name */
2058 mateusz.vi 922
      strcpy(dsubdir, entry->name);
2019 mateusz.vi 923
 
2058 mateusz.vi 924
      strcpy(subdir, entry->name);
2025 mateusz.vi 925
      strcat(subdir, "\\");
2057 mateusz.vi 926
      return(entry);
2025 mateusz.vi 927
    }
2057 mateusz.vi 928
  }
2025 mateusz.vi 929
 
2057 mateusz.vi 930
  return entry;
2019 mateusz.vi 931
}
932
 
933
 
934
/**
935
 * Given the current path, find the 1st subdirectory.
936
 * The subdirectory found is stored in subdir.
937
 * subdir is cleared on error or no subdirectories.
938
 * Returns the findfirst search HANDLE, which should be passed to
939
 * findclose when directory has finished processing, and can be
940
 * passed to findnextsubdir to find subsequent subdirectories.
2048 mateusz.vi 941
 * Returns NULL on error.
2019 mateusz.vi 942
 * currentpath must end in \
943
 */
2060 mateusz.vi 944
static struct find_t *findFirstSubdir(char *currentpath, char *subdir, char *dsubdir) {
945
  char buffer[PATH_MAX + 4];
946
  struct find_t *dir;         /* Current directory entry working with      */
2019 mateusz.vi 947
 
2060 mateusz.vi 948
  dir = malloc(sizeof(struct find_t));
2057 mateusz.vi 949
  if (dir == NULL) return(NULL);
950
 
2019 mateusz.vi 951
  /* get handle for files in current directory (using wildcard spec) */
952
  strcpy(buffer, currentpath);
2060 mateusz.vi 953
  strcat(buffer, "*.*");
2019 mateusz.vi 954
 
2060 mateusz.vi 955
  if (_dos_findfirst(buffer, 0x37, dir) != 0) {
2019 mateusz.vi 956
    showInvalidPath(currentpath);
2048 mateusz.vi 957
    return(NULL);
2019 mateusz.vi 958
  }
959
 
960
  /* clear result path */
961
  strcpy(subdir, "");
962
 
2057 mateusz.vi 963
  return cycleFindResults(dir, subdir, dsubdir);
2019 mateusz.vi 964
}
965
 
966
/**
2022 mateusz.vi 967
 * Given a search HANDLE, will find the next subdirectory,
2019 mateusz.vi 968
 * setting subdir to the found directory name.
2045 mateusz.vi 969
 * dsubdir is the name to display
2019 mateusz.vi 970
 * currentpath must end in \
971
 * If a subdirectory is found, returns 0, otherwise returns 1
972
 * (either error or no more files).
973
 */
2060 mateusz.vi 974
static int findNextSubdir(struct find_t *findnexthnd, char *subdir, char *dsubdir) {
2019 mateusz.vi 975
  /* clear result path */
2057 mateusz.vi 976
  subdir[0] = 0;
2019 mateusz.vi 977
 
2060 mateusz.vi 978
  if (_dos_findnext(findnexthnd) != 0) return(1); // no subdirs found
2019 mateusz.vi 979
 
2057 mateusz.vi 980
  if (cycleFindResults(findnexthnd, subdir, dsubdir) == NULL) {
2019 mateusz.vi 981
    return 1;
2048 mateusz.vi 982
  }
983
  return 0;
2019 mateusz.vi 984
}
985
 
986
/**
987
 * Given an initial path, displays the directory tree with
988
 * a non-recursive function using a Stack.
989
 * initialpath must be large enough to hold an added slash \ or /
990
 * if it does not already end in one.
991
 * Returns the count of subdirs in initialpath.
992
 */
2037 mateusz.vi 993
static long traverseTree(char *initialpath) {
2019 mateusz.vi 994
  long subdirsInInitialpath;
995
  char padding[MAXPADLEN] = "";
2055 mateusz.vi 996
  char subdir[PATH_MAX];
997
  char dsubdir[PATH_MAX];
2027 mateusz.vi 998
  SUBDIRINFO *sdi;
2019 mateusz.vi 999
 
1000
  STACK s;
1001
  stackDefaults(&s);
1002
  stackInit(&s);
1003
 
2048 mateusz.vi 1004
  if ( (sdi = newSubdirInfo(NULL, initialpath, initialpath)) == NULL) {
1005
    return(0);
1006
  }
2019 mateusz.vi 1007
  stackPushItem(&s, sdi);
1008
 
1009
  /* Store count of subdirs in initial path so can display message if none. */
1010
  subdirsInInitialpath = sdi->subdircnt;
1011
 
1012
  do
1013
  {
1014
    sdi = (SUBDIRINFO *)stackPopItem(&s);
1015
 
2048 mateusz.vi 1016
    if (sdi->findnexthnd == NULL) { // findfirst not called yet
2019 mateusz.vi 1017
      // 1st time this subdirectory processed, so display its name & possibly files
1018
      if (sdi->parent == NULL) // if initial path
1019
      {
1020
        // display initial path
1021
        showCurrentPath(/*sdi->dsubdir*/initialpath, NULL, -1, &(sdi->ddata));
1022
      }
1023
      else // normal processing (display path, add necessary padding)
1024
      {
1025
        showCurrentPath(sdi->dsubdir, padding, (sdi->parent->subdircnt > 0L)?1 : 0, &(sdi->ddata));
1026
        addPadding(padding, (sdi->parent->subdircnt > 0L)?1 : 0);
1027
      }
1028
 
1029
      if (showFiles == SHOWFILESON)  displayFiles(sdi->currentpath, padding, (sdi->subdircnt > 0L)?1 : 0, &(sdi->ddata));
2024 mateusz.vi 1030
      displaySummary(padding, (sdi->subdircnt > 0L)?1 : 0, &(sdi->ddata));
2019 mateusz.vi 1031
    }
1032
 
1033
    if (sdi->subdircnt > 0) /* if (there are more subdirectories to process) */
1034
    {
1035
      int flgErr;
2048 mateusz.vi 1036
      if (sdi->findnexthnd == NULL) {
2019 mateusz.vi 1037
        sdi->findnexthnd = findFirstSubdir(sdi->currentpath, subdir, dsubdir);
2048 mateusz.vi 1038
        flgErr = (sdi->findnexthnd == NULL);
1039
      } else {
2019 mateusz.vi 1040
        flgErr = findNextSubdir(sdi->findnexthnd, subdir, dsubdir);
1041
      }
1042
 
1043
      if (flgErr) // don't add invalid paths to stack
1044
      {
1045
        printf("INTERNAL ERROR: subdir count changed, expecting %li more!\n", sdi->subdircnt+1L);
1046
 
1047
        sdi->subdircnt = 0; /* force subdir counter to 0, none left */
1048
        stackPushItem(&s, sdi);
1049
      }
1050
      else
1051
      {
1052
        sdi->subdircnt = sdi->subdircnt - 1L; /* decrement subdirs left count */
1053
        stackPushItem(&s, sdi);
1054
 
1055
        /* store necessary information, validate subdir, and if no error store it. */
1056
        if ((sdi = newSubdirInfo(sdi, subdir, dsubdir)) != NULL)
1057
          stackPushItem(&s, sdi);
1058
      }
1059
    }
1060
    else /* this directory finished processing, so free resources */
1061
    {
1062
      /* Remove the padding for this directory, all but initial path. */
1063
      if (sdi->parent != NULL)
1064
        removePadding(padding);
1065
 
1066
      /* Prevent resource leaks, by ending findsearch and freeing memory. */
2060 mateusz.vi 1067
      _dos_findclose(sdi->findnexthnd);
2019 mateusz.vi 1068
      if (sdi != NULL)
1069
      {
1070
        if (sdi->currentpath != NULL)
1071
          free(sdi->currentpath);
1072
        free(sdi);
1073
      }
1074
    }
1075
  } while (stackTotalItems(&s)); /* while (stack is not empty) */
1076
 
1077
  stackTerm(&s);
1078
 
1079
  return subdirsInInitialpath;
1080
}
1081
 
1082
 
2037 mateusz.vi 1083
static void FixOptionText(void) {
2019 mateusz.vi 1084
  char buffer[MAXLINE];  /* sprintf can have problems with src==dest */
1085
 
1086
  /* Handle %c for options within messages using Set 8 */
1087
  strcpy(buffer, treeUsage);
1088
  sprintf(treeUsage, buffer, optionchar1, OptShowFiles[0], optionchar1, OptUseASCII[0]);
1089
  strcpy(buffer, treeFOption);
1090
  sprintf(treeFOption, buffer, optionchar1, OptShowFiles[0]);
1091
  strcpy(buffer, treeAOption);
1092
  sprintf(treeAOption, buffer, optionchar1, OptUseASCII[0]);
1093
  strcpy(buffer, useTreeHelp);
1094
  sprintf(useTreeHelp, buffer, optionchar1);
1095
}
1096
 
1097
 
2022 mateusz.vi 1098
/* Loads all messages from the message catalog. */
2037 mateusz.vi 1099
static void loadAllMessages(void) {
2019 mateusz.vi 1100
  /* Changes %c in certain lines with proper option characters. */
1101
  FixOptionText();
1102
}
1103
 
1104
 
2037 mateusz.vi 1105
int main(int argc, char **argv) {
2019 mateusz.vi 1106
  char serial[SERIALLEN]; /* volume serial #  0000:0000 */
1107
  char volume[VOLLEN];    /* volume name (label), possibly none */
1108
 
1109
  /* Load all text from message catalog (or uses hard coded text) */
1110
  loadAllMessages();
1111
 
1112
  /* Parse any command line arguments, obtain path */
1113
  parseArguments(argc, argv);
1114
 
1115
  /* Initialize screen size, may reset pause to NOPAUSE if redirected */
1116
  getConsoleSize();
1117
 
1118
  /* Get Volume & Serial Number */
1119
  GetVolumeAndSerial(volume, serial, path);
1120
  if (strlen(volume) == 0)
1121
    pprintf(pathListingNoLabel);
1122
  else
1123
    pprintf(pathListingWithLabel, volume);
1124
  if (serial[0] != '\0')  /* Don't print anything if no serial# found */
1125
    pprintf(serialNumber, serial);
1126
 
1127
  /* now traverse & print tree, returns nonzero if has subdirectories */
1128
  if (traverseTree(path) == 0)
1129
    pprintf(noSubDirs);
1130
  else if (dspSumDirs) /* show count of directories processed */
1131
    pprintf("\n    %lu total directories\n", totalSubDirCnt+1);
1132
 
1133
  return 0;
1134
}