Subversion Repositories SvarDOS

Rev

Rev 2068 | Rev 2074 | Go to most recent revision | Details | Compare with Previous | 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 */
2034 mateusz.vi 40
#include <dos.h>
2019 mateusz.vi 41
#include <stdlib.h>
42
#include <stdio.h>
43
#include <string.h>
44
 
45
#include "stack.h"
2066 mateusz.vi 46
#include "svarlang/svarlang.h"
2019 mateusz.vi 47
 
2066 mateusz.vi 48
 
2019 mateusz.vi 49
/* The default extended forms of the lines used. */
50
#define VERTBAR_STR  "\xB3   "                 /* |    */
51
#define TBAR_HORZBAR_STR "\xC3\xC4\xC4\xC4"    /* +--- */
52
#define CBAR_HORZBAR_STR "\xC0\xC4\xC4\xC4"    /* \--- */
53
 
54
/* Global flags */
55
#define SHOWFILESON    1  /* Display names of files in directories       */
56
#define SHOWFILESOFF   0  /* Don't display names of files in directories */
57
 
58
#define ASCIICHARS     1  /* Use ASCII [7bit] characters                 */
59
#define EXTENDEDCHARS  0  /* Use extended ASCII [8bit] characters        */
60
 
61
#define NOPAUSE        0  /* Default, don't pause after screenfull       */
62
#define PAUSE          1  /* Wait for keypress after each page           */
63
 
64
 
65
/* Global variables */
66
short showFiles = SHOWFILESOFF;
67
short charSet = EXTENDEDCHARS;
68
short pause = NOPAUSE;
69
 
70
short dspAll = 0;  /* if nonzero includes HIDDEN & SYSTEM files in output */
71
short dspSize = 0; /* if nonzero displays filesizes                       */
72
short dspAttr = 0; /* if nonzero displays file attributes [DACESHRBP]     */
73
short dspSumDirs = 0; /* show count of subdirectories  (per dir and total)*/
74
 
75
 
76
/* maintains total count, for > 4billion dirs, use a __int64 */
77
unsigned long totalSubDirCnt = 0;
78
 
79
 
80
/* text window size, used to determine when to pause,
81
   Note: rows is total rows available - 2
2022 mateusz.vi 82
   1 is for pause message and then one to prevent auto scroll up
2019 mateusz.vi 83
*/
84
short cols=80, rows=23;   /* determined these on startup (when possible)  */
85
 
86
 
87
 
88
/* Global constants */
89
#define SERIALLEN 16      /* Defines max size of volume & serial number   */
2055 mateusz.vi 90
#define VOLLEN 16
2019 mateusz.vi 91
 
2064 mateusz.vi 92
static char path[PATH_MAX];   /* Path to begin search from, default=current   */
2019 mateusz.vi 93
 
2055 mateusz.vi 94
#define MAXPADLEN (PATH_MAX*2) /* Must be large enough to hold the maximum padding */
95
/* (PATH_MAX/2)*4 == (max path len / min 2chars dirs "?\") * 4chars per padding    */
2019 mateusz.vi 96
 
97
/* The maximum size any line of text output can be, including room for '\0'*/
98
#define MAXLINE 160        /* Increased to fit two lines for translations  */
99
 
100
 
101
/* The hard coded strings used by the following show functions.            */
102
 
103
/* common to many functions [Set 1] */
104
char newLine[MAXLINE] = "\n";
105
 
106
 
107
/* Procedures */
108
 
109
 
2044 mateusz.vi 110
/* returns the current drive (A=0, B=1, etc) */
111
static unsigned char getdrive(void);
112
#pragma aux getdrive = \
113
"mov ah, 0x19" \
114
"int 0x21" \
115
modify [ah] \
116
value [al]
117
 
118
 
2049 mateusz.vi 119
/* waits for a keypress, flushes keyb buffer, returns nothing */
120
static void waitkey(void);
121
#pragma aux waitkey = \
122
"mov ah, 0x08" \
123
"int 0x21" \
124
/* flush keyb buffer in case it was an extended key */ \
125
"mov ax, 0x0C0B" \
126
"int 0x21" \
127
modify [ax]
128
 
129
 
2050 mateusz.vi 130
/* checks if stdout appears to be redirected. returns 0 if not, non-zero otherwise. */
2051 mateusz.vi 131
static unsigned char is_stdout_redirected(void);
132
#pragma aux is_stdout_redirected = \
133
"mov ax, 0x4400"    /* DOS 2+, IOCTL Get Device Info */            \
134
"mov bx, 0x0001"    /* file handle (stdout) */                     \
135
"int 0x21" \
136
"jc DONE"           /* on error AL contains a non-zero err code */ \
137
"and dl, 0x80"      /* bit 7 of DL is the "CHAR" flag */           \
138
"xor dl, 0x80"      /* return 0 if CHAR bit is set */              \
139
"mov al, dl" \
140
"DONE:" \
141
modify [ax bx dx] \
142
value [al]
2034 mateusz.vi 143
 
144
 
2061 mateusz.vi 145
static _Packed struct {
146
  unsigned short infolevel;
147
  unsigned short serial2;
148
  unsigned short serial1;
149
  char label[11];
150
  short fstype[8];
151
} glob_drv_info;
152
 
2068 mateusz.vi 153
 
