Subversion Repositories SvarDOS

Rev

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