Subversion Repositories SvarDOS

Rev

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