Subversion Repositories SvarDOS

Rev

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