Subversion Repositories SvarDOS

Rev

Rev 2243 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
421 mateuszvis 1
/* This file is part of the SvarCOM project and is published under the terms
2
 * of the MIT license.
3
 *
1716 mateusz.vi 4
 * Copyright (C) 2021-2024 Mateusz Viste
421 mateuszvis 5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a
7
 * copy of this software and associated documentation files (the "Software"),
8
 * to deal in the Software without restriction, including without limitation
9
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
 * and/or sell copies of the Software, and to permit persons to whom the
11
 * Software is furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
 * DEALINGS IN THE SOFTWARE.
23
 */
24
 
368 mateuszvis 25
/*
26
 * dir
27
 *
28
 * Displays a list of files and subdirectories in a directory.
29
 *
30
 * DIR [drive:][path][filename] [/P] [/W] [/A[:]attributes] [/O[[:]sortorder]] [/S] [/B] [/L]
31
 *
32
 * /P Pauses after each screenful of information.
33
 * /W Uses wide list format.
34
 *
35
 * /A Displays file with specified attributes:
36
 *     D Directories           R Read-only files     H Hidden files
37
 *     A Ready for archiving   S System files        - prefix meaning "not"
38
 *
39
 * /O List files in sorted order:
40
 *     N by name            S by size              E by extension
41
 *     D by date            G group dirs first     - prefix to reverse order
42
 *
43
 * /S Displays files in specified directory and all subdirectories.
44
 * /B Uses bare format (no heading information or summary)
45
 * /L Uses lowercases
2189 mateusz.vi 46
 *
47
 * about /S - recursive DIR on specified (or current) path and subdirectories:
48
 * prerequisite: some sort of mechanism that works as a stack pile of DTAs
49
 *
50
 * /S logic:
51
 * 1. do a FindFirst on current directory
52
 * 2. do FindNext calls in a loop, if a DIR entry is encountered, remember its
53
 *    name and put a copy of the current DTA on stack, then continue the
54
 *    listing without further interruption
55
 * 3. if a new DIR was discovered, do a FindFirst on it and jmp to 2.
56
 *    if no DIR found, then go to 4.
57
 * 4. look on the stack for a DTA.
58
 *    if any found, pop it and jmp to 2.
59
 *    otherwise job is done, exit.
368 mateuszvis 60
 */
61
 
396 mateuszvis 62
/* NOTE: /A attributes are matched in an exclusive way, ie. only files with
63
 *       the specified attributes are matched. This is different from how DOS
64
 *       itself matches attributes hence DIR cannot rely on the attributes
65
 *       filter within FindFirst.
66
 *
67
 * NOTE: Multiple /A are not supported - only the last one is significant.
68
 */
69
 
420 mateuszvis 70
 
1991 mateusz.vi 71
/* width of a column in wide mode output: 15 chars is the MINIMUM because
72
 * directories are enclosed in [BRACKETS] and they may have an extension, too.
73
 * Hence "[12345678.123]" is the longest we can get. Plus a delimiter space. */
74
#define WCOLWIDTH 15
424 mateuszvis 75
 
1991 mateusz.vi 76
 
1716 mateusz.vi 77
/* a "tiny" DTA is a DTA that is stripped from bytes that are not needed for
78
 * DIR operations */
79
_Packed struct TINYDTA {
80
/*  char reserved[21];
81
  unsigned char attr; */
82
  unsigned short time_sec2:5;
83
  unsigned short time_min:6;
84
  unsigned short time_hour:5;
85
  unsigned short date_dy:5;
86
  unsigned short date_mo:4;
87
  unsigned short date_yr:7;
88
  unsigned long size;
89
/*  char fname[13]; */
90
  char fname[12];
91
};
92
 
93
 
2230 mateusz.vi 94
static void far *cmd_dir_farmalloc(unsigned short segcount);
95
#pragma aux cmd_dir_farmalloc = \
96
"mov ah, 0x48" \
97
"int 0x21" \
98
"jnc DONE" \
99
"xor ax, ax" \
100
"DONE:" \
101
"xor bx, bx" \
102
"mov es, ax" \
103
parm [bx] \
104
modify [ax] \
105
value [es bx] \
106
 
107
 
108
static void cmd_dir_farfree(void far *ptr);
109
#pragma aux cmd_dir_farfree = \
110
"mov ah, 0x49" \
111
"int 0x21" \
112
parm [es ax] \
113
modify [ax]
114
 
115
 
116
 
2246 mateusz.vi 117
static unsigned long mul16(unsigned short a, unsigned short b);
118
#pragma aux mul16 = \
119
"mul bx" \
120
parm [ax] [bx] \
121
value [dx ax]
122
 
123
 
124
 
125
/* multiplies two 32 bits number together using 16bit math to avoid
126
 * linkage against Watcom libc.
127
 * AB * CD = (A*C << 32) + ((B*C + A*D) << 16) + B*D */
128
static unsigned long mul32(unsigned long ab, unsigned long cd) {
129
  unsigned short a = ab >> 16l;
130
  unsigned short b = ab & 0xffff;
131
  unsigned short c = cd >> 16l;
132
  unsigned short d = cd & 0xffff;
133
  unsigned long res;
134
 
135
  res = mul16(b, c);
136
  res += mul16(a, d);
137
  res <<= 16;
138
  res += mul16(b, d);
139
  return(res);
140
}
141
 
142
 
424 mateuszvis 143
/* fills freebytes with free bytes for drv (A=0, B=1, etc)
144
 * returns DOS ERR code on failure */
145
static unsigned short cmd_dir_df(unsigned long *freebytes, unsigned char drv) {
146
  unsigned short res = 0;
147
  unsigned short sects_per_clust = 0, avail_clusts = 0, bytes_per_sect = 0;
148
 
149
  _asm {
150
    push ax
151
    push bx
152
    push cx
153
    push dx
154
 
155
    mov ah, 0x36  /* DOS 2+ -- Get Disk Free Space */
156
    mov dl, [drv] /* A=1, B=2, etc (0 = DEFAULT DRIVE) */
157
    inc dl
158
    int 0x21      /* AX=sects_per_clust, BX=avail_clusts, CX=bytes_per_sect, DX=tot_clusters */
159
    cmp ax, 0xffff /* AX=0xffff on error (invalid drive) */
160
    jne COMPUTEDF
161
    mov [res], 0x0f /* fill res with DOS error code 15 ("invalid drive") */
162
    jmp DONE
163
 
164
    COMPUTEDF:
165
    /* freebytes = AX * BX * CX */
166
    mov [sects_per_clust], ax
167
    mov [avail_clusts], bx
168
    mov [bytes_per_sect], cx
169
 
170
    DONE:
171
    pop dx
172
    pop cx
173
    pop bx
174
    pop ax
175
  }
176
 
177
  /* multiple steps to avoid uint16 overflow */
2246 mateusz.vi 178
  *freebytes = mul32(sects_per_clust, avail_clusts);
179
  *freebytes = mul32(*freebytes, bytes_per_sect);
424 mateuszvis 180
 
181
  return(res);
182
}
183
 
