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