Subversion Repositories SvarDOS

Rev

Rev 2043 | Rev 2045 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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