Subversion Repositories SvarDOS

Rev

Rev 2066 | Rev 2073 | 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) {
2066 mateusz.vi 309
  printf(svarlang_strid(0x0201));
310
  printf(svarlang_strid(0x0202));
311
  puts("");
312
  printf(svarlang_strid(0x0203));
313
  printf(svarlang_strid(0x0204));
2019 mateusz.vi 314
  exit(1);
315
}
316
 
317
 
318
/* Displays error message then exits indicating error */
2037 mateusz.vi 319
static void showInvalidUsage(char * badOption) {
2066 mateusz.vi 320
  printf(svarlang_strid(0x0301), badOption); /* invalid switch - ... */
321
  printf("%s%s", svarlang_strid(0x0302), newLine); /* use TREE /? for usage info */
2019 mateusz.vi 322
  exit(1);
323
}
324
 
325
 
326
/* Displays author, copyright, etc info, then exits indicating no error. */
2037 mateusz.vi 327
static void showVersionInfo(void) {
2066 mateusz.vi 328
  printf(svarlang_strid(0x0201));
329
  printf(svarlang_strid(0x0202));
330
  printf(svarlang_strid(0x0403), VERSION);
331
  printf(svarlang_strid(0x0404));
332
  printf(svarlang_strid(0x0407));
2019 mateusz.vi 333
  exit(1);
334
}
335
 
336
 
337
/* Displays error messge for invalid drives and exits */
2037 mateusz.vi 338
static void showInvalidDrive(void) {
2066 mateusz.vi 339
  printf(svarlang_strid(0x0501)); /* invalid drive spec */
2019 mateusz.vi 340
  exit(1);
341
}
342
 
343
 
344
/**
345
 * Takes a given path, strips any \ or / that may appear on the end.
346
 * Returns a pointer to its static buffer containing path
347
 * without trailing slash and any necessary display conversions.
348
 */
2037 mateusz.vi 349
static char *fixPathForDisplay(char *path);
2019 mateusz.vi 350
 
351
/* Displays error message for invalid path; Does NOT exit */
2065 mateusz.vi 352
static void showInvalidPath(const char *badpath) {
353
  pprintf("%s\n", badpath);
2066 mateusz.vi 354
  pprintf(svarlang_strid(0x0601), badpath); /* invalid path - ... */
2019 mateusz.vi 355
}
356
 
357
/* Displays error message for out of memory; Does NOT exit */
2064 mateusz.vi 358
static void showOutOfMemory(const char *path) {
2066 mateusz.vi 359
  pprintf(svarlang_strid(0x0702), path); /* out of memory on subdir ... */
2019 mateusz.vi 360
}
361
 
362
 
363
/* Parses the command line and sets global variables. */
2064 mateusz.vi 364
static void parseArguments(int argc, char **argv) {
365
  int i;
2019 mateusz.vi 366
 
367
  /* if no drive specified on command line, use current */
2052 mateusz.vi 368
  if (truename(path, ".") != 0) showInvalidDrive();
2019 mateusz.vi 369
 
2064 mateusz.vi 370
  for (i = 1; i < argc; i++) {
371
 
2019 mateusz.vi 372
    /* Check if user is giving an option or drive/path */
2064 mateusz.vi 373
    if ((argv[i][0] != '/') && (argv[i][0] != '-') ) {
374
      if (truename(path, argv[i]) != 0) showInvalidPath(argv[i]);
375
      continue;
376
    }
2019 mateusz.vi 377
 
2064 mateusz.vi 378
    /* must be an option then */
379
    /* check multi character options 1st */
380
    if (argv[i][1] & 0xDF == 'D') {
381
      switch(argv[i][2] & 0xDF) {
382
        case 'A' :       /*  /DA  display attributes */
383
          dspAttr = 1;
384
          break;
385
        case 'F' :       /*  /DF  display filesizes  */
386
          dspSize = 1;
387
          break;
388
        case 'H' :       /*  /DH  display hidden & system files (normally not shown) */
389
          dspAll = 1;
390
          break;
391
        case 'R' :       /*  /DR  display results at end */
392
          dspSumDirs = 1;
393
          break;
394
        default:
2019 mateusz.vi 395
          showInvalidUsage(argv[i]);
396
      }
2064 mateusz.vi 397
      continue;
2019 mateusz.vi 398
    }
2064 mateusz.vi 399
 
400
    /* a 1 character option (or invalid) */
401
    if (argv[i][2] != 0) showInvalidUsage(argv[i]);
402
 
403
    switch(argv[i][1] & 0xDF) { /* upcase */
404
      case 'F': /* show files */
405
        showFiles = SHOWFILESON; /* set file display flag appropriately */
406
        break;
407
      case 'A': /* use ASCII only (7-bit) */
408
        charSet = ASCIICHARS;    /* set charset flag appropriately      */
409
        break;
410
      case 'V': /* Version information */
411
        showVersionInfo();       /* show version info and exit          */
412
        break;
413
      case 'P': /* wait for keypress after each page (pause) */
414
        pause = PAUSE;
415
        break;
2066 mateusz.vi 416
      case '?' & 0xDF:
2064 mateusz.vi 417
        showUsage();             /* show usage info and exit            */
418
        break;
419
      default: /* Invalid or unknown option */
420
        showInvalidUsage(argv[i]);
421
    }
2019 mateusz.vi 422
  }
423
}
424
 