2061 mateusz.vi 154
/* drv is 1-based (A=1, B=2, ...) */
2068 mateusz.vi 155
static void getdrvserial(unsigned char drv) {
156
  unsigned short drvinfo_seg = FP_SEG(&glob_drv_info);
2061 mateusz.vi 157
 
2068 mateusz.vi 158
  _asm {
159
    push ax
160
    push bx
161
    push dx
2061 mateusz.vi 162
 
2068 mateusz.vi 163
    mov ax, 0x6900
164
    xor bh, bh
165
    mov bl, drv
166
    mov dx, offset glob_drv_info
167
    push ds
168
    mov ds, drvinfo_seg
169
    int 0x21
170
    pop ds
171
 
172
    pop dx
173
    pop bx
174
    pop ax
175
  }
176
}
177
 
178
 
2052 mateusz.vi 179
static int truename(char *path, const char *origpath) {
180
  unsigned short origpath_seg = FP_SEG(origpath);
181
  unsigned short origpath_off = FP_OFF(origpath);
182
  unsigned short dstpath_seg = FP_SEG(path);
183
  unsigned short dstpath_off = FP_OFF(path);
184
  unsigned char cflag = 0;
185
 
186
  /* resolve path with truename */
187
  _asm {
188
    push ax
189
    push si
190
    push di
191
    push es
192
    push ds
193
 
194
    mov ah, 0x60          /* AH = 0x60 -> TRUENAME */
195
    mov di, dstpath_off   /* ES:DI -> dst buffer */
196
    mov es, dstpath_seg
197
    mov si, origpath_off  /* DS:SI -> src path */
198
    mov ds, origpath_seg
199
    int 0x21
200
    jnc DONE
201
    mov cflag, 1
202
    DONE:
203
 
204
    pop ds
205
    pop es
206
    pop di
207
    pop si
208
    pop ax
209
  }
210
 
211
  return(cflag);
212
}
213
 
214
 
2019 mateusz.vi 215
/* sets rows & cols to size of actual console window
216
 * force NOPAUSE if appears output redirected to a file or
217
 * piped to another program
218
 * Uses hard coded defaults and leaves pause flag unchanged
219
 * if unable to obtain information.
220
 */
2034 mateusz.vi 221
static void getConsoleSize(void) {
222
  unsigned short far *bios_cols = (unsigned short far *)MK_FP(0x40,0x4A);
223
  unsigned short far *bios_size = (unsigned short far *)MK_FP(0x40,0x4C);
2019 mateusz.vi 224
 
2050 mateusz.vi 225
  if (is_stdout_redirected() != 0) {
226
    /* e.g. redirected to a file, tree > filelist.txt */
227
    /* Output to a file or program, so no screen to fill (no max cols or rows) */
2034 mateusz.vi 228
      pause = NOPAUSE;   /* so ignore request to pause */
2050 mateusz.vi 229
  } else { /* e.g. the console */
230
    if ((*bios_cols == 0) || (*bios_size == 0)) { /* MDA does not report size */
231
      cols = 80;
232
      rows = 23;
233
    } else {
234
      cols = *bios_cols;
235
      rows = *bios_size / cols / 2;
236
      if (rows > 2) rows -= 2; /* necessary to keep screen from scrolling */
237
    }
2019 mateusz.vi 238
  }
239
}
240
 
2034 mateusz.vi 241
 
2019 mateusz.vi 242
/* when pause == NOPAUSE then identical to printf,
243
   otherwise counts lines printed and pauses as needed.
244
   Should be used for all messages printed that do not
245
   immediately exit afterwards (else printf may be used).
246
   May display N too many lines before pause if line is
247
   printed that exceeds cols [N=linelen%cols] and lacks
248
   any newlines (but this should not occur in tree).
249
*/
250
#include <stdarg.h>  /* va_list, va_start, va_end */
2037 mateusz.vi 251
static int pprintf(const char *msg, ...) {
2030 mateusz.vi 252
  static int lineCnt = -1;
2019 mateusz.vi 253
  static int lineCol = 0;
254
  va_list argptr;
255
  int cnt;
2055 mateusz.vi 256
  char buffer[MAXLINE];
2019 mateusz.vi 257
 
2030 mateusz.vi 258
  if (lineCnt == -1) lineCnt = rows;
259
 
2019 mateusz.vi 260
  va_start(argptr, msg);
261
  cnt = vsprintf(buffer, msg, argptr);
262
  va_end(argptr);
263
 
264
  if (pause == PAUSE)
265
  {
2030 mateusz.vi 266
    char *l = buffer;
267
    char *t;
2019 mateusz.vi 268
    /* cycle through counting newlines and lines > cols */
2030 mateusz.vi 269
    for (t = strchr(l, '\n'); t != NULL; t = strchr(l, '\n'))
2019 mateusz.vi 270
    {
2030 mateusz.vi 271
      char c;
2019 mateusz.vi 272
      t++;             /* point to character after newline */
2030 mateusz.vi 273
      c = *t;          /* store current value */
2019 mateusz.vi 274
      *t = '\0';       /* mark as end of string */
275
 
276
      /* print all but last line of a string that wraps across rows */
277
      /* adjusting for partial lines printed without the newlines   */
278
      while (strlen(l)+lineCol > cols)
279
      {
280
        char c = l[cols-lineCol];
281
        l[cols-lineCol] = '\0';
282
        printf("%s", l);
283
        l[cols-lineCol] = c;
284
        l += cols-lineCol;
285
 
286
        lineCnt--;  lineCol = 0;
2066 mateusz.vi 287
        if (!lineCnt) { lineCnt= rows;  fflush(NULL);  fprintf(stderr, "%s", svarlang_strid(0x0106));  waitkey(); }
2019 mateusz.vi 288
      }
289
 
290
      printf("%s", l); /* print out this line */
291
      *t = c;          /* restore value */
292
      l = t;           /* mark beginning of next line */
293
 
294
      lineCnt--;  lineCol = 0;
2066 mateusz.vi 295
      if (!lineCnt) { lineCnt= rows;  fflush(NULL);  fprintf(stderr, "%s", svarlang_strid(0x0106));  waitkey(); }
2019 mateusz.vi 296
    }
297
    printf("%s", l);   /* print rest of string that lacks newline */
298
    lineCol = strlen(l);
299
  }
300
  else  /* NOPAUSE */
301
    printf("%s", buffer);
302
 
303
  return cnt;
304
}
305
 
