Subversion Repositories SvarDOS

Rev

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