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