306
 
307
/* Displays to user valid options then exits program indicating no error */
2037 mateusz.vi 308
static void showUsage(void) {
2073 mateusz.vi 309
  puts(svarlang_strid(0x0201));
2066 mateusz.vi 310
  puts("");
2073 mateusz.vi 311
  puts(svarlang_strid(0x0202));
312
  puts("");
313
  puts(svarlang_strid(0x0203));
314
  puts(svarlang_strid(0x0204));
2019 mateusz.vi 315
  exit(1);
316
}
317
 
318
 
319
/* Displays error message then exits indicating error */
2037 mateusz.vi 320
static void showInvalidUsage(char * badOption) {
2066 mateusz.vi 321
  printf(svarlang_strid(0x0301), badOption); /* invalid switch - ... */
322
  printf("%s%s", svarlang_strid(0x0302), newLine); /* use TREE /? for usage info */
2019 mateusz.vi 323
  exit(1);
324
}
325
 
326
 
327
/* Displays author, copyright, etc info, then exits indicating no error. */
2037 mateusz.vi 328
static void showVersionInfo(void) {
2066 mateusz.vi 329
  printf(svarlang_strid(0x0201));
330
  printf(svarlang_strid(0x0202));
331
  printf(svarlang_strid(0x0403), VERSION);
332
  printf(svarlang_strid(0x0404));
333
  printf(svarlang_strid(0x0407));
2019 mateusz.vi 334
  exit(1);
335
}
336
 
337
 
338
/* Displays error messge for invalid drives and exits */
2037 mateusz.vi 339
static void showInvalidDrive(void) {
2066 mateusz.vi 340
  printf(svarlang_strid(0x0501)); /* invalid drive spec */
2019 mateusz.vi 341
  exit(1);
342
}
343
 
344
 
345
/**
346
 * Takes a given path, strips any \ or / that may appear on the end.
347
 * Returns a pointer to its static buffer containing path
348
 * without trailing slash and any necessary display conversions.
349
 */
2037 mateusz.vi 350
static char *fixPathForDisplay(char *path);
2019 mateusz.vi 351
 
352
/* Displays error message for invalid path; Does NOT exit */
2065 mateusz.vi 353
static void showInvalidPath(const char *badpath) {
354
  pprintf("%s\n", badpath);
2066 mateusz.vi 355
  pprintf(svarlang_strid(0x0601), badpath); /* invalid path - ... */
2019 mateusz.vi 356
}
357
 
358
/* Displays error message for out of memory; Does NOT exit */
2064 mateusz.vi 359
static void showOutOfMemory(const char *path) {
2066 mateusz.vi 360
  pprintf(svarlang_strid(0x0702), path); /* out of memory on subdir ... */
2019 mateusz.vi 361
}
362
 
363
 
364
/* Parses the command line and sets global variables. */
2064 mateusz.vi 365
static void parseArguments(int argc, char **argv) {
366
  int i;
2019 mateusz.vi 367
 
368
  /* if no drive specified on command line, use current */
2052 mateusz.vi 369
  if (truename(path, ".") != 0) showInvalidDrive();
2019 mateusz.vi 370
 
2064 mateusz.vi 371
  for (i = 1; i < argc; i++) {
372
 
2019 mateusz.vi 373
    /* Check if user is giving an option or drive/path */
2064 mateusz.vi 374
    if ((argv[i][0] != '/') && (argv[i][0] != '-') ) {
375
      if (truename(path, argv[i]) != 0) showInvalidPath(argv[i]);
376
      continue;
377
    }
2019 mateusz.vi 378
 
2064 mateusz.vi 379
    /* must be an option then */
380
    /* check multi character options 1st */
381
    if (argv[i][1] & 0xDF == 'D') {
382
      switch(argv[i][2] & 0xDF) {
383
        case 'A' :       /*  /DA  display attributes */
384
          dspAttr = 1;
385
          break;
386
        case 'F' :       /*  /DF  display filesizes  */
387
          dspSize = 1;
388
          break;
389
        case 'H' :       /*  /DH  display hidden & system files (normally not shown) */
390
          dspAll = 1;
391
          break;
392
        case 'R' :       /*  /DR  display results at end */
393
          dspSumDirs = 1;
394
          break;
395
        default:
2019 mateusz.vi 396
          showInvalidUsage(argv[i]);
397
      }
2064 mateusz.vi 398
      continue;
2019 mateusz.vi 399
    }
2064 mateusz.vi 400
 
401
    /* a 1 character option (or invalid) */
402
    if (argv[i][2] != 0) showInvalidUsage(argv[i]);
403
 
404
    switch(argv[i][1] & 0xDF) { /* upcase */
405
      case 'F': /* show files */
406
        showFiles = SHOWFILESON; /* set file display flag appropriately */
407
        break;
408
      case 'A': /* use ASCII only (7-bit) */
409
        charSet = ASCIICHARS;    /* set charset flag appropriately      */
410
        break;
411
      case 'V': /* Version information */
412
        showVersionInfo();       /* show version info and exit          */
413
        break;
414
      case 'P': /* wait for keypress after each page (pause) */
415
        pause = PAUSE;
416
        break;
2066 mateusz.vi 417
      case '?' & 0xDF:
2064 mateusz.vi 418
        showUsage();             /* show usage info and exit            */
419
        break;
420
      default: /* Invalid or unknown option */
421
        showInvalidUsage(argv[i]);
422
    }
2019 mateusz.vi 423
  }
424
}
425
 
