Subversion Repositories SvarDOS

Rev

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