Subversion Repositories SvarDOS

Rev

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

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