426
 
427
/**
428
 * Fills in the serial and volume variables with the serial #
429
 * and volume found using path.
430
 */
2037 mateusz.vi 431
static void GetVolumeAndSerial(char *volume, char *serial, char *path) {
2061 mateusz.vi 432
  getdrvserial((path[0] & 0xDF) - '@');
433
  memcpy(volume, glob_drv_info.label, 12);
434
  volume[11] = 0;
2019 mateusz.vi 435
 
2061 mateusz.vi 436
  sprintf(serial, "%04X:%04X", glob_drv_info.serial1, glob_drv_info.serial2);
2019 mateusz.vi 437
}
438
 
439
 
440
/**
441
 * Stores directory information obtained from FindFirst/Next that
442
 * we may wish to make use of when displaying directory entry.
443
 * e.g. attribute, dates, etc.
444
 */
2054 mateusz.vi 445
typedef struct DIRDATA {
446
  unsigned long subdirCnt;  /* how many subdirectories we have */
447
  unsigned long fileCnt;    /* how many [normal] files we have */
2056 mateusz.vi 448
  unsigned int attrib;      /* Directory attributes            */
2019 mateusz.vi 449
} DIRDATA;
450
 
451
/**
452
 * Contains the information stored in a Stack necessary to allow
453
 * non-recursive function to display directory tree.
454
 */
2062 mateusz.vi 455
struct SUBDIRINFO {
456
  struct SUBDIRINFO *parent; /* points to parent subdirectory                */
2019 mateusz.vi 457
  char *currentpath;    /* Stores the full path this structure represents     */
458
  char *subdir;         /* points to last subdir within currentpath           */
459
  char *dsubdir;        /* Stores a display ready directory name              */
460
  long subdircnt;       /* Initially a count of how many subdirs in this dir  */
2060 mateusz.vi 461
  struct find_t *findnexthnd; /* The handle returned by findfirst, used in findnext */
2019 mateusz.vi 462
  struct DIRDATA ddata; /* Maintain directory information, eg attributes      */
2062 mateusz.vi 463
};
2019 mateusz.vi 464
 
465
 
466
/**
467
 * Returns 0 if no subdirectories, count if has subdirs.
468
 * Path must end in slash \ or /
469
 * On error (invalid path) displays message and returns -1L.
470
 * Stores additional directory data in ddata if non-NULL
471
 * and path is valid.
472
 */
2037 mateusz.vi 473
static long hasSubdirectories(char *path, DIRDATA *ddata) {
2060 mateusz.vi 474
  struct find_t findData;
475
  char buffer[PATH_MAX + 4];
2063 mateusz.vi 476
  unsigned short hasSubdirs = 0;
2019 mateusz.vi 477
 
478
  /* get the handle to start with (using wildcard spec) */
479
  strcpy(buffer, path);
2060 mateusz.vi 480
  strcat(buffer, "*.*");
2019 mateusz.vi 481
 
2060 mateusz.vi 482
  if (_dos_findfirst(buffer, 0x37, &findData) != 0) {
2019 mateusz.vi 483
    showInvalidPath(path); /* Display error message */
2048 mateusz.vi 484
    return(-1);
2019 mateusz.vi 485
  }
486
 
487
  /*  cycle through entries counting directories found until no more entries */
488
  do {
2063 mateusz.vi 489
    if ((findData.attrib & _A_SUBDIR) == 0) continue; /* not a DIR */
490
      /* filter out system and hidden files, unless dspAll is on */
491
    if (dspAll == 0) {
492
      if (findData.attrib & _A_HIDDEN) continue;
493
      if (findData.attrib & _A_SYSTEM) continue;
2019 mateusz.vi 494
    }
2063 mateusz.vi 495
    if (findData.name[0] != '.') { /* ignore '.' and '..' */
496
      hasSubdirs++;      /* subdir of initial path found, so increment counter */
497
    }
2060 mateusz.vi 498
  } while(_dos_findnext(&findData) == 0);
2019 mateusz.vi 499
 
500
  /* prevent resource leaks, close the handle. */
2060 mateusz.vi 501
  _dos_findclose(&findData);
2019 mateusz.vi 502
 
503
  if (ddata != NULL)  // don't bother if user doesn't want them
504
  {
505
    /* The root directory of a volume (including non root paths
506
       corresponding to mount points) may not have a current (.) and
507
       parent (..) entry.  So we can't get attributes for initial
2022 mateusz.vi 508
       path in above loop from the FindFile call as it may not show up
2019 mateusz.vi 509
       (no . entry).  So instead we explicitly get them here.
510
    */
2056 mateusz.vi 511
    if (_dos_getfileattr(path, &(ddata->attrib)) != 0) {
2019 mateusz.vi 512
      //printf("ERROR: unable to get file attr, %i\n", GetLastError());
2047 mateusz.vi 513
      ddata->attrib = 0;
2019 mateusz.vi 514
    }
515
 
516
    /* a curiosity, for showing sum of directories process */
517
    ddata->subdirCnt = hasSubdirs;
518
  }
519
  totalSubDirCnt += hasSubdirs;
520
 
521
  return hasSubdirs;
522
}
523
 