184
 
528 mateuszvis 185
static void dir_pagination(unsigned short *availrows) {
186
  *availrows -= 1;
187
  if (*availrows == 0) {
188
    press_any_key();
189
    *availrows = screen_getheight() - 1;
190
  }
191
}
192
 
193
 
2206 mateusz.vi 194
static void dir_print_dirprefix(const char *p) {
195
  unsigned char t, lastbkslash;
196
  char buff[2] = {0, 0};
197
 
198
  /* find the last backslash of path */
199
  lastbkslash = 0;
200
  for (t = 0; p[t] != 0; t++) {
201
    if (p[t] == '\\') lastbkslash = t;
202
  }
203
 
204
  /* print path until last bkslash */
205
  do {
206
    *buff = *p;
207
    output(buff);
208
    p++;
209
  } while (lastbkslash-- != 0);
210
}
211
 
212
 
2204 mateusz.vi 213
/* print the "Directory of C:\ABC\.... string using a buffer with possible
214
 * file pattern garbage trailing */
215
static void dir_print_dirof(const char *p, unsigned short *availrows, unsigned char pagination) {
216
  char buff[2] = {0, 0};
217
  const char *dirof = svarlang_str(37,20); /* Directory of % */
218
 
2205 mateusz.vi 219
  outputnl("");
220
  if (pagination) dir_pagination(availrows);
221
 
2204 mateusz.vi 222
  /* print string until % */
223
  while ((*dirof != 0) && (*dirof != '%')) {
224
    *buff = *dirof;
225
    output(buff);
226
    dirof++;
227
  }
228
 
229
  if (*dirof != '%') return;
230
  dirof++;
231
 
232
  /* print path until last bkslash */
2206 mateusz.vi 233
  dir_print_dirprefix(p);
2204 mateusz.vi 234
 
235
  /* print the rest of the dirof string */
236
  while (*dirof != 0) {
237
    *buff = *dirof;
238
    output(buff);
239
    dirof++;
240
  }
241
 
242
  outputnl("");
243
  if (pagination) dir_pagination(availrows);
244
  outputnl("");
245
  if (pagination) dir_pagination(availrows);
246
}
247
 
248
 
2193 mateusz.vi 249
/* add a new dirname to path, C:\XXX\*.EXE + YYY -> C:\XXX\YYY\*.EXE */
250
static void path_add(char *path, const char *dirname) {
2196 mateusz.vi 251
  short i, ostatni = -1;
2198 mateusz.vi 252
  //printf("path_add(%s,%s) -> ", path, dirname);
2193 mateusz.vi 253
  /* find the last backslash */
254
  for (i = 0; path[i] != 0; i++) {
255
    if (path[i] == '\\') ostatni = i;
256
  }
257
  /* abort on error */
258
  if (ostatni == -1) return;
259
  /* do the trick */
2196 mateusz.vi 260
  /* move ending to the right */
2214 mateusz.vi 261
  memcpy_rtl(path + ostatni + sv_strlen(dirname) + 1, path + ostatni, sv_strlen(path + ostatni) + 1);
2196 mateusz.vi 262
  /* fill in the space with dirname */
2214 mateusz.vi 263
  memcpy_ltr(path + ostatni + 1, dirname, sv_strlen(dirname));
2198 mateusz.vi 264
  //printf("'%s'\n", path);
2193 mateusz.vi 265
}
266
 
267
 
268
/* take back last dir from path, C:\XXX\YYY\*.EXE -> C:\XXX\*.EXE */
269
static void path_back(char *path) {
270
  short i, ostatni = -1, przedostatni = -1;
2198 mateusz.vi 271
  //printf("path_back(%s) -> ", path);
2193 mateusz.vi 272
  /* find the two last backslashes */
273
  for (i = 0; path[i] != 0; i++) {
274
    if (path[i] == '\\') {
275
      przedostatni = ostatni;
276
      ostatni = i;
277
    }
278
  }
279
  /* abort on error */
280
  if (przedostatni == -1) return;
281
  /* do the trick */
2196 mateusz.vi 282
  memcpy_ltr(path + przedostatni, path + ostatni, 1 + i - ostatni);
2198 mateusz.vi 283
  //printf("'%s'\n", path);
2193 mateusz.vi 284
}
285
 
286
 
542 mateuszvis 287
/* parse an attr list like "Ar-hS" and fill bitfield into attrfilter_may and attrfilter_must.
288
 * /AHS   -> adds S and H to mandatory attribs ("must")
289
 * /A-S   -> removes S from allowed attribs ("may")
290
 * returns non-zero on error. */
291
static int dir_parse_attr_list(const char *arg, unsigned char *attrfilter_may, unsigned char *attrfilter_must) {
292
  for (; *arg != 0; arg++) {
293
    unsigned char curattr;
294
    char not;
295
    if (*arg == '-') {
296
      not = 1;
297
      arg++;
298
    } else {
299
      not = 0;
300
    }
301
    switch (*arg) {
302
      case 'd':
303
      case 'D':
304
        curattr = DOS_ATTR_DIR;
305
        break;
306
      case 'r':
307
      case 'R':
308
        curattr = DOS_ATTR_RO;
309
        break;
310
      case 'a':
311
      case 'A':
312
        curattr = DOS_ATTR_ARC;
313
        break;
314
      case 'h':
315
      case 'H':
316
        curattr = DOS_ATTR_HID;
317
        break;
318
      case 's':
319
      case 'S':
320
        curattr = DOS_ATTR_SYS;
321
        break;
322
      default:
323
        return(-1);
324
    }
325
    /* update res bitfield */
326
    if (not) {
327
      *attrfilter_may &= ~curattr;
328
    } else {
329
      *attrfilter_must |= curattr;
330
    }
331
  }
332
  return(0);
333
}
334
 
335
 
1716 mateusz.vi 336
/* compare attributes in a DTA node to mandatory and optional attributes. returns 1 on match, 0 otherwise */
337
static int filter_attribs(const struct DTA *dta, unsigned char attrfilter_must, unsigned char attrfilter_may) {
338
  /* if mandatory attribs are requested, filter them now */
339
  if ((attrfilter_must & dta->attr) != attrfilter_must) return(0);
340
 
341
  /* if file contains attributes that are not allowed -> skip */
342
  if ((~attrfilter_may & dta->attr) != 0) return(0);
343
 
344
  return(1);
345
}
346
 
347
 
1719 mateusz.vi 348
static struct {
349
  struct TINYDTA far *dtabuf_root;
350
  char order[8]; /* GNESD values (ucase = lower first ; lcase = higher first) */
1739 mateusz.vi 351
  unsigned char sortownia[256]; /* collation table (used for NLS-aware sorts) */
1719 mateusz.vi 352
} glob_sortcmp_dat;
1716 mateusz.vi 353
 
1719 mateusz.vi 354
 