425
 
426
/**
427
 * Fills in the serial and volume variables with the serial #
428
 * and volume found using path.
429
 */
2037 mateusz.vi 430
static void GetVolumeAndSerial(char *volume, char *serial, char *path) {
2061 mateusz.vi 431
  getdrvserial((path[0] & 0xDF) - '@');
432
  memcpy(volume, glob_drv_info.label, 12);
433
  volume[11] = 0;
2019 mateusz.vi 434
 
2061 mateusz.vi 435
  sprintf(serial, "%04X:%04X", glob_drv_info.serial1, glob_drv_info.serial2);
2019 mateusz.vi 436
}
437
 
438
 
439
/**
440
 * Stores directory information obtained from FindFirst/Next that
441
 * we may wish to make use of when displaying directory entry.
442
 * e.g. attribute, dates, etc.
443
 */
2054 mateusz.vi 444
typedef struct DIRDATA {
445
  unsigned long subdirCnt;  /* how many subdirectories we have */
446
  unsigned long fileCnt;    /* how many [normal] files we have */
2056 mateusz.vi 447
  unsigned int attrib;      /* Directory attributes            */
2019 mateusz.vi 448
} DIRDATA;
449
 
450
/**
451
 * Contains the information stored in a Stack necessary to allow
452
 * non-recursive function to display directory tree.
453
 */
2062 mateusz.vi 454
struct SUBDIRINFO {
455
  struct SUBDIRINFO *parent; /* points to parent subdirectory                */
2019 mateusz.vi 456
  char *currentpath;    /* Stores the full path this structure represents     */
457
  char *subdir;         /* points to last subdir within currentpath           */
458
  char *dsubdir;        /* Stores a display ready directory name              */
459
  long subdircnt;       /* Initially a count of how many subdirs in this dir  */
2060 mateusz.vi 460
  struct find_t *findnexthnd; /* The handle returned by findfirst, used in findnext */
2019 mateusz.vi 461
  struct DIRDATA ddata; /* Maintain directory information, eg attributes      */
2062 mateusz.vi 462
};
2019 mateusz.vi 463
 
464
 
465
/**
466
 * Returns 0 if no subdirectories, count if has subdirs.
467
 * Path must end in slash \ or /
468
 * On error (invalid path) displays message and returns -1L.
469
 * Stores additional directory data in ddata if non-NULL
470
 * and path is valid.
471
 */