524
 
525
/**
526
 * Allocates memory and stores the necessary stuff to allow us to
527
 * come back to this subdirectory after handling its subdirectories.
528
 * parentpath must end in \ or / or be NULL, however
529
 * parent should only be NULL for initialpath
530
 * if subdir does not end in slash, one is added to stored subdir
531
 * dsubdir is subdir already modified so ready to display to user
532
 */
2062 mateusz.vi 533
static struct SUBDIRINFO *newSubdirInfo(struct SUBDIRINFO *parent, char *subdir, char *dsubdir) {
2027 mateusz.vi 534
  int parentLen, subdirLen;
2062 mateusz.vi 535
  struct SUBDIRINFO *temp;
2019 mateusz.vi 536
 
537
  /* Get length of parent directory */
538
  if (parent == NULL)
539
    parentLen = 0;
540
  else
541
    parentLen = strlen(parent->currentpath);
542
 
543
  /* Get length of subdir, add 1 if does not end in slash */
544
  subdirLen = strlen(subdir);
545
  if ((subdirLen < 1) || ( (*(subdir+subdirLen-1) != '\\') && (*(subdir+subdirLen-1) != '/') ) )
546
    subdirLen++;
547
 
2062 mateusz.vi 548
  temp = malloc(sizeof(struct SUBDIRINFO));
549
  if (temp == NULL) {
2019 mateusz.vi 550
    showOutOfMemory(subdir);
551
    return NULL;
552
  }
553
  if ( ((temp->currentpath = (char *)malloc(parentLen+subdirLen+1)) == NULL) ||
554
       ((temp->dsubdir = (char *)malloc(strlen(dsubdir)+1)) == NULL) )
555
  {
556
    showOutOfMemory(subdir);
557
    if (temp->currentpath != NULL) free(temp->currentpath);
558
    free(temp);
559
    return NULL;
560
  }
561
  temp->parent = parent;
562
  if (parent == NULL)
563
    strcpy(temp->currentpath, "");
564
  else
565
    strcpy(temp->currentpath, parent->currentpath);
566
  strcat(temp->currentpath, subdir);
567
  /* if subdir[subdirLen-1] == '\0' then we must append a slash */
568
  if (*(subdir+subdirLen-1) == '\0')
569
    strcat(temp->currentpath, "\\");
570
  temp->subdir = temp->currentpath+parentLen;
571
  strcpy(temp->dsubdir, dsubdir);
572
  if ((temp->subdircnt = hasSubdirectories(temp->currentpath, &(temp->ddata))) == -1L)
573
  {
574
    free (temp->currentpath);
575
    free (temp->dsubdir);
576
    free(temp);
577
    return NULL;
578
  }
2048 mateusz.vi 579
  temp->findnexthnd = NULL;
2019 mateusz.vi 580
 
581
  return temp;
582
}
583
 
2048 mateusz.vi 584
 
2019 mateusz.vi 585
/**
586
 * Extends the padding with the necessary 4 characters.
587
 * Returns the pointer to the padding.
2022 mateusz.vi 588
 * padding should be large enough to hold the additional
2019 mateusz.vi 589
 * characters and '\0', moreSubdirsFollow specifies if
590
 * this is the last subdirectory in a given directory
591
 * or if more follow (hence if a | is needed).
592
 * padding must not be NULL
593
 */
2037 mateusz.vi 594
static char * addPadding(char *padding, int moreSubdirsFollow) {
595
  if (moreSubdirsFollow) {
596
    /* 1st char is | or a vertical bar */
597
    if (charSet == EXTENDEDCHARS) {
598
      strcat(padding, VERTBAR_STR);
599
    } else {
600
      strcat(padding, "|   ");
2019 mateusz.vi 601
    }
2037 mateusz.vi 602
  } else {
603
    strcat(padding, "    ");
604
  }
2019 mateusz.vi 605
 
2037 mateusz.vi 606
  return(padding);
2019 mateusz.vi 607
}
608
 
609
/**
2025 mateusz.vi 610
 * Removes the last padding added (last 4 characters added).
611
 * Does nothing if less than 4 characters in string.
2019 mateusz.vi 612
 * padding must not be NULL
613
 * Returns the pointer to padding.
614
 */
2037 mateusz.vi 615
static char *removePadding(char *padding) {
2027 mateusz.vi 616
  size_t len = strlen(padding);
2019 mateusz.vi 617
 
2025 mateusz.vi 618
  if (len < 4) return padding;
619
  *(padding + len - 4) = '\0';
2019 mateusz.vi 620
 
621
  return padding;
622
}
623
 
624
 
625
/**
626
 * Displays the current path, with necessary padding before it.
627
 * A \ or / on end of currentpath is not shown.
628
 * moreSubdirsFollow should be nonzero if this is not the last
629
 * subdirectory to be displayed in current directory, else 0.
630
 * Also displays additional information, such as attributes or
631
 * sum of size of included files.
632
 * currentpath is an ASCIIZ string of path to display
633
 *             assumed to be a displayable path (ie. OEM or UTF-8)
634
 * padding is an ASCIIZ string to display prior to entry.
635
 * moreSubdirsFollow is -1 for initial path else >= 0.
636
 */
