Subversion Repositories SvarDOS

Rev

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