2037 mateusz.vi 472
static long hasSubdirectories(char *path, DIRDATA *ddata) {
2060 mateusz.vi 473
  struct find_t findData;
474
  char buffer[PATH_MAX + 4];
2063 mateusz.vi 475
  unsigned short hasSubdirs = 0;
2019 mateusz.vi 476
 
477
  /* get the handle to start with (using wildcard spec) */
478
  strcpy(buffer, path);
2060 mateusz.vi 479
  strcat(buffer, "*.*");
2019 mateusz.vi 480
 
2060 mateusz.vi 481
  if (_dos_findfirst(buffer, 0x37, &findData) != 0) {
2019 mateusz.vi 482
    showInvalidPath(path); /* Display error message */
2048 mateusz.vi 483
    return(-1);
2019 mateusz.vi 484
  }
485
 
486
  /*  cycle through entries counting directories found until no more entries */
487
  do {
2063 mateusz.vi 488
    if ((findData.attrib & _A_SUBDIR) == 0) continue; /* not a DIR */
489
      /* filter out system and hidden files, unless dspAll is on */
490
    if (dspAll == 0) {
491
      if (findData.attrib & _A_HIDDEN) continue;
492
      if (findData.attrib & _A_SYSTEM) continue;
2019 mateusz.vi 493
    }
2063 mateusz.vi 494
    if (findData.name[0] != '.') { /* ignore '.' and '..' */
495
      hasSubdirs++;      /* subdir of initial path found, so increment counter */
496
    }
2060 mateusz.vi 497
  } while(_dos_findnext(&findData) == 0);
2019 mateusz.vi 498
 
499
  /* prevent resource leaks, close the handle. */
2060 mateusz.vi 500
  _dos_findclose(&findData);
2019 mateusz.vi 501
 
502
  if (ddata != NULL)  // don't bother if user doesn't want them
503
  {
504
    /* The root directory of a volume (including non root paths
505
       corresponding to mount points) may not have a current (.) and
506
       parent (..) entry.  So we can't get attributes for initial
2022 mateusz.vi 507
       path in above loop from the FindFile call as it may not show up
2019 mateusz.vi 508
       (no . entry).  So instead we explicitly get them here.
509
    */
2056 mateusz.vi 510
    if (_dos_getfileattr(path, &(ddata->attrib)) != 0) {
2019 mateusz.vi 511
      //printf("ERROR: unable to get file attr, %i\n", GetLastError());
2047 mateusz.vi 512
      ddata->attrib = 0;
2019 mateusz.vi 513
    }
514
 
515
    /* a curiosity, for showing sum of directories process */
516
    ddata->subdirCnt = hasSubdirs;
517
  }
518
  totalSubDirCnt += hasSubdirs;
519
 
520
  return hasSubdirs;
521
}
522
 
523
 
524
/**
525
 * Allocates memory and stores the necessary stuff to allow us to
526
 * come back to this subdirectory after handling its subdirectories.
527
 * parentpath must end in \ or / or be NULL, however
528
 * parent should only be NULL for initialpath
529
 * if subdir does not end in slash, one is added to stored subdir
530
 * dsubdir is subdir already modified so ready to display to user
531
 */
2062 mateusz.vi 532
static struct SUBDIRINFO *newSubdirInfo(struct SUBDIRINFO *parent, char *subdir, char *dsubdir) {
2027 mateusz.vi 533
  int parentLen, subdirLen;
2062 mateusz.vi 534
  struct SUBDIRINFO *temp;
2019 mateusz.vi 535
 
536
  /* Get length of parent directory */
537
  if (parent == NULL)
538
    parentLen = 0;
539
  else
540
    parentLen = strlen(parent->currentpath);
541
 
542
  /* Get length of subdir, add 1 if does not end in slash */
543
  subdirLen = strlen(subdir);
544
  if ((subdirLen < 1) || ( (*(subdir+subdirLen-1) != '\\') && (*(subdir+subdirLen-1) != '/') ) )
545
    subdirLen++;
546
 
2062 mateusz.vi 547
  temp = malloc(sizeof(struct SUBDIRINFO));
548
  if (temp == NULL) {
2019 mateusz.vi 549
    showOutOfMemory(subdir);
550
    return NULL;
551
  }
552
  if ( ((temp->currentpath = (char *)malloc(parentLen+subdirLen+1)) == NULL) ||
553
       ((temp->dsubdir = (char *)malloc(strlen(dsubdir)+1)) == NULL) )
554
  {
555
    showOutOfMemory(subdir);
556
    if (temp->currentpath != NULL) free(temp->currentpath);
557
    free(temp);
558
    return NULL;
559
  }
560
  temp->parent = parent;
561
  if (parent == NULL)
562
    strcpy(temp->currentpath, "");
563
  else
564
    strcpy(temp->currentpath, parent->currentpath);
565
  strcat(temp->currentpath, subdir);
566
  /* if subdir[subdirLen-1] == '\0' then we must append a slash */
567
  if (*(subdir+subdirLen-1) == '\0')
568
    strcat(temp->currentpath, "\\");
569
  temp->subdir = temp->currentpath+parentLen;
570
  strcpy(temp->dsubdir, dsubdir);
571
  if ((temp->subdircnt = hasSubdirectories(temp->currentpath, &(temp->ddata))) == -1L)
572
  {
573
    free (temp->currentpath);
574
    free (temp->dsubdir);
575
    free(temp);
576
    return NULL;
577
  }
2048 mateusz.vi 578
  temp->findnexthnd = NULL;
2019 mateusz.vi 579
 
580
  return temp;
581
}
582
 