2037 mateusz.vi 637
static void showCurrentPath(char *currentpath, char *padding, int moreSubdirsFollow, DIRDATA *ddata) {
2019 mateusz.vi 638
  if (padding != NULL)
639
    pprintf("%s", padding);
640
 
641
  /* print lead padding except for initial directory */
642
  if (moreSubdirsFollow >= 0)
643
  {
644
    if (charSet == EXTENDEDCHARS)
645
    {
646
      if (moreSubdirsFollow)
647
        pprintf("%s", TBAR_HORZBAR_STR);
648
      else
649
        pprintf("%s", CBAR_HORZBAR_STR);
2037 mateusz.vi 650
    } else {
2019 mateusz.vi 651
      if (moreSubdirsFollow)
652
        pprintf("+---");
653
      else
654
        pprintf("\\---");
655
    }
656
  }
657
 
658
  /* optional display data */
659
  if (dspAttr)  /* attributes */
2031 mateusz.vi 660
    pprintf("[%c%c%c%c%c] ",
2061 mateusz.vi 661
      (ddata->attrib & _A_SUBDIR)?'D':' ',  /* keep this one? its always true */
662
      (ddata->attrib & _A_ARCH)?'A':' ',
663
      (ddata->attrib & _A_SYSTEM)?'S':' ',
664
      (ddata->attrib & _A_HIDDEN)?'H':' ',
665
      (ddata->attrib & _A_RDONLY)?'R':' '
2019 mateusz.vi 666
    );
667
 
668
  /* display directory name */
669
  pprintf("%s\n", currentpath);
670
}
671
 
672
 
2022 mateusz.vi 673
/**
2019 mateusz.vi 674
 * Displays summary information about directory.
675
 * Expects to be called after displayFiles (optionally called)
676
 */
2037 mateusz.vi 677
static void displaySummary(char *padding, int hasMoreSubdirs, DIRDATA *ddata) {
2019 mateusz.vi 678
  addPadding(padding, hasMoreSubdirs);
679
 
2037 mateusz.vi 680
  if (dspSumDirs) {
681
    if (showFiles == SHOWFILESON) {
2019 mateusz.vi 682
      /* print File summary with lead padding, add filesize to it */
683
      pprintf("%s%lu files\n", padding, ddata->fileCnt);
684
    }
685
 
686
    /* print Directory summary with lead padding */
687
    pprintf("%s%lu subdirectories\n", padding, ddata->subdirCnt);
688
 
689
    /* show [nearly] blank line after summary */
690
    pprintf("%s\n", padding);
691
  }
692
 
693
  removePadding(padding);
694
}
695
 
2022 mateusz.vi 696
/**
2019 mateusz.vi 697
 * Displays files in directory specified by path.
698
 * Path must end in slash \ or /
699
 * Returns -1 on error,
700
 *          0 if no files, but no errors either,
701
 *      or  1 if files displayed, no errors.
702
 */
2047 mateusz.vi 703
static int displayFiles(const char *path, char *padding, int hasMoreSubdirs, DIRDATA *ddata) {
2060 mateusz.vi 704
  char buffer[PATH_MAX + 4];
705
  struct find_t entry;   /* current directory entry info    */
2019 mateusz.vi 706
  unsigned long filesShown = 0;
707
 
708
  /* get handle for files in current directory (using wildcard spec) */
709
  strcpy(buffer, path);
2060 mateusz.vi 710
  strcat(buffer, "*.*");
711
  if (_dos_findfirst(buffer, 0x37, &entry) != 0) return(-1);
2019 mateusz.vi 712
 
713
  addPadding(padding, hasMoreSubdirs);
714
 
715
  /* cycle through directory printing out files. */
2022 mateusz.vi 716
  do
2019 mateusz.vi 717
  {
718
    /* print padding followed by filename */
2061 mateusz.vi 719
    if ( ((entry.attrib & _A_SUBDIR) == 0) &&
720
         ( ((entry.attrib & (_A_HIDDEN | _A_SYSTEM)) == 0)  || dspAll) )
2019 mateusz.vi 721
    {
722
      /* print lead padding */
723
      pprintf("%s", padding);
724
 
725
      /* optional display data */
726
      if (dspAttr)  /* file attributes */
2031 mateusz.vi 727
        pprintf("[%c%c%c%c] ",
2061 mateusz.vi 728
          (entry.attrib & _A_ARCH)?'A':' ',
729
          (entry.attrib & _A_SYSTEM)?'S':' ',
730
          (entry.attrib & _A_HIDDEN)?'H':' ',
731
          (entry.attrib & _A_RDONLY)?'R':' '
2019 mateusz.vi 732
        );
733
 
2053 mateusz.vi 734
      if (dspSize) { /* file size */
2058 mateusz.vi 735
        if (entry.size < 1048576ul)  /* if less than a MB, display in bytes */
736
          pprintf("%10lu ", entry.size);
2053 mateusz.vi 737
        else                               /* otherwise display in KB */
2058 mateusz.vi 738
          pprintf("%8luKB ", entry.size / 1024ul);
2019 mateusz.vi 739
      }
740
 
741
      /* print filename */
2058 mateusz.vi 742
      pprintf("%s\n", entry.name);
2019 mateusz.vi 743
 
744
      filesShown++;
745
    }
2060 mateusz.vi 746
  } while(_dos_findnext(&entry) == 0);
2019 mateusz.vi 747
 
748
  if (filesShown)
749
  {
750
    pprintf("%s\n", padding);
751
  }
752
 
753
  removePadding(padding);
754
 
755
  /* store for summary display */
756
  if (ddata != NULL) ddata->fileCnt = filesShown;
757
 
758
  return (filesShown)? 1 : 0;
759
}
760
 
761
 
