Subversion Repositories SvarDOS

Rev

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