2048 mateusz.vi 583
 
2019 mateusz.vi 584
/**
585
 * Extends the padding with the necessary 4 characters.
586
 * Returns the pointer to the padding.
2022 mateusz.vi 587
 * padding should be large enough to hold the additional
2019 mateusz.vi 588
 * characters and '\0', moreSubdirsFollow specifies if
589
 * this is the last subdirectory in a given directory
590
 * or if more follow (hence if a | is needed).
591
 * padding must not be NULL
592
 */
2037 mateusz.vi 593
static char * addPadding(char *padding, int moreSubdirsFollow) {
594
  if (moreSubdirsFollow) {
595
    /* 1st char is | or a vertical bar */
596
    if (charSet == EXTENDEDCHARS) {
597
      strcat(padding, VERTBAR_STR);
598
    } else {
599
      strcat(padding, "|   ");
2019 mateusz.vi 600
    }
2037 mateusz.vi 601
  } else {
602
    strcat(padding, "    ");
603
  }
2019 mateusz.vi 604
 
2037 mateusz.vi 605
  return(padding);
2019 mateusz.vi 606
}
607
 
608
/**
2025 mateusz.vi 609
 * Removes the last padding added (last 4 characters added).
610
 * Does nothing if less than 4 characters in string.
2019 mateusz.vi 611
 * padding must not be NULL
612
 * Returns the pointer to padding.
613
 */
2037 mateusz.vi 614
static char *removePadding(char *padding) {
2027 mateusz.vi 615
  size_t len = strlen(padding);
2019 mateusz.vi 616
 
2025 mateusz.vi 617
  if (len < 4) return padding;
618
  *(padding + len - 4) = '\0';
2019 mateusz.vi 619
 
620
  return padding;
621
}
622
 
623
 
624
/**
625
 * Displays the current path, with necessary padding before it.
626
 * A \ or / on end of currentpath is not shown.
627
 * moreSubdirsFollow should be nonzero if this is not the last
628
 * subdirectory to be displayed in current directory, else 0.
629
 * Also displays additional information, such as attributes or
630
 * sum of size of included files.
631
 * currentpath is an ASCIIZ string of path to display
632
 *             assumed to be a displayable path (ie. OEM or UTF-8)
633
 * padding is an ASCIIZ string to display prior to entry.
634
 * moreSubdirsFollow is -1 for initial path else >= 0.
635
 */