762
/**
763
 * Common portion of findFirstSubdir and findNextSubdir
764
 * Checks current FindFile results to determine if a valid directory
765
 * was found, and if so copies appropriate data into subdir and dsubdir.
766
 * It will repeat until a valid subdirectory is found or no more
767
 * are found, at which point it closes the FindFile search handle and
2048 mateusz.vi 768
 * return NULL.  If successful, returns FindFile handle.
2019 mateusz.vi 769
 */
2060 mateusz.vi 770
static struct find_t *cycleFindResults(struct find_t *entry, char *subdir, char *dsubdir) {
2025 mateusz.vi 771
  /* cycle through directory until 1st non . or .. directory is found. */
2057 mateusz.vi 772
  for (;;) {
2025 mateusz.vi 773
    /* skip files & hidden or system directories */
2061 mateusz.vi 774
    if ((((entry->attrib & _A_SUBDIR) == 0) ||
2058 mateusz.vi 775
         ((entry->attrib &
2061 mateusz.vi 776
          (_A_HIDDEN | _A_SYSTEM)) != 0  && !dspAll) ) ||
2058 mateusz.vi 777
        (entry->name[0] == '.')) {
2060 mateusz.vi 778
      if (_dos_findnext(entry) != 0) {
779
        _dos_findclose(entry);      // prevent resource leaks
2048 mateusz.vi 780
        return(NULL); // no subdirs found
2019 mateusz.vi 781
      }
2048 mateusz.vi 782
    } else {
2025 mateusz.vi 783
      /* set display name */
2058 mateusz.vi 784
      strcpy(dsubdir, entry->name);
2019 mateusz.vi 785
 
2058 mateusz.vi 786
      strcpy(subdir, entry->name);
2025 mateusz.vi 787
      strcat(subdir, "\\");
2057 mateusz.vi 788
      return(entry);
2025 mateusz.vi 789
    }
2057 mateusz.vi 790
  }
2025 mateusz.vi 791
 
2057 mateusz.vi 792
  return entry;
2019 mateusz.vi 793
}
794
 
795
 
796
/**
797
 * Given the current path, find the 1st subdirectory.
798
 * The subdirectory found is stored in subdir.
799
 * subdir is cleared on error or no subdirectories.
800
 * Returns the findfirst search HANDLE, which should be passed to
801
 * findclose when directory has finished processing, and can be
802
 * passed to findnextsubdir to find subsequent subdirectories.
2048 mateusz.vi 803
 * Returns NULL on error.
2019 mateusz.vi 804
 * currentpath must end in \
805
 */
2060 mateusz.vi 806
static struct find_t *findFirstSubdir(char *currentpath, char *subdir, char *dsubdir) {
807
  char buffer[PATH_MAX + 4];
808
  struct find_t *dir;         /* Current directory entry working with      */
2019 mateusz.vi 809
 
2060 mateusz.vi 810
  dir = malloc(sizeof(struct find_t));
2057 mateusz.vi 811
  if (dir == NULL) return(NULL);
812
 
2019 mateusz.vi 813
  /* get handle for files in current directory (using wildcard spec) */
814
  strcpy(buffer, currentpath);
2060 mateusz.vi 815
  strcat(buffer, "*.*");
2019 mateusz.vi 816
 
2060 mateusz.vi 817
  if (_dos_findfirst(buffer, 0x37, dir) != 0) {
2019 mateusz.vi 818
    showInvalidPath(currentpath);
2048 mateusz.vi 819
    return(NULL);
2019 mateusz.vi 820
  }
821
 
822
  /* clear result path */
823
  strcpy(subdir, "");
824
 
2057 mateusz.vi 825
  return cycleFindResults(dir, subdir, dsubdir);
2019 mateusz.vi 826
}
827
 
828
/**
2022 mateusz.vi 829
 * Given a search HANDLE, will find the next subdirectory,
2019 mateusz.vi 830
 * setting subdir to the found directory name.
2045 mateusz.vi 831
 * dsubdir is the name to display
2019 mateusz.vi 832
 * currentpath must end in \
833
 * If a subdirectory is found, returns 0, otherwise returns 1
834
 * (either error or no more files).
835
 */
2060 mateusz.vi 836
static int findNextSubdir(struct find_t *findnexthnd, char *subdir, char *dsubdir) {
2019 mateusz.vi 837
  /* clear result path */
2057 mateusz.vi 838
  subdir[0] = 0;
2019 mateusz.vi 839
 
2060 mateusz.vi 840
  if (_dos_findnext(findnexthnd) != 0) return(1); // no subdirs found
2019 mateusz.vi 841
 
2057 mateusz.vi 842
  if (cycleFindResults(findnexthnd, subdir, dsubdir) == NULL) {
2019 mateusz.vi 843
    return 1;
2048 mateusz.vi 844
  }
845
  return 0;
2019 mateusz.vi 846
}
847
 
848
/**
849
 * Given an initial path, displays the directory tree with
850
 * a non-recursive function using a Stack.
851
 * initialpath must be large enough to hold an added slash \ or /
852
 * if it does not already end in one.
853
 * Returns the count of subdirs in initialpath.
854
 */
