Subversion Repositories SvarDOS

Rev

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