2037 mateusz.vi 636
static void showCurrentPath(char *currentpath, char *padding, int moreSubdirsFollow, DIRDATA *ddata) {
2019 mateusz.vi 637
  if (padding != NULL)
638
    pprintf("%s", padding);
639
 
640
  /* print lead padding except for initial directory */
641
  if (moreSubdirsFollow >= 0)
642
  {
643
    if (charSet == EXTENDEDCHARS)
644
    {
645
      if (moreSubdirsFollow)
646
        pprintf("%s", TBAR_HORZBAR_STR);
647
      else
648
        pprintf("%s", CBAR_HORZBAR_STR);
2037 mateusz.vi 649
    } else {
2019 mateusz.vi 650
      if (moreSubdirsFollow)
651
        pprintf("+---");
652
      else
653
        pprintf("\\---");
654
    }
655
  }
656
 
657
  /* optional display data */
658
  if (dspAttr)  /* attributes */
2031 mateusz.vi 659
    pprintf("[%c%c%c%c%c] ",
2061 mateusz.vi 660
      (ddata->attrib & _A_SUBDIR)?'D':' ',  /* keep this one? its always true */
661
      (ddata->attrib & _A_ARCH)?'A':' ',
662
      (ddata->attrib & _A_SYSTEM)?'S':' ',
663
      (ddata->attrib & _A_HIDDEN)?'H':' ',
664
      (ddata->attrib & _A_RDONLY)?'R':' '
2019 mateusz.vi 665
    );
666
 
667
  /* display directory name */
668
  pprintf("%s\n", currentpath);
669
}
670
 
671
 
2022 mateusz.vi 672
/**
2019 mateusz.vi 673
 * Displays summary information about directory.
674
 * Expects to be called after displayFiles (optionally called)
675
 */
2037 mateusz.vi 676
static void displaySummary(char *padding, int hasMoreSubdirs, DIRDATA *ddata) {
2019 mateusz.vi 677
  addPadding(padding, hasMoreSubdirs);
678
 
2037 mateusz.vi 679
  if (dspSumDirs) {
680
    if (showFiles == SHOWFILESON) {
2019 mateusz.vi 681
      /* print File summary with lead padding, add filesize to it */
682
      pprintf("%s%lu files\n", padding, ddata->fileCnt);
683
    }
684
 
685
    /* print Directory summary with lead padding */
686
    pprintf("%s%lu subdirectories\n", padding, ddata->subdirCnt);
687
 
688
    /* show [nearly] blank line after summary */
689
    pprintf("%s\n", padding);
690
  }
691
 
692
  removePadding(padding);
693
}
694
 
2022 mateusz.vi 695
/**
2019 mateusz.vi 696
 * Displays files in directory specified by path.
697
 * Path must end in slash \ or /
698
 * Returns -1 on error,
699
 *          0 if no files, but no errors either,
700
 *      or  1 if files displayed, no errors.
701
 */
2047 mateusz.vi 702
static int displayFiles(const char *path, char *padding, int hasMoreSubdirs, DIRDATA *ddata) {
2060 mateusz.vi 703
  char buffer[PATH_MAX + 4];
704
  struct find_t entry;   /* current directory entry info    */
2019 mateusz.vi 705
  unsigned long filesShown = 0;
706
 
707
  /* get handle for files in current directory (using wildcard spec) */
708
  strcpy(buffer, path);
2060 mateusz.vi 709
  strcat(buffer, "*.*");
710
  if (_dos_findfirst(buffer, 0x37, &entry) != 0) return(-1);
2019 mateusz.vi 711
 
712
  addPadding(padding, hasMoreSubdirs);
713
 
714
  /* cycle through directory printing out files. */
2022 mateusz.vi 715
  do
2019 mateusz.vi 716
  {
717
    /* print padding followed by filename */
2061 mateusz.vi 718
    if ( ((entry.attrib & _A_SUBDIR) == 0) &&
719
         ( ((entry.attrib & (_A_HIDDEN | _A_SYSTEM)) == 0)  || dspAll) )
2019 mateusz.vi 720
    {
721
      /* print lead padding */
722
      pprintf("%s", padding);
723
 
724
      /* optional display data */
725
      if (dspAttr)  /* file attributes */
2031 mateusz.vi 726
        pprintf("[%c%c%c%c] ",
2061 mateusz.vi 727
          (entry.attrib & _A_ARCH)?'A':' ',
728
          (entry.attrib & _A_SYSTEM)?'S':' ',
729
          (entry.attrib & _A_HIDDEN)?'H':' ',
730
          (entry.attrib & _A_RDONLY)?'R':' '
2019 mateusz.vi 731
        );
732
 
2053 mateusz.vi 733
      if (dspSize) { /* file size */
2058 mateusz.vi 734
        if (entry.size < 1048576ul)  /* if less than a MB, display in bytes */
735
          pprintf("%10lu ", entry.size);
2053 mateusz.vi 736
        else                               /* otherwise display in KB */
2058 mateusz.vi 737
          pprintf("%8luKB ", entry.size / 1024ul);
2019 mateusz.vi 738
      }
739
 
740
      /* print filename */
2058 mateusz.vi 741
      pprintf("%s\n", entry.name);
2019 mateusz.vi 742
 
743
      filesShown++;
744
    }
2060 mateusz.vi 745
  } while(_dos_findnext(&entry) == 0);
2019 mateusz.vi 746
 
747
  if (filesShown)
748
  {
749
    pprintf("%s\n", padding);
750
  }
751
 
752
  removePadding(padding);
753
 
754
  /* store for summary display */
755
  if (ddata != NULL) ddata->fileCnt = filesShown;
756
 
757
  return (filesShown)? 1 : 0;
758
}
759
 