355
/* translates an order string like "GNE-S" into values fed into the order[]
356
 * table of glob_sortcmp_dat. returns 0 on success, non-zero otherwise. */
1724 mateusz.vi 357
static int dir_process_order_directive(const char *ordstring) {
1719 mateusz.vi 358
  const char *gnesd = "gnesd"; /* must be lower case */
359
  int ordi, orderi = 0, i;
360
 
361
  /* tabula rasa */
362
  glob_sortcmp_dat.order[0] = 0;
363
 
1721 mateusz.vi 364
  /* /O alone is a short hand for /OGN */
365
  if (*ordstring == 0) {
366
    glob_sortcmp_dat.order[0] = 'G';
367
    glob_sortcmp_dat.order[1] = 'N';
368
    glob_sortcmp_dat.order[2] = 0;
369
  }
370
 
1726 mateusz.vi 371
  /* stupid MSDOS compatibility ("DIR /O:GNE") */
372
  if (*ordstring == ':') ordstring++;
373
 
1719 mateusz.vi 374
  /* parsing */
375
  for (ordi = 0; ordstring[ordi] != 0; ordi++) {
376
    if (ordstring[ordi] == '-') {
377
      if ((ordstring[ordi + 1] == '-') || (ordstring[ordi + 1] == 0)) return(-1);
378
      continue;
379
    }
380
    if (orderi == sizeof(glob_sortcmp_dat.order)) return(-1);
381
 
382
    for (i = 0; gnesd[i] != 0; i++) {
383
      if ((ordstring[ordi] | 32) == gnesd[i]) { /* | 32 is lcase-ing the char */
384
        if ((ordi > 0) && (ordstring[ordi - 1] == '-')) {
385
          glob_sortcmp_dat.order[orderi] = gnesd[i];
386
        } else {
387
          glob_sortcmp_dat.order[orderi] = gnesd[i] ^ 32;
388
        }
389
        orderi++;
390
        break;
391
      }
392
    }
393
    if (gnesd[i] == 0) return(-1);
394
  }
395
 
396
  return(0);
397
}
398
 
399
 
2242 mateusz.vi 400
static int sortcmp(const struct TINYDTA far *dta1, const struct TINYDTA far *dta2) {
1719 mateusz.vi 401
  char *ordconf = glob_sortcmp_dat.order;
402
 
403
  /* debug stuff
404
  {
405
    int i;
406
    printf("%lu vs %lu | ", dta1->size, dta2->size);
407
    for (i = 0; dta1->fname[i] != 0; i++) printf("%c", dta1->fname[i]);
408
    printf(" vs ");
409
    for (i = 0; dta2->fname[i] != 0; i++) printf("%c", dta2->fname[i]);
410
    printf("\n");
411
  } */
412
 
413
  for (;;) {
414
    int r = -1;
415
    if (*ordconf & 32) r = 1;
416
 
417
    switch (*ordconf | 32) {
418
      case 'g': /* sort by type (directories first, then files) */
419
        if ((dta1->time_sec2 & DOS_ATTR_DIR) > (dta2->time_sec2 & DOS_ATTR_DIR)) return(0 - r);
420
        if ((dta1->time_sec2 & DOS_ATTR_DIR) < (dta2->time_sec2 & DOS_ATTR_DIR)) return(r);
421
        break;
422
      case ' ': /* default (last resort) sort: by name */
423
      case 'e': /* sort by extension */
424
      case 'n': /* sort by filename */
425
      {
426
        const char far *f1 = dta1->fname;
427
        const char far *f2 = dta2->fname;
428
        int i, limit = 12;
429
        /* special handling for '.' and '..' entries */
430
        if ((f1[0] == '.') && (f2[0] != '.')) return(0 - r);
431
        if ((f2[0] == '.') && (f1[0] != '.')) return(r);
432
 
433
        if ((*ordconf | 32) == 'e') {
434
          /* fast-forward to extension or end of filename */
435
          while ((*f1 != 0) && (*f1 != '.')) f1++;
436
          while ((*f2 != 0) && (*f2 != '.')) f2++;
437
          limit = 4; /* TINYDTA structs are not nul-terminated */
438
        }
439
        /* cmp */
440
        for (i = 0; i < limit; i++) {
1739 mateusz.vi 441
          if ((glob_sortcmp_dat.sortownia[(unsigned char)(*f1)]) < (glob_sortcmp_dat.sortownia[(unsigned char)(*f2)])) return(0 - r);
442
          if ((glob_sortcmp_dat.sortownia[(unsigned char)(*f1)]) > (glob_sortcmp_dat.sortownia[(unsigned char)(*f2)])) return(r);
1719 mateusz.vi 443
          if (*f1 == 0) break;
444
          f1++;
445
          f2++;
446
        }
447
      }
448
        break;
449
      case 's': /* sort by size */
450
        if (dta1->size > dta2->size) return(r);
451
        if (dta1->size < dta2->size) return(0 - r);
452
        break;
453
      case 'd': /* sort by date */
454
        if (dta1->date_yr < dta2->date_yr) return(0 - r);
455
        if (dta1->date_yr > dta2->date_yr) return(r);
456
        if (dta1->date_mo < dta2->date_mo) return(0 - r);
457
        if (dta1->date_mo > dta2->date_mo) return(r);
458
        if (dta1->date_dy < dta2->date_dy) return(0 - r);
459
        if (dta1->date_dy > dta2->date_dy) return(r);
460
        if (dta1->time_hour < dta2->time_hour) return(0 - r);
461
        if (dta1->time_hour > dta2->time_hour) return(r);
462
        if (dta1->time_min < dta2->time_min) return(0 - r);
463
        if (dta1->time_min > dta2->time_min) return(r);
464
        break;
465
    }
466
 
467
    if (*ordconf == 0) break;
468
    ordconf++;
469
  }
470
 
471
  return(0);
472
}
473
 
474
 
2242 mateusz.vi 475
/* sort function for DIR /O (selection sort) */
476
static void cmd_dir_sort(struct TINYDTA far *dta, unsigned short dtacount) {
477
  int i, t, smallest;
478
  for (i = 0; i < (dtacount - 1); i++) {
479
    // find "smallest" entry
480
    smallest = i;
481
    for (t = i + 1; t < dtacount; t++) {
482
      if (sortcmp(dta + t, dta + smallest) < 0) smallest = t;
483
    }
484
    // if smallest different than current found then swap
485
    if (smallest != i) {
486
      struct TINYDTA entry;
487
      memcpy_ltr_far(&entry, dta + i, sizeof(struct TINYDTA));
488
      memcpy_ltr_far(dta + i, dta + smallest, sizeof(struct TINYDTA));
489
      memcpy_ltr_far(dta + smallest, &entry, sizeof(struct TINYDTA));
490
    }
491
  }
492
}
493
 
494
 
542 mateuszvis 495
#define DIR_ATTR_DEFAULT (DOS_ATTR_RO | DOS_ATTR_DIR | DOS_ATTR_ARC)
496
 
