Subversion Repositories SvarDOS

Rev

Rev 421 | Rev 425 | Go to most recent revision | 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
 *
4
 * Copyright (C) 2021 Mateusz Viste
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
46
 */
47
 
396 mateuszvis 48
/* NOTE: /A attributes are matched in an exclusive way, ie. only files with
49
 *       the specified attributes are matched. This is different from how DOS
50
 *       itself matches attributes hence DIR cannot rely on the attributes
51
 *       filter within FindFirst.
52
 *
53
 * NOTE: Multiple /A are not supported - only the last one is significant.
54
 */
55
 
420 mateuszvis 56
#define WCOLWIDTH 15  /* width of a column in wide mode output */
57
 
424 mateuszvis 58
 
59
/* fills freebytes with free bytes for drv (A=0, B=1, etc)
60
 * returns DOS ERR code on failure */
61
static unsigned short cmd_dir_df(unsigned long *freebytes, unsigned char drv) {
62
  unsigned short res = 0;
63
  unsigned short sects_per_clust = 0, avail_clusts = 0, bytes_per_sect = 0;
64
 
65
  _asm {
66
    push ax
67
    push bx
68
    push cx
69
    push dx
70
 
71
    mov ah, 0x36  /* DOS 2+ -- Get Disk Free Space */
72
    mov dl, [drv] /* A=1, B=2, etc (0 = DEFAULT DRIVE) */
73
    inc dl
74
    int 0x21      /* AX=sects_per_clust, BX=avail_clusts, CX=bytes_per_sect, DX=tot_clusters */
75
    cmp ax, 0xffff /* AX=0xffff on error (invalid drive) */
76
    jne COMPUTEDF
77
    mov [res], 0x0f /* fill res with DOS error code 15 ("invalid drive") */
78
    jmp DONE
79
 
80
    COMPUTEDF:
81
    /* freebytes = AX * BX * CX */
82
    mov [sects_per_clust], ax
83
    mov [avail_clusts], bx
84
    mov [bytes_per_sect], cx
85
 
86
    DONE:
87
    pop dx
88
    pop cx
89
    pop bx
90
    pop ax
91
  }
92
 
93
  /* multiple steps to avoid uint16 overflow */
94
  *freebytes = sects_per_clust;
95
  *freebytes *= avail_clusts;
96
  *freebytes *= bytes_per_sect;
97
 
98
  return(res);
99
}
100
 
101
 