760
 
761
/**
762
 * Common portion of findFirstSubdir and findNextSubdir
763
 * Checks current FindFile results to determine if a valid directory
764
 * was found, and if so copies appropriate data into subdir and dsubdir.
765
 * It will repeat until a valid subdirectory is found or no more
766
 * are found, at which point it closes the FindFile search handle and
2048 mateusz.vi 767
 * return NULL.  If successful, returns FindFile handle.
2019 mateusz.vi 768
 */
2060 mateusz.vi 769
static struct find_t *cycleFindResults(struct find_t *entry, char *subdir, char *dsubdir) {
2025 mateusz.vi 770
  /* cycle through directory until 1st non . or .. directory is found. */
2057 mateusz.vi 771
  for (;;) {
2025 mateusz.vi 772
    /* skip files & hidden or system directories */
2061 mateusz.vi 773
    if ((((entry->attrib & _A_SUBDIR) == 0) ||
2058 mateusz.vi 774
         ((entry->attrib &
2061 mateusz.vi 775
          (_A_HIDDEN | _A_SYSTEM)) != 0  && !dspAll) ) ||
2058 mateusz.vi 776
        (entry->name[0] == '.')) {
2060 mateusz.vi 777
      if (_dos_findnext(entry) != 0) {
778
        _dos_findclose(entry);      // prevent resource leaks
2048 mateusz.vi 779
        return(NULL); // no subdirs found
2019 mateusz.vi 780
      }
2048 mateusz.vi 781
    } else {
2025 mateusz.vi 782
      /* set display name */
2058 mateusz.vi 783
      strcpy(dsubdir, entry->name);
2019 mateusz.vi 784
 
2058 mateusz.vi 785
      strcpy(subdir, entry->name);
2025 mateusz.vi 786
      strcat(subdir, "\\");
2057 mateusz.vi 787
      return(entry);
2025 mateusz.vi 788
    }
2057 mateusz.vi 789
  }
2025 mateusz.vi 790
 
2057 mateusz.vi 791
  return entry;
2019 mateusz.vi 792
}
793
 
794
 
795
/**
796
 * Given the current path, find the 1st subdirectory.
797
 * The subdirectory found is stored in subdir.
798
 * subdir is cleared on error or no subdirectories.
799
 * Returns the findfirst search HANDLE, which should be passed to
800
 * findclose when directory has finished processing, and can be
801
 * passed to findnextsubdir to find subsequent subdirectories.
2048 mateusz.vi 802
 * Returns NULL on error.
2019 mateusz.vi 803
 * currentpath must end in \
804
 */