1724 mateusz.vi 497
struct dirrequest {
498
  unsigned char attrfilter_may;
499
  unsigned char attrfilter_must;
500
  const char *filespecptr;
420 mateuszvis 501
 
396 mateuszvis 502
  #define DIR_FLAG_PAUSE  1
503
  #define DIR_FLAG_RECUR  4
420 mateuszvis 504
  #define DIR_FLAG_LCASE  8
1719 mateusz.vi 505
  #define DIR_FLAG_SORT  16
1724 mateusz.vi 506
  unsigned char flags;
368 mateuszvis 507
 
420 mateuszvis 508
  #define DIR_OUTPUT_NORM 1
509
  #define DIR_OUTPUT_WIDE 2
510
  #define DIR_OUTPUT_BARE 3
1724 mateusz.vi 511
  unsigned char format;
512
};
420 mateuszvis 513
 
1719 mateusz.vi 514
 
1724 mateusz.vi 515
static int dir_parse_cmdline(struct dirrequest *req, const char **argv) {
516
  for (; *argv != NULL; argv++) {
517
    if (*argv[0] == '/') {
518
      const char *arg = *argv + 1;
396 mateuszvis 519
      char neg = 0;
520
      /* detect negations and get actual argument */
542 mateuszvis 521
      if (*arg == '-') {
522
        neg = 1;
523
        arg++;
524
      }
396 mateuszvis 525
      /* */
542 mateuszvis 526
      switch (*arg) {
396 mateuszvis 527
        case 'a':
528
        case 'A':
542 mateuszvis 529
          arg++;
530
          /* preset defaults */
1724 mateusz.vi 531
          req->attrfilter_may = DIR_ATTR_DEFAULT;
532
          req->attrfilter_must = 0;
542 mateuszvis 533
          /* /-A only allowed without further parameters (used to cancel possible previous /Asmth) */
534
          if (neg) {
535
            if (*arg != 0) {
536
              nls_outputnl_err(0, 2); /* invalid switch */
1724 mateusz.vi 537
              return(-1);
542 mateuszvis 538
            }
539
          } else {
1085 mateusz.vi 540
            /* skip colon if present */
541
            if (*arg == ':') arg++;
542 mateuszvis 542
            /* start with "allow everything" */
1724 mateusz.vi 543
            req->attrfilter_may = (DOS_ATTR_ARC | DOS_ATTR_DIR | DOS_ATTR_HID | DOS_ATTR_SYS | DOS_ATTR_RO);
544
            if (dir_parse_attr_list(arg, &(req->attrfilter_may), &(req->attrfilter_must)) != 0) {
542 mateuszvis 545
              nls_outputnl_err(0, 3); /* invalid parameter format */
1724 mateusz.vi 546
              return(-1);
542 mateuszvis 547
            }
548
          }
396 mateuszvis 549
          break;
399 mateuszvis 550
        case 'b':
551
        case 'B':
1724 mateusz.vi 552
          req->format = DIR_OUTPUT_BARE;
399 mateuszvis 553
          break;
421 mateuszvis 554
        case 'l':
555
        case 'L':
1724 mateusz.vi 556
          req->flags |= DIR_FLAG_LCASE;
420 mateuszvis 557
          break;
421 mateuszvis 558
        case 'o':
559
        case 'O':
1720 mateusz.vi 560
          if (neg) {
1724 mateusz.vi 561
            req->flags &= (0xff ^ DIR_FLAG_SORT);
1720 mateusz.vi 562
            break;
563
          }
1724 mateusz.vi 564
          if (dir_process_order_directive(arg+1) != 0) {
1719 mateusz.vi 565
            nls_output_err(0, 3); /* invalid parameter format */
566
            output(": ");
567
            outputnl(arg);
1724 mateusz.vi 568
            return(-1);
1719 mateusz.vi 569
          }
1724 mateusz.vi 570
          req->flags |= DIR_FLAG_SORT;
421 mateuszvis 571
          break;
396 mateuszvis 572
        case 'p':
573
        case 'P':
1724 mateusz.vi 574
          req->flags |= DIR_FLAG_PAUSE;
575
          if (neg) req->flags &= (0xff ^ DIR_FLAG_PAUSE);
396 mateuszvis 576
          break;
421 mateuszvis 577
        case 's':
578
        case 'S':
2193 mateusz.vi 579
          req->flags |= DIR_FLAG_RECUR;
420 mateuszvis 580
          break;
421 mateuszvis 581
        case 'w':
582
        case 'W':
1724 mateusz.vi 583
          req->format = DIR_OUTPUT_WIDE;
421 mateuszvis 584
          break;
393 mateuszvis 585
        default:
542 mateuszvis 586
          nls_outputnl_err(0, 2); /* invalid switch */
1724 mateusz.vi 587
          return(-1);
393 mateuszvis 588
      }
589
    } else {  /* filespec */
1724 mateusz.vi 590
      if (req->filespecptr != NULL) {
542 mateuszvis 591
        nls_outputnl_err(0, 4); /* too many parameters */
1724 mateusz.vi 592
        return(-1);
393 mateuszvis 593
      }
1724 mateusz.vi 594
      req->filespecptr = *argv;
393 mateuszvis 595
    }
596
  }
368 mateuszvis 597
 
1724 mateusz.vi 598
  return(0);
599
}
393 mateuszvis 600
 
1724 mateusz.vi 601
 
2201 mateusz.vi 602
static void dir_print_summary_files(char *buff64, unsigned short uint32maxlen, unsigned long summary_totsz, unsigned long summary_fcount, unsigned short *availrows, unsigned char flags, const struct nls_patterns *nls) {
603
  unsigned short i;
2225 mateusz.vi 604
 
2201 mateusz.vi 605
  /* x file(s) (maximum of files in a FAT-32 directory is 65'535) */
2218 mateusz.vi 606
  sv_memset(buff64, ' ', 8);
2225 mateusz.vi 607
  buff64[8] = 0;
2201 mateusz.vi 608
  i = nls_format_number(buff64 + 8, summary_fcount, nls);
609
  output(buff64 + i);
2225 mateusz.vi 610
  output(" ");
611
  output(svarlang_str(37,22)); /* "file(s)" */
612
  output(" ");
613
 
2201 mateusz.vi 614
  /* xxxx bytes */
2218 mateusz.vi 615
  sv_memset(buff64, ' ', 14);
2201 mateusz.vi 616
  i = nls_format_number(buff64 + uint32maxlen, summary_totsz, nls);
617
  output(buff64 + i + 1);
618
  output(" ");
619
  nls_outputnl(37,23); /* "bytes" */
620
  if (flags & DIR_FLAG_PAUSE) dir_pagination(availrows);
621
}
622
 
623
 
2241 mateusz.vi 624
/* max amount of files to sort - limited by the memory block I will allocate
625
 * to store the TINYDTA of each entry */
626
#define MAX_SORTABLE_FILES (65500 / sizeof(struct TINYDTA))
2193 mateusz.vi 627
 
