Subversion Repositories SvarDOS

Rev

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