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