372 mateuszvis 102
static int cmd_dir(struct cmd_funcparam *p) {
393 mateuszvis 103
  const char *filespecptr = NULL;
388 mateuszvis 104
  struct DTA *dta = (void *)0x80; /* set DTA to its default location at 80h in PSP */
417 mateuszvis 105
  unsigned short i;
396 mateuszvis 106
  unsigned short availrows;  /* counter of available rows on display (used for /P) */
420 mateuszvis 107
  unsigned short wcols = screen_getwidth() / WCOLWIDTH; /* number of columns in wide mode */
108
  unsigned char wcolcount;
109
  struct nls_patterns *nls = (void *)(p->BUFFER + (BUFFER_SIZE / 3));
110
  char *buff2 = p->BUFFER + (BUFFER_SIZE / 3 * 2);
424 mateuszvis 111
  unsigned long summary_fcount = 0;
112
  unsigned long summary_totsz = 0;
113
  unsigned char drv = 0;
420 mateuszvis 114
 
396 mateuszvis 115
  #define DIR_FLAG_PAUSE  1
116
  #define DIR_FLAG_RECUR  4
420 mateuszvis 117
  #define DIR_FLAG_LCASE  8
396 mateuszvis 118
  unsigned char flags = 0;
368 mateuszvis 119
 
420 mateuszvis 120
  #define DIR_OUTPUT_NORM 1
121
  #define DIR_OUTPUT_WIDE 2
122
  #define DIR_OUTPUT_BARE 3
123
  unsigned char format = DIR_OUTPUT_NORM;
124
 
390 mateuszvis 125
  if (cmd_ishlp(p)) {
396 mateuszvis 126
    outputnl("Displays a list of files and subdirectories in a directory");
127
    outputnl("");
128
    outputnl("DIR [drive:][path][filename] [/P] [/W] [/A[:]attributes] [/O[[:]sortorder]] [/S] [/B] [/L]");
129
    outputnl("");
130
    outputnl("/P Pauses after each screenful of information");
131
    outputnl("/W Uses wide list format");
132
    outputnl("");
133
    outputnl("/A Displays files with specified attributes:");
134
    outputnl("    D Directories            R Read-only files        H Hidden files");
135
    outputnl("    A Ready for archiving    S System files           - prefix meaning \"not\"");
136
    outputnl("");
137
    outputnl("/O List files in sorted order:");
138
    outputnl("    N by name                S by size                E by extension");
139
    outputnl("    D by date                G group dirs first       - prefix to reverse order");
140
    outputnl("");
141
    outputnl("/S Displays files in specified directory and all subdirectories");
142
    outputnl("/B Uses bare format (no heading information or summary)");
143
    outputnl("/L Uses lowercases");
390 mateuszvis 144
    return(-1);
145
  }
146
 
420 mateuszvis 147
  i = nls_getpatterns(nls);
148
  if (i != 0) outputnl(doserr(i));
149
 
393 mateuszvis 150
  /* parse command line */
151
  for (i = 0; i < p->argc; i++) {
152
    if (p->argv[i][0] == '/') {
396 mateuszvis 153
      char arg;
154
      char neg = 0;
155
      /* detect negations and get actual argument */
156
      if (p->argv[i][1] == '-') neg = 1;
157
      arg = p->argv[i][1 + neg];
158
      /* */
159
      switch (arg) {
160
        case 'a':
161
        case 'A':
162
          /* TODO */
163
          outputnl("/A NOT IMPLEMENTED YET");
164
          return(-1);
165
          break;
399 mateuszvis 166
        case 'b':
167
        case 'B':
420 mateuszvis 168
          format = DIR_OUTPUT_BARE;
399 mateuszvis 169
          break;
421 mateuszvis 170
        case 'l':
171
        case 'L':
172
          flags |= DIR_FLAG_LCASE;
420 mateuszvis 173
          break;
421 mateuszvis 174
        case 'o':
175
        case 'O':
176
          /* TODO */
177
          outputnl("/O NOT IMPLEMENTED YET");
178
          return(-1);
179
          break;
396 mateuszvis 180
        case 'p':
181
        case 'P':
182
          flags |= DIR_FLAG_PAUSE;
183
          if (neg) flags &= (0xff ^ DIR_FLAG_PAUSE);
184
          break;
421 mateuszvis 185
        case 's':
186
        case 'S':
187
          /* TODO */
188
          outputnl("/S NOT IMPLEMENTED YET");
189
          return(-1);
420 mateuszvis 190
          break;
421 mateuszvis 191
        case 'w':
192
        case 'W':
193
          format = DIR_OUTPUT_WIDE;
194
          break;
393 mateuszvis 195
        default:
196
          outputnl("Invalid switch");
197
          return(-1);
198
      }
199
    } else {  /* filespec */
200
      if (filespecptr != NULL) {
201
        outputnl("Too many parameters");
202
        return(-1);
203
      }
204
      filespecptr = p->argv[i];
205
    }
206
  }
368 mateuszvis 207
 
393 mateuszvis 208
  if (filespecptr == NULL) filespecptr = ".";
209
 
417 mateuszvis 210
  /* special case: "DIR drive:" (truename() fails on "C:" under MS-DOS 6.0) */
211
  if ((filespecptr[0] != 0) && (filespecptr[1] == ':') && (filespecptr[2] == 0)) {
212
    if ((filespecptr[0] >= 'a') && (filespecptr[0] <= 'z')) {
213
      p->BUFFER[0] = filespecptr[0] - ('a' - 1);
214
    } else {
215
      p->BUFFER[0] = filespecptr[0] - ('A' - 1);
399 mateuszvis 216
    }
417 mateuszvis 217
    i = curpathfordrv(p->BUFFER, p->BUFFER[0]);
218
  } else {
219
    i = file_truename(filespecptr, p->BUFFER);
399 mateuszvis 220
  }
417 mateuszvis 221
  if (i != 0) {
222
    outputnl(doserr(i));
223
    return(-1);
224
  }
393 mateuszvis 225
 
420 mateuszvis 226
  if (format != DIR_OUTPUT_BARE) {
424 mateuszvis 227
    drv = p->BUFFER[0];
399 mateuszvis 228
    if (drv >= 'a') {
229
      drv -= 'a';
230
    } else {
231
      drv -= 'A';
232
    }
403 mateuszvis 233
    cmd_vol_internal(drv, buff2);
234
    sprintf(buff2, "Directory of %s", p->BUFFER);
399 mateuszvis 235
    /* trim at first '?', if any */
403 mateuszvis 236
    for (i = 0; buff2[i] != 0; i++) if (buff2[i] == '?') buff2[i] = 0;
237
    outputnl(buff2);
399 mateuszvis 238
    outputnl("");
239
  }
240
 
417 mateuszvis 241
  /* if dir: append a backslash (also get its len) */
242
  i = path_appendbkslash_if_dir(p->BUFFER);
393 mateuszvis 243
 
417 mateuszvis 244
  /* if ends with a \ then append ????????.??? */
245
  if (p->BUFFER[i - 1] == '\\') strcat(p->BUFFER, "????????.???");
393 mateuszvis 246
 
417 mateuszvis 247
  i = findfirst(dta, p->BUFFER, DOS_ATTR_RO | DOS_ATTR_HID | DOS_ATTR_SYS | DOS_ATTR_DIR | DOS_ATTR_ARC);
248
  if (i != 0) {
249
    outputnl(doserr(i));
250
    return(-1);
251
  }
252
 
396 mateuszvis 253
  availrows = screen_getheight();
420 mateuszvis 254
  wcolcount = 0; /* may be used for columns counting with wide mode */
396 mateuszvis 255
 
420 mateuszvis 256
  do {
257
    if (flags & DIR_FLAG_LCASE) _strlwr(dta->fname); /* OpenWatcom extension, probably does not care about NLS so results may be odd with non-A-Z characters... */
368 mateuszvis 258
 
424 mateuszvis 259
    summary_fcount++;
260
    if ((dta->attr & DOS_ATTR_DIR) == 0) summary_totsz += dta->size;
261
 
420 mateuszvis 262
    switch (format) {
263
      case DIR_OUTPUT_NORM:
264
        /* print fname-space-extension (unless it's "." or "..", then print as-is) */
265
        if (dta->fname[0] == '.') {
266
          output(dta->fname);
267
          i = strlen(dta->fname);
268
          while (i++ < 12) output(" ");
269
        } else {
270
          file_fname2fcb(buff2, dta->fname);
271
          memmove(buff2 + 9, buff2 + 8, 4);
272
          buff2[8] = ' ';
273
          output(buff2);
274
        }
275
        output(" ");
276
        /* either <DIR> or right aligned 10-chars byte size */
277
        memset(buff2, ' ', 10);
278
        if (dta->attr & DOS_ATTR_DIR) {
279
          strcpy(buff2 + 10, "<DIR>");
280
        } else {
281
          _ultoa(dta->size, buff2 + 10, 10); /* OpenWatcom extension */
282
        }
283
        output(buff2 + strlen(buff2) - 10);
284
        /* two spaces and NLS DATE */
285
        buff2[0] = ' ';
286
        buff2[1] = ' ';
287
        nls_format_date(buff2 + 2, dta->date_yr + 1980, dta->date_mo, dta->date_dy, nls);
288
        output(buff2);
289
 
290
        /* one space and NLS TIME */
291
        nls_format_time(buff2 + 1, dta->time_hour, dta->time_min, nls);
292
        outputnl(buff2);
293
        break;
294
 
295
      case DIR_OUTPUT_WIDE: /* display in columns of 12 chars per item */
296
        i = strlen(dta->fname);
297
        if (dta->attr & DOS_ATTR_DIR) {
298
          i += 2;
299
          output("[");
300
          output(dta->fname);
301
          output("]");
302
        } else {
303
          output(dta->fname);
304
        }
305
        while (i++ < WCOLWIDTH) output(" ");
306
        if (++wcolcount == wcols) {
307
          wcolcount = 0;
308
          outputnl("");
309
        }
310
        break;
311
 
312
      case DIR_OUTPUT_BARE:
313
        outputnl(dta->fname);
314
        break;
396 mateuszvis 315
    }
368 mateuszvis 316
 
420 mateuszvis 317
    if ((flags & DIR_FLAG_PAUSE) && (--availrows < 2)) {
318
      press_any_key();
319
      availrows = screen_getheight();
320
    }
321
 
322
  } while (findnext(dta) == 0);
323
 
424 mateuszvis 324
  if (wcolcount != 0) outputnl(""); /* in wide mode make sure to end on a clear row */
420 mateuszvis 325
 
424 mateuszvis 326
  /* print out summary (unless bare output mode) */
327
  if (format != DIR_OUTPUT_BARE) {
328
    unsigned short alignpos;
329
    /* x file(s) */
330
    memset(buff2, ' ', 13); /* 13 is the max len of a 32 bit number with thousand separators (4'000'000'000) */
331
    i = nls_format_number(buff2 + 13, summary_fcount, nls);
332
    alignpos = sprintf(buff2 + 13 + i, " %s ", "file(s)");
333
    output(buff2 + i);
334
    /* xxxx bytes */
335
    i = nls_format_number(buff2 + 13, summary_totsz, nls);
336
    output(buff2 + i);
337
    output(" ");
338
    outputnl("bytes");
339
    /* xxxx bytes free */
340
    printf("totsz = %lu\r\n", summary_totsz);
341
    i = cmd_dir_df(&summary_totsz, drv);
342
    if (i != 0) outputnl(doserr(i));
343
    alignpos += 13 + 13;
344
    memset(buff2, ' ', alignpos); /* align the freebytes value to same column as totbytes */
345
    i = nls_format_number(buff2 + alignpos, summary_totsz, nls);
346
    output(buff2 + i);
347
    output(" ");
348
    outputnl("bytes free");
349
  }
350
 
368 mateuszvis 351
  return(-1);
352
}