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