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
{
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
 
613
/**
614
 * Stores directory information obtained from FindFirst/Next that
615
 * we may wish to make use of when displaying directory entry.
616
 * e.g. attribute, dates, etc.
617
 */
618
typedef struct DIRDATA
619
{
620
  DWORD subdirCnt;          /* how many subdirectories we have */
621
  DWORD fileCnt;            /* how many [normal] files we have */
622
  DWORD dwDirAttributes;    /* Directory attributes            */
623
} DIRDATA;
624
 
625
/**
626
 * Contains the information stored in a Stack necessary to allow
627
 * non-recursive function to display directory tree.
628
 */
629
typedef struct SUBDIRINFO
630
{
631
  struct SUBDIRINFO * parent; /* points to parent subdirectory                */
632
  char *currentpath;    /* Stores the full path this structure represents     */
633
  char *subdir;         /* points to last subdir within currentpath           */
634
  char *dsubdir;        /* Stores a display ready directory name              */
635
  long subdircnt;       /* Initially a count of how many subdirs in this dir  */
636
  HANDLE findnexthnd;   /* The handle returned by findfirst, used in findnext */
637
  struct DIRDATA ddata; /* Maintain directory information, eg attributes      */
638
} SUBDIRINFO;
639
 
640
 
641
/**
642
 * Returns 0 if no subdirectories, count if has subdirs.
643
 * Path must end in slash \ or /
644
 * On error (invalid path) displays message and returns -1L.
645
 * Stores additional directory data in ddata if non-NULL
646
 * and path is valid.
647
 */
2030 mateusz.vi 648
long hasSubdirectories(char *path, DIRDATA *ddata)
2019 mateusz.vi 649
{
650
  static WIN32_FIND_DATA findData;
651
  HANDLE hnd;
652
  static char buffer[MAXBUF];
653
  int hasSubdirs = 0;
654
 
655
  /* get the handle to start with (using wildcard spec) */
656
  strcpy(buffer, path);
657
  strcat(buffer, "*");
658
 
659
  /* Use FindFirstFileEx when available (falls back to FindFirstFile).
660
   * Allows us to limit returned results to just directories
661
   * if supported by underlying filesystem.
662
   */
2030 mateusz.vi 663
  hnd = FindFirstFileA(buffer, &findData);
2019 mateusz.vi 664
  if (hnd == INVALID_HANDLE_VALUE)
665
  {
666
    showInvalidPath(path); /* Display error message */
667
    return -1L;
668
  }
669
 
670
 
671
  /*  cycle through entries counting directories found until no more entries */
672
  do {
673
    if (((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) &&
674
	((findData.dwFileAttributes &
675
	 (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) == 0 || dspAll) )
676
    {
677
      if ( (strcmp(findData.cFileName, ".") != 0) && /* ignore initial [current] path */
678
           (strcmp(findData.cFileName, "..") != 0) ) /* and ignore parent path */
679
        hasSubdirs++;      /* subdir of initial path found, so increment counter */
680
    }
681
  } while(FindNextFile(hnd, &findData) != 0);
682
 
683
  /* prevent resource leaks, close the handle. */
684
  FindClose(hnd);
685
 
686
  if (ddata != NULL)  // don't bother if user doesn't want them
687
  {
688
    /* The root directory of a volume (including non root paths
689
       corresponding to mount points) may not have a current (.) and
690
       parent (..) entry.  So we can't get attributes for initial
2022 mateusz.vi 691
       path in above loop from the FindFile call as it may not show up
2019 mateusz.vi 692
       (no . entry).  So instead we explicitly get them here.
693
    */
694
    if ((ddata->dwDirAttributes = GetFileAttributes(path)) == (DWORD)-1)
695
    {
696
      //printf("ERROR: unable to get file attr, %i\n", GetLastError());
697
      ddata->dwDirAttributes = 0;
698
    }
699
 
700
    /* a curiosity, for showing sum of directories process */
701
    ddata->subdirCnt = hasSubdirs;
702
  }
703
  totalSubDirCnt += hasSubdirs;
704
 
705
  return hasSubdirs;
706
}
707
 
708
 
709
/**
710
 * Allocates memory and stores the necessary stuff to allow us to
711
 * come back to this subdirectory after handling its subdirectories.
712
 * parentpath must end in \ or / or be NULL, however
713
 * parent should only be NULL for initialpath
714
 * if subdir does not end in slash, one is added to stored subdir
715
 * dsubdir is subdir already modified so ready to display to user
716
 */
717
SUBDIRINFO *newSubdirInfo(SUBDIRINFO *parent, char *subdir, char *dsubdir)
718
{
2027 mateusz.vi 719
  int parentLen, subdirLen;
2032 mateusz.vi 720
  SUBDIRINFO *temp;
2019 mateusz.vi 721
 
722
  /* Get length of parent directory */
723
  if (parent == NULL)
724
    parentLen = 0;
725
  else
726
    parentLen = strlen(parent->currentpath);
727
 
728
  /* Get length of subdir, add 1 if does not end in slash */
729
  subdirLen = strlen(subdir);
730
  if ((subdirLen < 1) || ( (*(subdir+subdirLen-1) != '\\') && (*(subdir+subdirLen-1) != '/') ) )
731
    subdirLen++;
732
 
2032 mateusz.vi 733
  temp = (SUBDIRINFO *)malloc(sizeof(SUBDIRINFO));
2022 mateusz.vi 734
  if (temp == NULL)
2019 mateusz.vi 735
  {
736
    showOutOfMemory(subdir);
737
    return NULL;
738
  }
739
  if ( ((temp->currentpath = (char *)malloc(parentLen+subdirLen+1)) == NULL) ||
740
       ((temp->dsubdir = (char *)malloc(strlen(dsubdir)+1)) == NULL) )
741
  {
742
    showOutOfMemory(subdir);
743
    if (temp->currentpath != NULL) free(temp->currentpath);
744
    free(temp);
745
    return NULL;
746
  }
747
  temp->parent = parent;
748
  if (parent == NULL)
749
    strcpy(temp->currentpath, "");
750
  else
751
    strcpy(temp->currentpath, parent->currentpath);
752
  strcat(temp->currentpath, subdir);
753
  /* if subdir[subdirLen-1] == '\0' then we must append a slash */
754
  if (*(subdir+subdirLen-1) == '\0')
755
    strcat(temp->currentpath, "\\");
756
  temp->subdir = temp->currentpath+parentLen;
757
  strcpy(temp->dsubdir, dsubdir);
758
  if ((temp->subdircnt = hasSubdirectories(temp->currentpath, &(temp->ddata))) == -1L)
759
  {
760
    free (temp->currentpath);
761
    free (temp->dsubdir);
762
    free(temp);
763
    return NULL;
764
  }
765
  temp->findnexthnd = INVALID_HANDLE_VALUE;
766
 
767
  return temp;
768
}
769
 
770
/**
771
 * Extends the padding with the necessary 4 characters.
772
 * Returns the pointer to the padding.
2022 mateusz.vi 773
 * padding should be large enough to hold the additional
2019 mateusz.vi 774
 * characters and '\0', moreSubdirsFollow specifies if
775
 * this is the last subdirectory in a given directory
776
 * or if more follow (hence if a | is needed).
777
 * padding must not be NULL
778
 */
779
char * addPadding(char *padding, int moreSubdirsFollow)
780
{
781
    if (moreSubdirsFollow)
782
    {
783
      /* 1st char is | or a vertical bar */
784
      if (charSet == EXTENDEDCHARS)
785
        strcat(padding, VERTBAR_STR);
786
      else
787
        strcat(padding, "|   ");
788
    }
789
    else
790
      strcat(padding, "    ");
791
 
792
    return padding;
793
}
794
 
795
/**
2025 mateusz.vi 796
 * Removes the last padding added (last 4 characters added).
797
 * Does nothing if less than 4 characters in string.
2019 mateusz.vi 798
 * padding must not be NULL
799
 * Returns the pointer to padding.
800
 */
801
char * removePadding(char *padding)
802
{
2027 mateusz.vi 803
  size_t len = strlen(padding);
2019 mateusz.vi 804
 
2025 mateusz.vi 805
  if (len < 4) return padding;
806
  *(padding + len - 4) = '\0';
2019 mateusz.vi 807
 
808
  return padding;
809
}
810
 
811
/**
812
 * Takes a given path, strips any \ or / that may appear on the end.
813
 * Returns a pointer to its static buffer containing path
814
 * without trailing slash and any necessary display conversions.
815
 */
816
char *fixPathForDisplay(char *path)
817
{
818
  static char buffer[MAXBUF];
2027 mateusz.vi 819
  int pathlen;
2019 mateusz.vi 820
 
821
  strcpy(buffer, path);
822
  pathlen = strlen(buffer);
823
  if (pathlen > 1)
824
  {
825
    pathlen--;
826
    if ((buffer[pathlen] == '\\') || (buffer[pathlen] == '/'))
827
      buffer[pathlen] = '\0'; // strip off trailing slash on end
828
  }
829
 
830
  return buffer;
831
}
832
 
833
/**
834
 * Displays the current path, with necessary padding before it.
835
 * A \ or / on end of currentpath is not shown.
836
 * moreSubdirsFollow should be nonzero if this is not the last
837
 * subdirectory to be displayed in current directory, else 0.
838
 * Also displays additional information, such as attributes or
839
 * sum of size of included files.
840
 * currentpath is an ASCIIZ string of path to display
841
 *             assumed to be a displayable path (ie. OEM or UTF-8)
842
 * padding is an ASCIIZ string to display prior to entry.
843
 * moreSubdirsFollow is -1 for initial path else >= 0.
844
 */
845
void showCurrentPath(char *currentpath, char *padding, int moreSubdirsFollow, DIRDATA *ddata)
846
{
847
  if (padding != NULL)
848
    pprintf("%s", padding);
849
 
850
  /* print lead padding except for initial directory */
851
  if (moreSubdirsFollow >= 0)
852
  {
853
    if (charSet == EXTENDEDCHARS)
854
    {
855
      if (moreSubdirsFollow)
856
        pprintf("%s", TBAR_HORZBAR_STR);
857
      else
858
        pprintf("%s", CBAR_HORZBAR_STR);
859
    }
860
    else
861
    {
862
      if (moreSubdirsFollow)
863
        pprintf("+---");
864
      else
865
        pprintf("\\---");
866
    }
867
  }
868
 
869
  /* optional display data */
870
  if (dspAttr)  /* attributes */
2031 mateusz.vi 871
    pprintf("[%c%c%c%c%c] ",
2019 mateusz.vi 872
      (ddata->dwDirAttributes & FILE_ATTRIBUTE_DIRECTORY)?'D':' ',  /* keep this one? its always true */
873
      (ddata->dwDirAttributes & FILE_ATTRIBUTE_ARCHIVE)?'A':' ',
874
      (ddata->dwDirAttributes & FILE_ATTRIBUTE_SYSTEM)?'S':' ',
875
      (ddata->dwDirAttributes & FILE_ATTRIBUTE_HIDDEN)?'H':' ',
2031 mateusz.vi 876
      (ddata->dwDirAttributes & FILE_ATTRIBUTE_READONLY)?'R':' '
2019 mateusz.vi 877
    );
878
 
879
  /* display directory name */
880
  pprintf("%s\n", currentpath);
881
}
882
 
883
 
2022 mateusz.vi 884
/**
2019 mateusz.vi 885
 * Displays summary information about directory.
886
 * Expects to be called after displayFiles (optionally called)
887
 */
2024 mateusz.vi 888
static void displaySummary(char *padding, int hasMoreSubdirs, DIRDATA *ddata)
2019 mateusz.vi 889
{
890
  addPadding(padding, hasMoreSubdirs);
891
 
892
  if (dspSumDirs)
893
  {
894
    if (showFiles == SHOWFILESON)
895
    {
896
      /* print File summary with lead padding, add filesize to it */
897
      pprintf("%s%lu files\n", padding, ddata->fileCnt);
898
    }
899
 
900
    /* print Directory summary with lead padding */
901
    pprintf("%s%lu subdirectories\n", padding, ddata->subdirCnt);
902
 
903
    /* show [nearly] blank line after summary */
904
    pprintf("%s\n", padding);
905
  }
906
 
907
  removePadding(padding);
908
}
909
 
2022 mateusz.vi 910
/**
2019 mateusz.vi 911
 * Displays files in directory specified by path.
912
 * Path must end in slash \ or /
913
 * Returns -1 on error,
914
 *          0 if no files, but no errors either,
915
 *      or  1 if files displayed, no errors.
916
 */
917
int displayFiles(char *path, char *padding, int hasMoreSubdirs, DIRDATA *ddata)
918
{
919
  static char buffer[MAXBUF];
920
  WIN32_FIND_DATA entry; /* current directory entry info    */
921
  HANDLE dir;         /* Current directory entry working with      */
922
  unsigned long filesShown = 0;
923
 
924
  /* get handle for files in current directory (using wildcard spec) */
925
  strcpy(buffer, path);
926
  strcat(buffer, "*");
927
  dir = FindFirstFile(buffer, &entry);
928
  if (dir == INVALID_HANDLE_VALUE)
929
    return -1;
930
 
931
  addPadding(padding, hasMoreSubdirs);
932
 
933
  /* cycle through directory printing out files. */
2022 mateusz.vi 934
  do
2019 mateusz.vi 935
  {
936
    /* print padding followed by filename */
937
    if ( ((entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) &&
938
         ( ((entry.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN |
939
         FILE_ATTRIBUTE_SYSTEM)) == 0)  || dspAll) )
940
    {
941
      /* print lead padding */
942
      pprintf("%s", padding);
943
 
944
      /* optional display data */
945
      if (dspAttr)  /* file attributes */
2031 mateusz.vi 946
        pprintf("[%c%c%c%c] ",
2019 mateusz.vi 947
          (entry.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)?'A':' ',
948
          (entry.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)?'S':' ',
949
          (entry.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)?'H':' ',
2031 mateusz.vi 950
          (entry.dwFileAttributes & FILE_ATTRIBUTE_READONLY)?'R':' '
2019 mateusz.vi 951
        );
952
 
953
      if (dspSize)  /* file size */
954
      {
955
        if (entry.nFileSizeHigh)
956
        {
957
          pprintf("******** ");  /* error exceed max value we can display, > 4GB */
958
        }
959
        else
960
        {
2023 mateusz.vi 961
          if (entry.nFileSizeLow < 1048576l)  /* if less than a MB, display in bytes */
2019 mateusz.vi 962
            pprintf("%10lu ", entry.nFileSizeLow);
963
          else                               /* otherwise display in KB */
964
            pprintf("%8luKB ", entry.nFileSizeLow/1024UL);
965
        }
966
      }
967
 
968
      /* print filename */
969
      pprintf("%s\n", entry.cFileName);
970
 
971
      filesShown++;
972
    }
973
  } while(FindNextFile(dir, &entry) != 0);
974
 
975
  if (filesShown)
976
  {
977
    pprintf("%s\n", padding);
978
  }
979
 
980
  /* cleanup directory search */
981
  FindClose(dir);
982
  /* dir = NULL; */
983
 
984
  removePadding(padding);
985
 
986
  /* store for summary display */
987
  if (ddata != NULL) ddata->fileCnt = filesShown;
988
 
989
  return (filesShown)? 1 : 0;
990
}
991
 
992
 
993
/**
994
 * Common portion of findFirstSubdir and findNextSubdir
995
 * Checks current FindFile results to determine if a valid directory
996
 * was found, and if so copies appropriate data into subdir and dsubdir.
997
 * It will repeat until a valid subdirectory is found or no more
998
 * are found, at which point it closes the FindFile search handle and
999
 * return INVALID_HANDLE_VALUE.  If successful, returns FindFile handle.
1000
 */
2032 mateusz.vi 1001
HANDLE cycleFindResults(HANDLE findnexthnd, WIN32_FIND_DATA_BOTH *entry, char *subdir, char *dsubdir)
2019 mateusz.vi 1002
{
2025 mateusz.vi 1003
  /* cycle through directory until 1st non . or .. directory is found. */
1004
  do
2019 mateusz.vi 1005
  {
2025 mateusz.vi 1006
    /* skip files & hidden or system directories */
2032 mateusz.vi 1007
    if ((((entry->ad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) ||
1008
         ((entry->ad.dwFileAttributes &
2025 mateusz.vi 1009
          (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0  && !dspAll) ) ||
2032 mateusz.vi 1010
        ((strcmp(entry->ad.cFileName, ".") == 0) ||
1011
         (strcmp(entry->ad.cFileName, "..") == 0)) )
2019 mateusz.vi 1012
    {
2032 mateusz.vi 1013
      if (FindNextFile(findnexthnd, &(entry->ad)) == 0)
2019 mateusz.vi 1014
      {
2025 mateusz.vi 1015
        FindClose(findnexthnd);      // prevent resource leaks
1016
        return INVALID_HANDLE_VALUE; // no subdirs found
2019 mateusz.vi 1017
      }
2025 mateusz.vi 1018
    }
1019
    else
2019 mateusz.vi 1020
    {
2025 mateusz.vi 1021
      /* set display name */
2032 mateusz.vi 1022
      strcpy(dsubdir, entry->ad.cFileName);
2019 mateusz.vi 1023
 
2025 mateusz.vi 1024
      /* set canical name to use for further FindFile calls */
1025
      /* use short file name if exists as lfn may contain unicode values converted
1026
       * to default character (eg. ?) and so not a valid path.
1027
       */
2032 mateusz.vi 1028
      strcpy(subdir, entry->ad.cFileName);
2025 mateusz.vi 1029
      strcat(subdir, "\\");
1030
    }
1031
  } while (!*subdir); // while (subdir is still blank)
1032
 
2019 mateusz.vi 1033
  return findnexthnd;
1034
}
1035
 
1036
 
1037
/* FindFile buffer used by findFirstSubdir and findNextSubdir only */
1038
static WIN32_FIND_DATA_BOTH findSubdir_entry; /* current directory entry info    */
1039
 
1040
/**
1041
 * Given the current path, find the 1st subdirectory.
1042
 * The subdirectory found is stored in subdir.
1043
 * subdir is cleared on error or no subdirectories.
1044
 * Returns the findfirst search HANDLE, which should be passed to
1045
 * findclose when directory has finished processing, and can be
1046
 * passed to findnextsubdir to find subsequent subdirectories.
1047
 * Returns INVALID_HANDLE_VALUE on error.
1048
 * currentpath must end in \
1049
 */
1050
HANDLE findFirstSubdir(char *currentpath, char *subdir, char *dsubdir)
1051
{
1052
  static char buffer[MAXBUF];
1053
  HANDLE dir;         /* Current directory entry working with      */
1054
 
1055
  /* get handle for files in current directory (using wildcard spec) */
1056
  strcpy(buffer, currentpath);
1057
  strcat(buffer, "*");
1058
 
2030 mateusz.vi 1059
  dir = FindFirstFileA(buffer, &findSubdir_entry.ad);
2019 mateusz.vi 1060
  if (dir == INVALID_HANDLE_VALUE)
1061
  {
1062
    showInvalidPath(currentpath);
1063
    return INVALID_HANDLE_VALUE;
1064
  }
1065
 
1066
  /* clear result path */
1067
  strcpy(subdir, "");
1068
 
2032 mateusz.vi 1069
  return cycleFindResults(dir, &findSubdir_entry, subdir, dsubdir);
2019 mateusz.vi 1070
}
1071
 
1072
/**
2022 mateusz.vi 1073
 * Given a search HANDLE, will find the next subdirectory,
2019 mateusz.vi 1074
 * setting subdir to the found directory name.
1075
 * dsubdir is the name to display (lfn or sfn as appropriate)
1076
 * currentpath must end in \
1077
 * If a subdirectory is found, returns 0, otherwise returns 1
1078
 * (either error or no more files).
1079
 */
1080
int findNextSubdir(HANDLE findnexthnd, char *subdir, char *dsubdir)
1081
{
1082
  /* clear result path */
1083
  strcpy(subdir, "");
1084
 
2025 mateusz.vi 1085
  if (FindNextFile(findnexthnd, &findSubdir_entry.ad) == 0) return 1; // no subdirs found
2019 mateusz.vi 1086
 
2032 mateusz.vi 1087
  if (cycleFindResults(findnexthnd, &findSubdir_entry, subdir, dsubdir) == INVALID_HANDLE_VALUE)
2019 mateusz.vi 1088
    return 1;
1089
  else
1090
    return 0;
1091
}
1092
 
1093
/**
1094
 * Given an initial path, displays the directory tree with
1095
 * a non-recursive function using a Stack.
1096
 * initialpath must be large enough to hold an added slash \ or /
1097
 * if it does not already end in one.
1098
 * Returns the count of subdirs in initialpath.
1099
 */
1100
long traverseTree(char *initialpath)
1101
{
1102
  long subdirsInInitialpath;
1103
  char padding[MAXPADLEN] = "";
1104
  char subdir[MAXBUF];
1105
  char dsubdir[MAXBUF];
2027 mateusz.vi 1106
  SUBDIRINFO *sdi;
2019 mateusz.vi 1107
 
1108
  STACK s;
1109
  stackDefaults(&s);
1110
  stackInit(&s);
1111
 
1112
  if ( (sdi = newSubdirInfo(NULL, initialpath, initialpath)) == NULL)
1113
    return 0L;
1114
  stackPushItem(&s, sdi);
1115
 
1116
  /* Store count of subdirs in initial path so can display message if none. */
1117
  subdirsInInitialpath = sdi->subdircnt;
1118
 
1119
  do
1120
  {
1121
    sdi = (SUBDIRINFO *)stackPopItem(&s);
1122
 
1123
    if (sdi->findnexthnd == INVALID_HANDLE_VALUE)  // findfirst not called yet
1124
    {
1125
      // 1st time this subdirectory processed, so display its name & possibly files
1126
      if (sdi->parent == NULL) // if initial path
1127
      {
1128
        // display initial path
1129
        showCurrentPath(/*sdi->dsubdir*/initialpath, NULL, -1, &(sdi->ddata));
1130
      }
1131
      else // normal processing (display path, add necessary padding)
1132
      {
1133
        showCurrentPath(sdi->dsubdir, padding, (sdi->parent->subdircnt > 0L)?1 : 0, &(sdi->ddata));
1134
        addPadding(padding, (sdi->parent->subdircnt > 0L)?1 : 0);
1135
      }
1136
 
1137
      if (showFiles == SHOWFILESON)  displayFiles(sdi->currentpath, padding, (sdi->subdircnt > 0L)?1 : 0, &(sdi->ddata));
2024 mateusz.vi 1138
      displaySummary(padding, (sdi->subdircnt > 0L)?1 : 0, &(sdi->ddata));
2019 mateusz.vi 1139
    }
1140
 
1141
    if (sdi->subdircnt > 0) /* if (there are more subdirectories to process) */
1142
    {
1143
      int flgErr;
1144
      if (sdi->findnexthnd == INVALID_HANDLE_VALUE)
1145
      {
1146
        sdi->findnexthnd = findFirstSubdir(sdi->currentpath, subdir, dsubdir);
1147
        flgErr = (sdi->findnexthnd == INVALID_HANDLE_VALUE);
1148
      }
1149
      else
1150
      {
1151
        flgErr = findNextSubdir(sdi->findnexthnd, subdir, dsubdir);
1152
      }
1153
 
1154
      if (flgErr) // don't add invalid paths to stack
1155
      {
1156
        printf("INTERNAL ERROR: subdir count changed, expecting %li more!\n", sdi->subdircnt+1L);
1157
 
1158
        sdi->subdircnt = 0; /* force subdir counter to 0, none left */
1159
        stackPushItem(&s, sdi);
1160
      }
1161
      else
1162
      {
1163
        sdi->subdircnt = sdi->subdircnt - 1L; /* decrement subdirs left count */
1164
        stackPushItem(&s, sdi);
1165
 
1166
        /* store necessary information, validate subdir, and if no error store it. */
1167
        if ((sdi = newSubdirInfo(sdi, subdir, dsubdir)) != NULL)
1168
          stackPushItem(&s, sdi);
1169
      }
1170
    }
1171
    else /* this directory finished processing, so free resources */
1172
    {
1173
      /* Remove the padding for this directory, all but initial path. */
1174
      if (sdi->parent != NULL)
1175
        removePadding(padding);
1176
 
1177
      /* Prevent resource leaks, by ending findsearch and freeing memory. */
1178
      FindClose(sdi->findnexthnd);
1179
      if (sdi != NULL)
1180
      {
1181
        if (sdi->currentpath != NULL)
1182
          free(sdi->currentpath);
1183
        free(sdi);
1184
      }
1185
    }
1186
  } while (stackTotalItems(&s)); /* while (stack is not empty) */
1187
 
1188
  stackTerm(&s);
1189
 
1190
  return subdirsInInitialpath;
1191
}
1192
 
1193
 
1194
/**
1195
 * Process strings, converting \\, \n, \r, and \t to actual chars.
1196
 * This method is used to allow the message catalog to use \\, \n, \r, and \t
1197
 * returns a pointer to its internal buffer, so strcpy soon after use.
1198
 * Can only handle lines up to MAXLINE chars.
1199
 * This is required because most messages are passed as
1200
 * string arguments to printf, and not actually parsed by it.
1201
 */
1202
char *processLine(char *line)
1203
{
1204
  static char buffer[MAXLINE+MAXLINE];
2027 mateusz.vi 1205
  char *src = line, *dst = buffer;
2019 mateusz.vi 1206
 
1207
  if (line == NULL) return NULL;
1208
 
1209
  /* cycle through copying characters, except when a \ is encountered. */
1210
  for ( ; *src != '\0'; src++, dst++)
1211
  {
1212
    if (*src == '\\')
1213
    {
1214
      src++;
1215
      switch (*src)
1216
      {
1217
	  case '\0': /* a slash ends a line, ignore the slash. */
1218
		  src--; /* next time through will see the '\0'    */
1219
		  break;
1220
	  case '\\': /* a single slash */
1221
		  *dst = '\\';
1222
		  break;
1223
	  case 'n': /* a newline */
1224
		  *dst = '\n';
1225
		  break;
1226
	  case 'r': /* a carriage return */
1227
		  *dst = '\r';
1228
		  break;
1229
	  case 't': /* a horizontal tab */
1230
		  *dst = '\t';
1231
		  break;
1232
	  default: /* just copy over the letter */
1233
		  *dst = *src;
1234
		  break;
1235
      }
1236
    }
1237
    else
1238
      *dst = *src;
1239
  }
1240
 
1241
  /* ensure '\0' terminated */
1242
  *dst = '\0';
1243
 
1244
  return buffer;
1245
}
1246
 
1247
 
1248
void FixOptionText(void)
1249
{
1250
  char buffer[MAXLINE];  /* sprintf can have problems with src==dest */
1251
 
1252
  /* Handle %c for options within messages using Set 8 */
1253
  strcpy(buffer, treeUsage);
1254
  sprintf(treeUsage, buffer, optionchar1, OptShowFiles[0], optionchar1, OptUseASCII[0]);
1255
  strcpy(buffer, treeFOption);
1256
  sprintf(treeFOption, buffer, optionchar1, OptShowFiles[0]);
1257
  strcpy(buffer, treeAOption);
1258
  sprintf(treeAOption, buffer, optionchar1, OptUseASCII[0]);
1259
  strcpy(buffer, useTreeHelp);
1260
  sprintf(useTreeHelp, buffer, optionchar1);
1261
}
1262
 
1263
 
2022 mateusz.vi 1264
/* Loads all messages from the message catalog. */
2019 mateusz.vi 1265
void loadAllMessages(void)
1266
{
1267
  /* Changes %c in certain lines with proper option characters. */
1268
  FixOptionText();
1269
}
1270
 
1271
 
1272
int main(int argc, char *argv[])
1273
{
1274
  char serial[SERIALLEN]; /* volume serial #  0000:0000 */
1275
  char volume[VOLLEN];    /* volume name (label), possibly none */
1276
 
1277
  /* Load all text from message catalog (or uses hard coded text) */
1278
  loadAllMessages();
1279
 
1280
  /* Parse any command line arguments, obtain path */
1281
  parseArguments(argc, argv);
1282
 
1283
  /* Initialize screen size, may reset pause to NOPAUSE if redirected */
1284
  getConsoleSize();
1285
 
1286
  /* Get Volume & Serial Number */
1287
  GetVolumeAndSerial(volume, serial, path);
1288
  if (strlen(volume) == 0)
1289
    pprintf(pathListingNoLabel);
1290
  else
1291
    pprintf(pathListingWithLabel, volume);
1292
  if (serial[0] != '\0')  /* Don't print anything if no serial# found */
1293
    pprintf(serialNumber, serial);
1294
 
1295
  /* now traverse & print tree, returns nonzero if has subdirectories */
1296
  if (traverseTree(path) == 0)
1297
    pprintf(noSubDirs);
1298
  else if (dspSumDirs) /* show count of directories processed */
1299
    pprintf("\n    %lu total directories\n", totalSubDirCnt+1);
1300
 
1301
  return 0;
1302
}