Subversion Repositories SvarDOS

Rev

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