2060 mateusz.vi 805
static struct find_t *findFirstSubdir(char *currentpath, char *subdir, char *dsubdir) {
806
  char buffer[PATH_MAX + 4];
807
  struct find_t *dir;         /* Current directory entry working with      */
2019 mateusz.vi 808
 
2060 mateusz.vi 809
  dir = malloc(sizeof(struct find_t));
2057 mateusz.vi 810
  if (dir == NULL) return(NULL);
811
 
2019 mateusz.vi 812
  /* get handle for files in current directory (using wildcard spec) */
813
  strcpy(buffer, currentpath);
2060 mateusz.vi 814
  strcat(buffer, "*.*");
2019 mateusz.vi 815
 
2060 mateusz.vi 816
  if (_dos_findfirst(buffer, 0x37, dir) != 0) {
2019 mateusz.vi 817
    showInvalidPath(currentpath);
2048 mateusz.vi 818
    return(NULL);
2019 mateusz.vi 819
  }
820
 
821
  /* clear result path */
822
  strcpy(subdir, "");
823
 
2057 mateusz.vi 824
  return cycleFindResults(dir, subdir, dsubdir);
2019 mateusz.vi 825
}
826
 
827
/**
2022 mateusz.vi 828
 * Given a search HANDLE, will find the next subdirectory,
2019 mateusz.vi 829
 * setting subdir to the found directory name.
2045 mateusz.vi 830
 * dsubdir is the name to display
2019 mateusz.vi 831
 * currentpath must end in \
832
 * If a subdirectory is found, returns 0, otherwise returns 1
833
 * (either error or no more files).
834
 */
2060 mateusz.vi 835
static int findNextSubdir(struct find_t *findnexthnd, char *subdir, char *dsubdir) {
2019 mateusz.vi 836
  /* clear result path */
2057 mateusz.vi 837
  subdir[0] = 0;
2019 mateusz.vi 838
 
2060 mateusz.vi 839
  if (_dos_findnext(findnexthnd) != 0) return(1); // no subdirs found
2019 mateusz.vi 840
 
2057 mateusz.vi 841
  if (cycleFindResults(findnexthnd, subdir, dsubdir) == NULL) {
2019 mateusz.vi 842
    return 1;
2048 mateusz.vi 843
  }
844
  return 0;
2019 mateusz.vi 845
}
846
 
847
/**
848
 * Given an initial path, displays the directory tree with
849
 * a non-recursive function using a Stack.
850
 * initialpath must be large enough to hold an added slash \ or /
851
 * if it does not already end in one.
852
 * Returns the count of subdirs in initialpath.
853
 */