1724 mateusz.vi 628
static enum cmd_result cmd_dir(struct cmd_funcparam *p) {
629
  struct DTA *dta = (void *)0x80; /* set DTA to its default location at 80h in PSP */
630
  struct TINYDTA far *dtabuf = NULL; /* used to buffer results when sorting is enabled */
631
  unsigned short dtabufcount = 0;
632
  unsigned short i;
633
  unsigned short availrows;  /* counter of available rows on display (used for /P) */
634
  unsigned short screenw = screen_getwidth();
635
  unsigned short wcols = screenw / WCOLWIDTH; /* number of columns in wide mode */
636
  unsigned char wcolcount;
637
  struct {
638
    struct nls_patterns nls;
639
    char buff64[64];
640
    char path[128];
2193 mateusz.vi 641
    struct DTA dtastack[64]; /* used for /S, max number of subdirs in DOS5 is 42 (A/B/C/...) */
642
    unsigned char dtastacklen;
643
  } *buf;
2201 mateusz.vi 644
  unsigned long summary_recurs_fcount = 0; /* used for /s global summary */
645
  unsigned long summary_recurs_totsz = 0;  /* used for /s global summary */
646
  unsigned long summary_fcount;
647
  unsigned long summary_totsz;
1724 mateusz.vi 648
  unsigned char drv = 0;
649
  struct dirrequest req;
2214 mateusz.vi 650
  unsigned short summary_alignpos = sv_strlen(svarlang_str(37,22)) + 2;
2200 mateusz.vi 651
  unsigned short uint32maxlen = 14; /* 13 is the max len of a 32 bit number with thousand separators (4'000'000'000) */
652
  if (screenw < 80) uint32maxlen = 10;
1724 mateusz.vi 653
 
654
  if (cmd_ishlp(p)) {
655
    nls_outputnl(37,0); /* "Displays a list of files and subdirectories in a directory" */
656
    outputnl("");
657
    nls_outputnl(37,1); /* "DIR [drive:][path][filename] [/P] [/W] [/A[:]attributes] [/O[[:]sortorder]] [/S] [/B] [/L]" */
658
    outputnl("");
659
    nls_outputnl(37,2); /* "/P Pauses after each screenful of information" */
660
    nls_outputnl(37,3); /* "/W Uses wide list format" */
661
    outputnl("");
662
    nls_outputnl(37,4); /* "/A Displays files with specified attributes:" */
663
    nls_outputnl(37,5); /* "    D Directories            R Read-only files        H Hidden files" */
664
    nls_outputnl(37,6); /* "    A Ready for archiving    S System files           - prefix meaning "not"" */
665
    outputnl("");
666
    nls_outputnl(37,7); /* "/O List files in sorted order:" */
667
    nls_outputnl(37,8); /* "    N by name                S by size                E by extension" */
668
    nls_outputnl(37,9); /* "    D by date                G group dirs first       - prefix to reverse order" */
669
    outputnl("");
670
    nls_outputnl(37,10); /* "/S Displays files in specified directory and all subdirectories" */
671
    nls_outputnl(37,11); /* "/B Uses bare format (no heading information or summary)" */
672
    nls_outputnl(37,12); /* "/L Uses lowercases" */
2205 mateusz.vi 673
    goto GAMEOVER;
1724 mateusz.vi 674
  }
675
 
2243 mateusz.vi 676
  /* reserve buf space within the upstream-supplied buffer */
677
  if (sizeof(*buf) > p->BUFFERSZ) {
2193 mateusz.vi 678
    nls_output_err(255, 8); /* insufficient memory */
2205 mateusz.vi 679
    goto GAMEOVER;
2193 mateusz.vi 680
  }
2243 mateusz.vi 681
  buf = (void *)(p->BUFFER);
682
  sv_bzero(buf, sizeof(*buf));
2193 mateusz.vi 683
 
1739 mateusz.vi 684
  /* zero out glob_sortcmp_dat and init the collation table */
2213 mateusz.vi 685
  sv_bzero(&glob_sortcmp_dat, sizeof(glob_sortcmp_dat));
1739 mateusz.vi 686
  for (i = 0; i < 256; i++) {
687
    glob_sortcmp_dat.sortownia[i] = i;
688
    /* sorting should be case-insensitive */
1740 mateusz.vi 689
    if ((i >= 'A') && (i <= 'Z')) glob_sortcmp_dat.sortownia[i] |= 32;
1739 mateusz.vi 690
  }
691
 
1743 mateusz.vi 692
  /* try to replace (or complement) my naive collation table with an NLS-aware
1744 mateusz.vi 693
   * version provided by the kernel (or NLSFUNC)
1745 mateusz.vi 694
   * see https://github.com/SvarDOS/bugz/issues/68 for some thoughts */
695
  {
1743 mateusz.vi 696
    _Packed struct nlsseqtab {
697
      unsigned char id;
698
      unsigned short taboff;
699
      unsigned short tabseg;
700
    } collat;
701
    void *colptr = &collat;
702
    unsigned char errflag = 1;
703
    _asm {
704
      push ax
705
      push bx
706
      push cx
707
      push dx
708
      push di
709
      push es
710
 
711
      mov ax, 0x6506  /* DOS 3.3+ - Get collating sequence table */
712
      mov bx, 0xffff  /* code page, FFFFh = "current" */
713
      mov cx, 5       /* size of buffer at ES:DI */
714
      mov dx, 0xffff  /* country id, FFFFh = "current" */
715
      push ds
716
      pop es          /* ES:DI = address of buffer for the 5-bytes struct */
717
      mov di, colptr
718
      int 0x21
719
      jc FAIL
720
      xor al, al
721
      mov errflag, al
722
      FAIL:
723
 
724
      pop es
725
      pop di
726
      pop dx
727
      pop cx
728
      pop bx
729
      pop ax
730
    }
731
 
732
    if ((errflag == 0) && (collat.id == 6)) {
733
      unsigned char far *ptr = MK_FP(collat.tabseg, collat.taboff);
734
      unsigned short count = *(unsigned short far *)ptr;
1745 mateusz.vi 735
#ifdef DIR_DUMPNLSCOLLATE
736
      printf("NLS AT %04X:%04X (%u elements)\n", collat.tabseg, collat.taboff, count);
737
#endif
1743 mateusz.vi 738
      if (count <= 256) { /* you never know */
739
        ptr += 2; /* skip the count header */
740
        for (i = 0; i < count; i++) {
741
          glob_sortcmp_dat.sortownia[i] = ptr[i];
1745 mateusz.vi 742
#ifdef DIR_DUMPNLSCOLLATE
743
          printf(" %03u", ptr[i]);
744
          if ((i & 15) == 15) {
745
            printf("\n");
746
            fflush(stdout);
747
          }
748
#endif
1743 mateusz.vi 749
        }
750
      }
751
    }
752
  }
753
 
1724 mateusz.vi 754
  i = nls_getpatterns(&(buf->nls));
755
  if (i != 0) nls_outputnl_doserr(i);
756
 
757
  /* disable usage of thousands separator on narrow screens */
758
  if (screenw < 80) buf->nls.thousep[0] = 0;
759
 
1725 mateusz.vi 760
  /*** PARSING COMMAND LINE STARTS *******************************************/
761
 
762
  /* init req with some defaults */
2213 mateusz.vi 763
  sv_bzero(&req, sizeof(req));
1725 mateusz.vi 764
  req.attrfilter_may = DIR_ATTR_DEFAULT;
765
  req.format = DIR_OUTPUT_NORM;
766
 
767
  /* process DIRCMD first (so it can be overidden by user's cmdline) */
768
  {
769
  const char far *dircmd = env_lookup_val(p->env_seg, "DIRCMD");
770
  if (dircmd != NULL) {
771
    const char *argvptrs[32];
772
    cmd_explode(buf->buff64, dircmd, argvptrs);
773
    if ((dir_parse_cmdline(&req, argvptrs) != 0) || (req.filespecptr != NULL)) {
774
      nls_output(255, 10);/* bad environment */
775
      output(" - ");
776
      outputnl("DIRCMD");
2205 mateusz.vi 777
      goto GAMEOVER;
1725 mateusz.vi 778
    }
779
  }
780
  }
781
 
782
  /* parse user's command line */
2205 mateusz.vi 783
  if (dir_parse_cmdline(&req, p->argv) != 0) goto GAMEOVER;
1724 mateusz.vi 784
 
2193 mateusz.vi 785
  /*** PARSING COMMAND LINE DONE *********************************************/
786
 
1725 mateusz.vi 787
  /* if no filespec provided, then it's about the current directory */
788
  if (req.filespecptr == NULL) req.filespecptr = ".";
789
 
2202 mateusz.vi 790
  availrows = screen_getheight() - 1;
528 mateuszvis 791
 
417 mateuszvis 792
  /* special case: "DIR drive:" (truename() fails on "C:" under MS-DOS 6.0) */
1724 mateusz.vi 793
  if ((req.filespecptr[0] != 0) && (req.filespecptr[1] == ':') && (req.filespecptr[2] == 0)) {
794
    if ((req.filespecptr[0] >= 'a') && (req.filespecptr[0] <= 'z')) {
795
      buf->path[0] = req.filespecptr[0] - ('a' - 1);
417 mateuszvis 796
    } else {
1724 mateusz.vi 797
      buf->path[0] = req.filespecptr[0] - ('A' - 1);
399 mateuszvis 798
    }
1717 mateusz.vi 799
    i = curpathfordrv(buf->path, buf->path[0]);
417 mateuszvis 800
  } else {
1724 mateusz.vi 801
    i = file_truename(req.filespecptr, buf->path);
399 mateuszvis 802
  }
417 mateuszvis 803
  if (i != 0) {
538 mateuszvis 804
    nls_outputnl_doserr(i);
2205 mateusz.vi 805
    goto GAMEOVER;
417 mateuszvis 806
  }
393 mateuszvis 807
 
2198 mateusz.vi 808
  /* volume label and serial */
1724 mateusz.vi 809
  if (req.format != DIR_OUTPUT_BARE) {
1717 mateusz.vi 810
    drv = buf->path[0];
399 mateuszvis 811
    if (drv >= 'a') {
812
      drv -= 'a';
813
    } else {
814
      drv -= 'A';
815
    }
1717 mateusz.vi 816
    cmd_vol_internal(drv, buf->buff64);
2202 mateusz.vi 817
    availrows -= 2;
2198 mateusz.vi 818
  }
819
 
820
  NEXT_ITER: /* re-entry point for /S recursing */
821
 
2201 mateusz.vi 822
  summary_fcount = 0;
823
  summary_totsz = 0;
824
 
2203 mateusz.vi 825
  /* if dir: append a backslash (also get its len) */
826
  i = path_appendbkslash_if_dir(buf->path);
827
 
828
  /* if ends with a \ then append ????????.??? */
2215 mateusz.vi 829
  if (buf->path[i - 1] == '\\') sv_strcat(buf->path, "????????.???");
2203 mateusz.vi 830
 
831
  /* ask DOS for list of files, but only with allowed attribs */
832
  i = findfirst(dta, buf->path, req.attrfilter_may);
833
 
834
  /* print "directory of" unless /B or /S mode with no match */
835
  if ((req.format != DIR_OUTPUT_BARE) && (((req.flags & DIR_FLAG_RECUR) == 0) || (i == 0))) {
2204 mateusz.vi 836
    dir_print_dirof(buf->path, &availrows, req.flags & DIR_FLAG_PAUSE);
399 mateuszvis 837
  }
838
 
2203 mateusz.vi 839
  /* if no file match then abort */
417 mateuszvis 840
  if (i != 0) {
2197 mateusz.vi 841
    if (req.flags & DIR_FLAG_RECUR) goto CHECK_RECURS;
538 mateuszvis 842
    nls_outputnl_doserr(i);
2205 mateusz.vi 843
    goto GAMEOVER;
417 mateuszvis 844
  }
845
 
1716 mateusz.vi 846
  /* if sorting is involved, then let's buffer all results (and sort them) */
1724 mateusz.vi 847
  if (req.flags & DIR_FLAG_SORT) {
1716 mateusz.vi 848
    /* allocate a memory buffer - try several sizes until one succeeds */
2194 mateusz.vi 849
    unsigned short max_dta_bufcount;
850
 
851
    /* compute the amount of DTAs I can buffer */
852
    for (max_dta_bufcount = MAX_SORTABLE_FILES; max_dta_bufcount != 0; max_dta_bufcount /= 2) {
2230 mateusz.vi 853
      dtabuf = cmd_dir_farmalloc(max_dta_bufcount * sizeof(struct TINYDTA) / 16);
1716 mateusz.vi 854
      if (dtabuf != NULL) break;
855
    }
2194 mateusz.vi 856
    /* printf("max_dta_bufcount = %u\n", max_dta_bufcount); */
1716 mateusz.vi 857
 
858
    if (dtabuf == NULL) {
859
      nls_outputnl_doserr(8); /* out of memory */
2205 mateusz.vi 860
      goto GAMEOVER;
1716 mateusz.vi 861
    }
862
 
863
    /* remember the address so I can free it afterwards */
1719 mateusz.vi 864
    glob_sortcmp_dat.dtabuf_root = dtabuf;
1716 mateusz.vi 865
 
866
    do {
867
      /* filter out files with uninteresting attributes */
1724 mateusz.vi 868
      if (filter_attribs(dta, req.attrfilter_must, req.attrfilter_may) == 0) continue;
1716 mateusz.vi 869
 
2207 mateusz.vi 870
      /* /B hides . and .. entries */
871
      if ((req.format == DIR_OUTPUT_BARE) && (dta->fname[0] == '.')) continue;
872
 
1719 mateusz.vi 873
      /* normalize "size" of directories to zero because kernel returns garbage
874
       * sizes for directories which might confuse the sorting routine later */
875
      if (dta->attr & DOS_ATTR_DIR) dta->size = 0;
876
 
2217 mateusz.vi 877
      memcpy_ltr_far(&(dtabuf[dtabufcount]), ((char *)dta) + 22, sizeof(struct TINYDTA));
1716 mateusz.vi 878
 
879
      /* save attribs in sec field, otherwise zero it (this field is not
880
       * displayed and dropping the attr field saves 2 bytes per entry) */
881
      dtabuf[dtabufcount++].time_sec2 = (dta->attr & 31);
882
 
883
      /* do I have any space left? */
884
      if (dtabufcount == max_dta_bufcount) {
1719 mateusz.vi 885
        //TODO some kind of user notification might be nice here
1716 mateusz.vi 886
        //outputnl("TOO MANY ENTRIES FOR SORTING! LIST IS UNSORTED");
887
        break;
888
      }
889
 
890
    } while (findnext(dta) == 0);
891
 
1742 mateusz.vi 892
    /* no match? kein gluck! (this can happen when filtering attribs with /A:xxx
893
     * because while findfirst() succeeds, all entries can be rejected) */
894
    if (dtabufcount == 0) {
2207 mateusz.vi 895
      if (req.flags & DIR_FLAG_RECUR) goto CHECK_RECURS;
1742 mateusz.vi 896
      nls_outputnl_doserr(2); /* "File not found" */
2205 mateusz.vi 897
      goto GAMEOVER;
1742 mateusz.vi 898
    }
899
 
2242 mateusz.vi 900
    /* sort the list */
901
    cmd_dir_sort(dtabuf, dtabufcount);
1716 mateusz.vi 902
 
2242 mateusz.vi 903
    /* preload first entry (last, since entries are sorted in reverse) */
1716 mateusz.vi 904
    dtabufcount--;
2242 mateusz.vi 905
    memcpy_ltr_far(((unsigned char *)dta) + 22, dtabuf + dtabufcount, sizeof(struct TINYDTA));
906
    dta->attr = dtabuf[dtabufcount].time_sec2; /* restore attr from the abused time_sec2 field */
1716 mateusz.vi 907
  }
908
 
420 mateuszvis 909
  wcolcount = 0; /* may be used for columns counting with wide mode */
396 mateuszvis 910
 
1716 mateusz.vi 911
  for (;;) {
542 mateuszvis 912
 
1716 mateusz.vi 913
    /* filter out attributes (skip if entry comes from buffer, then it was already veted) */
1741 mateusz.vi 914
    if (filter_attribs(dta, req.attrfilter_must, req.attrfilter_may) == 0) goto NEXT_ENTRY;
542 mateuszvis 915
 
2207 mateusz.vi 916
    /* /B hides . and .. entries */
917
    if ((req.format == DIR_OUTPUT_BARE) && (dta->fname[0] == '.')) continue;
918
 
2218 mateusz.vi 919
    /* turn string lcase (/L) - naive method, only low-ascii */
920
    if (req.flags & DIR_FLAG_LCASE) {
921
      char *s = dta->fname;
922
      while (*s != 0) {
923
        if ((*s >= 'A') && (*s <= 'Z')) *s |= 0x20;
924
        s++;
925
      }
926
    }
368 mateuszvis 927
 
424 mateuszvis 928
    summary_fcount++;
929
    if ((dta->attr & DOS_ATTR_DIR) == 0) summary_totsz += dta->size;
930
 
1724 mateusz.vi 931
    switch (req.format) {
420 mateuszvis 932
      case DIR_OUTPUT_NORM:
933
        /* print fname-space-extension (unless it's "." or "..", then print as-is) */
934
        if (dta->fname[0] == '.') {
935
          output(dta->fname);
2214 mateusz.vi 936
          i = sv_strlen(dta->fname);
420 mateuszvis 937
          while (i++ < 12) output(" ");
938
        } else {
1717 mateusz.vi 939
          file_fname2fcb(buf->buff64, dta->fname);
2214 mateusz.vi 940
          memcpy_rtl(buf->buff64 + 9, buf->buff64 + 8, 4);
1717 mateusz.vi 941
          buf->buff64[8] = ' ';
942
          output(buf->buff64);
420 mateuszvis 943
        }
944
        output(" ");
1960 mateusz.vi 945
        /* either <DIR> or right aligned 13 or 10 chars byte size, depending
946
         * on the presence of a thousands delimiter (max 2'000'000'000) */
947
        {
2214 mateusz.vi 948
          unsigned short szlen = 10 + (sv_strlen(buf->nls.thousep) * 3);
2218 mateusz.vi 949
          sv_memset(buf->buff64, ' ', 16);
1960 mateusz.vi 950
          if (dta->attr & DOS_ATTR_DIR) {
2216 mateusz.vi 951
            sv_strcpy(buf->buff64 + szlen, svarlang_str(37,21));
1960 mateusz.vi 952
          } else {
953
            nls_format_number(buf->buff64 + 12, dta->size, &(buf->nls));
954
          }
2214 mateusz.vi 955
          output(buf->buff64 + sv_strlen(buf->buff64) - szlen);
420 mateuszvis 956
        }
1960 mateusz.vi 957
        /* one spaces and NLS DATE */
1717 mateusz.vi 958
        buf->buff64[0] = ' ';
1141 mateusz.vi 959
        if (screenw >= 80) {
1960 mateusz.vi 960
          nls_format_date(buf->buff64 + 1, dta->date_yr + 1980, dta->date_mo, dta->date_dy, &(buf->nls));
1141 mateusz.vi 961
        } else {
1960 mateusz.vi 962
          nls_format_date(buf->buff64 + 1, (dta->date_yr + 80) % 100, dta->date_mo, dta->date_dy, &(buf->nls));
1141 mateusz.vi 963
        }
1717 mateusz.vi 964
        output(buf->buff64);
420 mateuszvis 965
 
966
        /* one space and NLS TIME */
1717 mateusz.vi 967
        nls_format_time(buf->buff64 + 1, dta->time_hour, dta->time_min, 0xff, &(buf->nls));
968
        outputnl(buf->buff64);
420 mateuszvis 969
        break;
970
 
971
      case DIR_OUTPUT_WIDE: /* display in columns of 12 chars per item */
2214 mateusz.vi 972
        i = sv_strlen(dta->fname);
420 mateuszvis 973
        if (dta->attr & DOS_ATTR_DIR) {
974
          i += 2;
975
          output("[");
976
          output(dta->fname);
977
          output("]");
978
        } else {
979
          output(dta->fname);
980
        }
981
        while (i++ < WCOLWIDTH) output(" ");
982
        if (++wcolcount == wcols) {
983
          wcolcount = 0;
984
          outputnl("");
528 mateuszvis 985
        } else {
986
          availrows++; /* wide mode is the only one that does not write one line per file */
420 mateuszvis 987
        }
988
        break;
989
 
990
      case DIR_OUTPUT_BARE:
2206 mateusz.vi 991
        /* if /B used in combination with /S then files are displayed with full path */
992
        if (req.flags & DIR_FLAG_RECUR) dir_print_dirprefix(buf->path);
420 mateuszvis 993
        outputnl(dta->fname);
994
        break;
396 mateuszvis 995
    }
368 mateuszvis 996
 
1724 mateusz.vi 997
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
420 mateuszvis 998
 
1741 mateusz.vi 999
    NEXT_ENTRY:
1716 mateusz.vi 1000
    /* take next entry, either from buf or disk */
1001
    if (dtabufcount > 0) {
1002
      dtabufcount--;
2242 mateusz.vi 1003
      memcpy_ltr_far(((unsigned char *)dta) + 22, dtabuf + dtabufcount, sizeof(struct TINYDTA));
1004
      dta->attr = dtabuf[dtabufcount].time_sec2; /* restore attr from the abused time_sec2 field */
1716 mateusz.vi 1005
    } else {
1006
      if (findnext(dta) != 0) break;
1007
    }
420 mateuszvis 1008
 
1716 mateusz.vi 1009
  }
1010
 
528 mateuszvis 1011
  if (wcolcount != 0) {
1012
    outputnl(""); /* in wide mode make sure to end on a clear row */
1724 mateusz.vi 1013
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
528 mateuszvis 1014
  }
420 mateuszvis 1015
 
424 mateuszvis 1016
  /* print out summary (unless bare output mode) */
1724 mateusz.vi 1017
  if (req.format != DIR_OUTPUT_BARE) {
2201 mateusz.vi 1018
    dir_print_summary_files(buf->buff64, uint32maxlen, summary_totsz, summary_fcount, &availrows, req.flags, &(buf->nls));
424 mateuszvis 1019
  }
1020
 
2201 mateusz.vi 1021
  /* update global counters in case /s is used */
1022
  summary_recurs_fcount += summary_fcount;
1023
  summary_recurs_totsz += summary_totsz;
1024
 
2193 mateusz.vi 1025
  /* /S processing */
2197 mateusz.vi 1026
  CHECK_RECURS:
1027
  /* if /S then look for a subdir */
1028
  if (req.flags & DIR_FLAG_RECUR) {
1029
    /* do the findfirst on *.* instead of reusing the user filter */
1030
    char *s;
1031
    char backup[4];
2200 mateusz.vi 1032
    //printf("orig path='%s' new=", buf->path);
2197 mateusz.vi 1033
    for (s = buf->path; *s != 0; s++);
1034
    for (; s[-1] != '\\'; s--);
1035
    memcpy_ltr(backup, s, 4);
1036
    memcpy_ltr(s, "*.*", 4);
2200 mateusz.vi 1037
    //printf("'%s'\n", buf->path);
2197 mateusz.vi 1038
    if (findfirst(dta, buf->path, DOS_ATTR_DIR) == 0) {
1039
      memcpy_ltr(s, backup, 4);
1040
      for (;;) {
1041
        if ((dta->fname[0] != '.') && (dta->attr & DOS_ATTR_DIR)) break;
1042
        if (findnext(dta) != 0) goto NOSUBDIR;
1043
      }
2200 mateusz.vi 1044
      //printf("GOT DIR (/S): '%s'\n", dta->fname);
2197 mateusz.vi 1045
      /* add dir to path and redo scan */
1046
      memcpy_ltr(&(buf->dtastack[buf->dtastacklen]), dta, sizeof(struct DTA));
1047
      buf->dtastacklen++;
1048
      path_add(buf->path, dta->fname);
1049
      goto NEXT_ITER;
1050
    }
1051
    memcpy_ltr(s, backup, 4);
2193 mateusz.vi 1052
  }
2197 mateusz.vi 1053
  NOSUBDIR:
1054
 
2193 mateusz.vi 1055
  while (buf->dtastacklen > 0) {
1056
    /* rewind path one directory back, pop the next dta and do a FindNext */
1057
    path_back(buf->path);
1058
    buf->dtastacklen--;
1059
    TRYNEXTENTRY:
1060
    if (findnext(&(buf->dtastack[buf->dtastacklen])) != 0) continue;
1061
    if ((buf->dtastack[buf->dtastacklen].attr & DOS_ATTR_DIR) == 0) goto TRYNEXTENTRY;
2200 mateusz.vi 1062
    if (buf->dtastack[buf->dtastacklen].fname[0] == '.') goto TRYNEXTENTRY;
2193 mateusz.vi 1063
    /* something found -> add dir to path and redo scan */
1064
    path_add(buf->path, buf->dtastack[buf->dtastacklen].fname);
1065
    goto NEXT_ITER;
1066
  }
1067
 
2200 mateusz.vi 1068
  /* print out disk space available (unless bare output mode) */
1069
  if (req.format != DIR_OUTPUT_BARE) {
2201 mateusz.vi 1070
    /* if /s mode then print also global stats */
1071
    if (req.flags & DIR_FLAG_RECUR) {
2205 mateusz.vi 1072
      if (summary_recurs_fcount == 0) {
1073
        file_truename(req.filespecptr, buf->path);
1074
        dir_print_dirof(buf->path, &availrows, req.flags & DIR_FLAG_PAUSE);
1075
        nls_outputnl_doserr(2); /* "File not found" */
1076
        goto GAMEOVER;
1077
      } else {
1078
        outputnl("");
1079
        if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
1080
        nls_outputnl(37,25); /* Total files listed: */
1081
        if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
1082
        dir_print_summary_files(buf->buff64, uint32maxlen, summary_recurs_totsz, summary_recurs_fcount, &availrows, req.flags, &(buf->nls));
1083
      }
2201 mateusz.vi 1084
    }
2200 mateusz.vi 1085
    /* xxxx bytes free */
1086
    i = cmd_dir_df(&summary_totsz, drv);
1087
    if (i != 0) nls_outputnl_doserr(i);
2218 mateusz.vi 1088
    sv_memset(buf->buff64, ' ', summary_alignpos + 8 + uint32maxlen); /* align the freebytes value to same column as totbytes */
2200 mateusz.vi 1089
    i = nls_format_number(buf->buff64 + summary_alignpos + 8 + uint32maxlen, summary_totsz, &(buf->nls));
1090
    output(buf->buff64 + i + 1);
1091
    output(" ");
1092
    nls_outputnl(37,24); /* "bytes free" */
1093
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
1094
  }
1095
 
2205 mateusz.vi 1096
  GAMEOVER:
1097
 
1716 mateusz.vi 1098
  /* free the buffer memory (if used) */
2230 mateusz.vi 1099
  if (glob_sortcmp_dat.dtabuf_root != NULL) cmd_dir_farfree(glob_sortcmp_dat.dtabuf_root);
1716 mateusz.vi 1100
 
533 mateuszvis 1101
  return(CMD_OK);
368 mateuszvis 1102
}