Subversion Repositories SvarDOS

Rev

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