2037 mateusz.vi 854
static long traverseTree(char *initialpath) {
2019 mateusz.vi 855
  long subdirsInInitialpath;
856
  char padding[MAXPADLEN] = "";
2055 mateusz.vi 857
  char subdir[PATH_MAX];
858
  char dsubdir[PATH_MAX];
2062 mateusz.vi 859
  struct SUBDIRINFO *sdi;
2019 mateusz.vi 860
 
861
  STACK s;
862
  stackDefaults(&s);
863
  stackInit(&s);
864
 
2048 mateusz.vi 865
  if ( (sdi = newSubdirInfo(NULL, initialpath, initialpath)) == NULL) {
866
    return(0);
867
  }
2019 mateusz.vi 868
  stackPushItem(&s, sdi);
869
 
870
  /* Store count of subdirs in initial path so can display message if none. */
871
  subdirsInInitialpath = sdi->subdircnt;
872
 
873
  do
874
  {
2062 mateusz.vi 875
    sdi = (struct SUBDIRINFO *)stackPopItem(&s);
2019 mateusz.vi 876
 
2048 mateusz.vi 877
    if (sdi->findnexthnd == NULL) { // findfirst not called yet
2019 mateusz.vi 878
      // 1st time this subdirectory processed, so display its name & possibly files
879
      if (sdi->parent == NULL) // if initial path
880
      {
881
        // display initial path
882
        showCurrentPath(/*sdi->dsubdir*/initialpath, NULL, -1, &(sdi->ddata));
883
      }
884
      else // normal processing (display path, add necessary padding)
885
      {
886
        showCurrentPath(sdi->dsubdir, padding, (sdi->parent->subdircnt > 0L)?1 : 0, &(sdi->ddata));
887
        addPadding(padding, (sdi->parent->subdircnt > 0L)?1 : 0);
888
      }
889
 
890
      if (showFiles == SHOWFILESON)  displayFiles(sdi->currentpath, padding, (sdi->subdircnt > 0L)?1 : 0, &(sdi->ddata));
2024 mateusz.vi 891
      displaySummary(padding, (sdi->subdircnt > 0L)?1 : 0, &(sdi->ddata));
2019 mateusz.vi 892
    }
893
 
894
    if (sdi->subdircnt > 0) /* if (there are more subdirectories to process) */
895
    {
896
      int flgErr;
2048 mateusz.vi 897
      if (sdi->findnexthnd == NULL) {
2019 mateusz.vi 898
        sdi->findnexthnd = findFirstSubdir(sdi->currentpath, subdir, dsubdir);
2048 mateusz.vi 899
        flgErr = (sdi->findnexthnd == NULL);
900
      } else {
2019 mateusz.vi 901
        flgErr = findNextSubdir(sdi->findnexthnd, subdir, dsubdir);
902
      }
903
 
904
      if (flgErr) // don't add invalid paths to stack
905
      {
906
        printf("INTERNAL ERROR: subdir count changed, expecting %li more!\n", sdi->subdircnt+1L);
907
 
908
        sdi->subdircnt = 0; /* force subdir counter to 0, none left */
909
        stackPushItem(&s, sdi);
910
      }
911
      else
912
      {
913
        sdi->subdircnt = sdi->subdircnt - 1L; /* decrement subdirs left count */
914
        stackPushItem(&s, sdi);
915
 
916
        /* store necessary information, validate subdir, and if no error store it. */
917
        if ((sdi = newSubdirInfo(sdi, subdir, dsubdir)) != NULL)
918
          stackPushItem(&s, sdi);
919
      }
920
    }
921
    else /* this directory finished processing, so free resources */
922
    {
923
      /* Remove the padding for this directory, all but initial path. */
924
      if (sdi->parent != NULL)
925
        removePadding(padding);
926
 
927
      /* Prevent resource leaks, by ending findsearch and freeing memory. */
2060 mateusz.vi 928
      _dos_findclose(sdi->findnexthnd);
2019 mateusz.vi 929
      if (sdi != NULL)
930
      {
931
        if (sdi->currentpath != NULL)
932
          free(sdi->currentpath);
933
        free(sdi);
934
      }
935
    }
936
  } while (stackTotalItems(&s)); /* while (stack is not empty) */
937
 
938
  stackTerm(&s);
939
 
940
  return subdirsInInitialpath;
941
}
942
 
943
 
2037 mateusz.vi 944
int main(int argc, char **argv) {
2019 mateusz.vi 945
  char serial[SERIALLEN]; /* volume serial #  0000:0000 */
946
  char volume[VOLLEN];    /* volume name (label), possibly none */
947
 
2066 mateusz.vi 948
  /* load translation strings */
949
  svarlang_autoload_exepath(argv[0], getenv("LANG"));
2019 mateusz.vi 950
 
951
  /* Parse any command line arguments, obtain path */
952
  parseArguments(argc, argv);
953
 
954
  /* Initialize screen size, may reset pause to NOPAUSE if redirected */
955
  getConsoleSize();
956
 
957
  /* Get Volume & Serial Number */
958
  GetVolumeAndSerial(volume, serial, path);
2066 mateusz.vi 959
  if (volume[0] == 0) {
960
    pprintf(svarlang_strid(0x0102)); /* Dir PATH listing */
961
  } else {
962
    pprintf(svarlang_strid(0x0103), volume); /* Dir PATH listing for volume ... */
963
  }
964
  if (serial[0] != '\0') {  /* Don't print anything if no serial# found */
965
    pprintf(svarlang_strid(0x0104), serial); /* vol serial num is ... */
966
  }
2019 mateusz.vi 967
 
968
  /* now traverse & print tree, returns nonzero if has subdirectories */
2066 mateusz.vi 969
  if (traverseTree(path) == 0) {
970
    pprintf(svarlang_strid(0x0105)); /* no subdirs exist */
971
  } else if (dspSumDirs) { /* show count of directories processed */
2019 mateusz.vi 972
    pprintf("\n    %lu total directories\n", totalSubDirCnt+1);
2066 mateusz.vi 973
  }
2019 mateusz.vi 974
 
2066 mateusz.vi 975
  return(0);
2019 mateusz.vi 976
}