Subversion Repositories SvarDOS

Rev

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