Subversion Repositories SvarDOS

Rev

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