Subversion Repositories SvarDOS

Rev

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