2037 mateusz.vi 855
static long traverseTree(char *initialpath) {
2019 mateusz.vi 856
  long subdirsInInitialpath;
857
  char padding[MAXPADLEN] = "";
2055 mateusz.vi 858
  char subdir[PATH_MAX];
859
  char dsubdir[PATH_MAX];
2062 mateusz.vi 860
  struct SUBDIRINFO *sdi;
2019 mateusz.vi 861
 
862
  STACK s;
863
  stackDefaults(&s);
864
  stackInit(&s);
865
 
2048 mateusz.vi 866
  if ( (sdi = newSubdirInfo(NULL, initialpath, initialpath)) == NULL) {
867
    return(0);
868
  }
2019 mateusz.vi 869
  stackPushItem(&s, sdi);
870
 
871
  /* Store count of subdirs in initial path so can display message if none. */
872
  subdirsInInitialpath = sdi->subdircnt;
873
 
874
  do
875
  {
2062 mateusz.vi 876
    sdi = (struct SUBDIRINFO *)stackPopItem(&s);
2019 mateusz.vi 877
 
2048 mateusz.vi 878
    if (sdi->findnexthnd == NULL) { // findfirst not called yet
2019 mateusz.vi 879
      // 1st time this subdirectory processed, so display its name & possibly files
880
      if (sdi->parent == NULL) // if initial path
881
      {
882
        // display initial path
883
        showCurrentPath(/*sdi->dsubdir*/initialpath, NULL, -1, &(sdi->ddata));
884
      }
885
      else // normal processing (display path, add necessary padding)
886
      {
887
        showCurrentPath(sdi->dsubdir, padding, (sdi->parent->subdircnt > 0L)?1 : 0, &(sdi->ddata));
888
        addPadding(padding, (sdi->parent->subdircnt > 0L)?1 : 0);
889
      }
890
 
891
      if (showFiles == SHOWFILESON)  displayFiles(sdi->currentpath, padding, (sdi->subdircnt > 0L)?1 : 0, &(sdi->ddata));
2024 mateusz.vi 892
      displaySummary(padding, (sdi->subdircnt > 0L)?1 : 0, &(sdi->ddata));
2019 mateusz.vi 893
    }
894
 
895
    if (sdi->subdircnt > 0) /* if (there are more subdirectories to process) */
896
    {
897
      int flgErr;
2048 mateusz.vi 898
      if (sdi->findnexthnd == NULL) {
2019 mateusz.vi 899
        sdi->findnexthnd = findFirstSubdir(sdi->currentpath, subdir, dsubdir);
2048 mateusz.vi 900
        flgErr = (sdi->findnexthnd == NULL);
901
      } else {
2019 mateusz.vi 902
        flgErr = findNextSubdir(sdi->findnexthnd, subdir, dsubdir);
903
      }
904
 
905
      if (flgErr) // don't add invalid paths to stack
906
      {
907
        printf("INTERNAL ERROR: subdir count changed, expecting %li more!\n", sdi->subdircnt+1L);
908
 
909
        sdi->subdircnt = 0; /* force subdir counter to 0, none left */
910
        stackPushItem(&s, sdi);
911
      }
912
      else
913
      {
914
        sdi->subdircnt = sdi->subdircnt - 1L; /* decrement subdirs left count */
915
        stackPushItem(&s, sdi);
916
 
917
        /* store necessary information, validate subdir, and if no error store it. */
918
        if ((sdi = newSubdirInfo(sdi, subdir, dsubdir)) != NULL)
919
          stackPushItem(&s, sdi);
920
      }
921
    }
922
    else /* this directory finished processing, so free resources */
923
    {
924
      /* Remove the padding for this directory, all but initial path. */
925
      if (sdi->parent != NULL)
926
        removePadding(padding);
927
 
928
      /* Prevent resource leaks, by ending findsearch and freeing memory. */
2060 mateusz.vi 929
      _dos_findclose(sdi->findnexthnd);
2019 mateusz.vi 930
      if (sdi != NULL)
931
      {
932
        if (sdi->currentpath != NULL)
933
          free(sdi->currentpath);
934
        free(sdi);
935
      }
936
    }
937
  } while (stackTotalItems(&s)); /* while (stack is not empty) */
938
 
939
  stackTerm(&s);
940
 
941
  return subdirsInInitialpath;
942
}
943
 
944
 
2037 mateusz.vi 945
int main(int argc, char **argv) {
2019 mateusz.vi 946
  char serial[SERIALLEN]; /* volume serial #  0000:0000 */
947
  char volume[VOLLEN];    /* volume name (label), possibly none */
948
 
2066 mateusz.vi 949
  /* load translation strings */
950
  svarlang_autoload_exepath(argv[0], getenv("LANG"));
2019 mateusz.vi 951
 
952
  /* Parse any command line arguments, obtain path */
953
  parseArguments(argc, argv);
954
 
955
  /* Initialize screen size, may reset pause to NOPAUSE if redirected */
956
  getConsoleSize();
957
 
958
  /* Get Volume & Serial Number */
959
  GetVolumeAndSerial(volume, serial, path);
2066 mateusz.vi 960
  if (volume[0] == 0) {
961
    pprintf(svarlang_strid(0x0102)); /* Dir PATH listing */
962
  } else {
963
    pprintf(svarlang_strid(0x0103), volume); /* Dir PATH listing for volume ... */
964
  }
965
  if (serial[0] != '\0') {  /* Don't print anything if no serial# found */
966
    pprintf(svarlang_strid(0x0104), serial); /* vol serial num is ... */
967
  }
2019 mateusz.vi 968
 
969
  /* now traverse & print tree, returns nonzero if has subdirectories */
2066 mateusz.vi 970
  if (traverseTree(path) == 0) {
971
    pprintf(svarlang_strid(0x0105)); /* no subdirs exist */
972
  } else if (dspSumDirs) { /* show count of directories processed */
2019 mateusz.vi 973
    pprintf("\n    %lu total directories\n", totalSubDirCnt+1);
2066 mateusz.vi 974
  }
2019 mateusz.vi 975
 
2066 mateusz.vi 976
  return(0);
2019 mateusz.vi 977
}