Subversion Repositories SvarDOS

Rev

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