Subversion Repositories SvarDOS

Rev

Rev 1141 | Rev 1716 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1141 Rev 1142
1
/* This file is part of the SvarCOM project and is published under the terms
1
/* This file is part of the SvarCOM project and is published under the terms
2
 * of the MIT license.
2
 * of the MIT license.
3
 *
3
 *
4
 * Copyright (C) 2021-2022 Mateusz Viste
4
 * Copyright (C) 2021-2022 Mateusz Viste
5
 *
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a
6
 * Permission is hereby granted, free of charge, to any person obtaining a
7
 * copy of this software and associated documentation files (the "Software"),
7
 * copy of this software and associated documentation files (the "Software"),
8
 * to deal in the Software without restriction, including without limitation
8
 * to deal in the Software without restriction, including without limitation
9
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
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
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:
11
 * Software is furnished to do so, subject to the following conditions:
12
 *
12
 *
13
 * The above copyright notice and this permission notice shall be included in
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
14
 * all copies or substantial portions of the Software.
15
 *
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
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,
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
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
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
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
21
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
 * DEALINGS IN THE SOFTWARE.
22
 * DEALINGS IN THE SOFTWARE.
23
 */
23
 */
24
 
24
 
25
/*
25
/*
26
 * dir
26
 * dir
27
 *
27
 *
28
 * Displays a list of files and subdirectories in a directory.
28
 * Displays a list of files and subdirectories in a directory.
29
 *
29
 *
30
 * DIR [drive:][path][filename] [/P] [/W] [/A[:]attributes] [/O[[:]sortorder]] [/S] [/B] [/L]
30
 * DIR [drive:][path][filename] [/P] [/W] [/A[:]attributes] [/O[[:]sortorder]] [/S] [/B] [/L]
31
 *
31
 *
32
 * /P Pauses after each screenful of information.
32
 * /P Pauses after each screenful of information.
33
 * /W Uses wide list format.
33
 * /W Uses wide list format.
34
 *
34
 *
35
 * /A Displays file with specified attributes:
35
 * /A Displays file with specified attributes:
36
 *     D Directories           R Read-only files     H Hidden files
36
 *     D Directories           R Read-only files     H Hidden files
37
 *     A Ready for archiving   S System files        - prefix meaning "not"
37
 *     A Ready for archiving   S System files        - prefix meaning "not"
38
 *
38
 *
39
 * /O List files in sorted order:
39
 * /O List files in sorted order:
40
 *     N by name            S by size              E by extension
40
 *     N by name            S by size              E by extension
41
 *     D by date            G group dirs first     - prefix to reverse order
41
 *     D by date            G group dirs first     - prefix to reverse order
42
 *
42
 *
43
 * /S Displays files in specified directory and all subdirectories.
43
 * /S Displays files in specified directory and all subdirectories.
44
 * /B Uses bare format (no heading information or summary)
44
 * /B Uses bare format (no heading information or summary)
45
 * /L Uses lowercases
45
 * /L Uses lowercases
46
 */
46
 */
47
 
47
 
48
/* NOTE: /A attributes are matched in an exclusive way, ie. only files with
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
49
 *       the specified attributes are matched. This is different from how DOS
50
 *       itself matches attributes hence DIR cannot rely on the attributes
50
 *       itself matches attributes hence DIR cannot rely on the attributes
51
 *       filter within FindFirst.
51
 *       filter within FindFirst.
52
 *
52
 *
53
 * NOTE: Multiple /A are not supported - only the last one is significant.
53
 * NOTE: Multiple /A are not supported - only the last one is significant.
54
 */
54
 */
55
 
55
 
56
#define WCOLWIDTH 15  /* width of a column in wide mode output */
56
#define WCOLWIDTH 15  /* width of a column in wide mode output */
57
 
57
 
58
 
58
 
59
/* fills freebytes with free bytes for drv (A=0, B=1, etc)
59
/* fills freebytes with free bytes for drv (A=0, B=1, etc)
60
 * returns DOS ERR code on failure */
60
 * returns DOS ERR code on failure */
61
static unsigned short cmd_dir_df(unsigned long *freebytes, unsigned char drv) {
61
static unsigned short cmd_dir_df(unsigned long *freebytes, unsigned char drv) {
62
  unsigned short res = 0;
62
  unsigned short res = 0;
63
  unsigned short sects_per_clust = 0, avail_clusts = 0, bytes_per_sect = 0;
63
  unsigned short sects_per_clust = 0, avail_clusts = 0, bytes_per_sect = 0;
64
 
64
 
65
  _asm {
65
  _asm {
66
    push ax
66
    push ax
67
    push bx
67
    push bx
68
    push cx
68
    push cx
69
    push dx
69
    push dx
70
 
70
 
71
    mov ah, 0x36  /* DOS 2+ -- Get Disk Free Space */
71
    mov ah, 0x36  /* DOS 2+ -- Get Disk Free Space */
72
    mov dl, [drv] /* A=1, B=2, etc (0 = DEFAULT DRIVE) */
72
    mov dl, [drv] /* A=1, B=2, etc (0 = DEFAULT DRIVE) */
73
    inc dl
73
    inc dl
74
    int 0x21      /* AX=sects_per_clust, BX=avail_clusts, CX=bytes_per_sect, DX=tot_clusters */
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) */
75
    cmp ax, 0xffff /* AX=0xffff on error (invalid drive) */
76
    jne COMPUTEDF
76
    jne COMPUTEDF
77
    mov [res], 0x0f /* fill res with DOS error code 15 ("invalid drive") */
77
    mov [res], 0x0f /* fill res with DOS error code 15 ("invalid drive") */
78
    jmp DONE
78
    jmp DONE
79
 
79
 
80
    COMPUTEDF:
80
    COMPUTEDF:
81
    /* freebytes = AX * BX * CX */
81
    /* freebytes = AX * BX * CX */
82
    mov [sects_per_clust], ax
82
    mov [sects_per_clust], ax
83
    mov [avail_clusts], bx
83
    mov [avail_clusts], bx
84
    mov [bytes_per_sect], cx
84
    mov [bytes_per_sect], cx
85
 
85
 
86
    DONE:
86
    DONE:
87
    pop dx
87
    pop dx
88
    pop cx
88
    pop cx
89
    pop bx
89
    pop bx
90
    pop ax
90
    pop ax
91
  }
91
  }
92
 
92
 
93
  /* multiple steps to avoid uint16 overflow */
93
  /* multiple steps to avoid uint16 overflow */
94
  *freebytes = sects_per_clust;
94
  *freebytes = sects_per_clust;
95
  *freebytes *= avail_clusts;
95
  *freebytes *= avail_clusts;
96
  *freebytes *= bytes_per_sect;
96
  *freebytes *= bytes_per_sect;
97
 
97
 
98
  return(res);
98
  return(res);
99
}
99
}
100
 
100
 
101
 
101
 
102
static void dir_pagination(unsigned short *availrows) {
102
static void dir_pagination(unsigned short *availrows) {
103
  *availrows -= 1;
103
  *availrows -= 1;
104
  if (*availrows == 0) {
104
  if (*availrows == 0) {
105
    press_any_key();
105
    press_any_key();
106
    *availrows = screen_getheight() - 1;
106
    *availrows = screen_getheight() - 1;
107
  }
107
  }
108
}
108
}
109
 
109
 
110
 
110
 
111
/* parse an attr list like "Ar-hS" and fill bitfield into attrfilter_may and attrfilter_must.
111
/* parse an attr list like "Ar-hS" and fill bitfield into attrfilter_may and attrfilter_must.
112
 * /AHS   -> adds S and H to mandatory attribs ("must")
112
 * /AHS   -> adds S and H to mandatory attribs ("must")
113
 * /A-S   -> removes S from allowed attribs ("may")
113
 * /A-S   -> removes S from allowed attribs ("may")
114
 * returns non-zero on error. */
114
 * returns non-zero on error. */
115
static int dir_parse_attr_list(const char *arg, unsigned char *attrfilter_may, unsigned char *attrfilter_must) {
115
static int dir_parse_attr_list(const char *arg, unsigned char *attrfilter_may, unsigned char *attrfilter_must) {
116
  for (; *arg != 0; arg++) {
116
  for (; *arg != 0; arg++) {
117
    unsigned char curattr;
117
    unsigned char curattr;
118
    char not;
118
    char not;
119
    if (*arg == '-') {
119
    if (*arg == '-') {
120
      not = 1;
120
      not = 1;
121
      arg++;
121
      arg++;
122
    } else {
122
    } else {
123
      not = 0;
123
      not = 0;
124
    }
124
    }
125
    switch (*arg) {
125
    switch (*arg) {
126
      case 'd':
126
      case 'd':
127
      case 'D':
127
      case 'D':
128
        curattr = DOS_ATTR_DIR;
128
        curattr = DOS_ATTR_DIR;
129
        break;
129
        break;
130
      case 'r':
130
      case 'r':
131
      case 'R':
131
      case 'R':
132
        curattr = DOS_ATTR_RO;
132
        curattr = DOS_ATTR_RO;
133
        break;
133
        break;
134
      case 'a':
134
      case 'a':
135
      case 'A':
135
      case 'A':
136
        curattr = DOS_ATTR_ARC;
136
        curattr = DOS_ATTR_ARC;
137
        break;
137
        break;
138
      case 'h':
138
      case 'h':
139
      case 'H':
139
      case 'H':
140
        curattr = DOS_ATTR_HID;
140
        curattr = DOS_ATTR_HID;
141
        break;
141
        break;
142
      case 's':
142
      case 's':
143
      case 'S':
143
      case 'S':
144
        curattr = DOS_ATTR_SYS;
144
        curattr = DOS_ATTR_SYS;
145
        break;
145
        break;
146
      default:
146
      default:
147
        return(-1);
147
        return(-1);
148
    }
148
    }
149
    /* update res bitfield */
149
    /* update res bitfield */
150
    if (not) {
150
    if (not) {
151
      *attrfilter_may &= ~curattr;
151
      *attrfilter_may &= ~curattr;
152
    } else {
152
    } else {
153
      *attrfilter_must |= curattr;
153
      *attrfilter_must |= curattr;
154
    }
154
    }
155
  }
155
  }
156
  return(0);
156
  return(0);
157
}
157
}
158
 
158
 
159
 
159
 
160
#define DIR_ATTR_DEFAULT (DOS_ATTR_RO | DOS_ATTR_DIR | DOS_ATTR_ARC)
160
#define DIR_ATTR_DEFAULT (DOS_ATTR_RO | DOS_ATTR_DIR | DOS_ATTR_ARC)
161
 
161
 
162
static enum cmd_result cmd_dir(struct cmd_funcparam *p) {
162
static enum cmd_result cmd_dir(struct cmd_funcparam *p) {
163
  const char *filespecptr = NULL;
163
  const char *filespecptr = NULL;
164
  struct DTA *dta = (void *)0x80; /* set DTA to its default location at 80h in PSP */
164
  struct DTA *dta = (void *)0x80; /* set DTA to its default location at 80h in PSP */
165
  unsigned short i;
165
  unsigned short i;
166
  unsigned short availrows;  /* counter of available rows on display (used for /P) */
166
  unsigned short availrows;  /* counter of available rows on display (used for /P) */
167
  unsigned short screenw = screen_getwidth();
167
  unsigned short screenw = screen_getwidth();
168
  unsigned short wcols = screenw / WCOLWIDTH; /* number of columns in wide mode */
168
  unsigned short wcols = screenw / WCOLWIDTH; /* number of columns in wide mode */
169
  unsigned char wcolcount;
169
  unsigned char wcolcount;
170
  struct nls_patterns *nls = (void *)(p->BUFFER + (p->BUFFERSZ / 2));
170
  struct nls_patterns *nls = (void *)(p->BUFFER + (p->BUFFERSZ / 2));
171
  char *buff2 = p->BUFFER + (p->BUFFERSZ / 2) + sizeof(*nls);
171
  char *buff2 = p->BUFFER + (p->BUFFERSZ / 2) + sizeof(*nls);
172
  unsigned long summary_fcount = 0;
172
  unsigned long summary_fcount = 0;
173
  unsigned long summary_totsz = 0;
173
  unsigned long summary_totsz = 0;
174
  unsigned char drv = 0;
174
  unsigned char drv = 0;
175
  unsigned char attrfilter_may = DIR_ATTR_DEFAULT;
175
  unsigned char attrfilter_may = DIR_ATTR_DEFAULT;
176
  unsigned char attrfilter_must = 0;
176
  unsigned char attrfilter_must = 0;
177
 
177
 
178
  #define DIR_FLAG_PAUSE  1
178
  #define DIR_FLAG_PAUSE  1
179
  #define DIR_FLAG_RECUR  4
179
  #define DIR_FLAG_RECUR  4
180
  #define DIR_FLAG_LCASE  8
180
  #define DIR_FLAG_LCASE  8
181
  unsigned char flags = 0;
181
  unsigned char flags = 0;
182
 
182
 
183
  #define DIR_OUTPUT_NORM 1
183
  #define DIR_OUTPUT_NORM 1
184
  #define DIR_OUTPUT_WIDE 2
184
  #define DIR_OUTPUT_WIDE 2
185
  #define DIR_OUTPUT_BARE 3
185
  #define DIR_OUTPUT_BARE 3
186
  unsigned char format = DIR_OUTPUT_NORM;
186
  unsigned char format = DIR_OUTPUT_NORM;
187
 
187
 
188
  if (cmd_ishlp(p)) {
188
  if (cmd_ishlp(p)) {
189
    nls_outputnl(37,0); /* "Displays a list of files and subdirectories in a directory" */
189
    nls_outputnl(37,0); /* "Displays a list of files and subdirectories in a directory" */
190
    outputnl("");
190
    outputnl("");
191
    nls_outputnl(37,1); /* "DIR [drive:][path][filename] [/P] [/W] [/A[:]attributes] [/O[[:]sortorder]] [/S] [/B] [/L]" */
191
    nls_outputnl(37,1); /* "DIR [drive:][path][filename] [/P] [/W] [/A[:]attributes] [/O[[:]sortorder]] [/S] [/B] [/L]" */
192
    outputnl("");
192
    outputnl("");
193
    nls_outputnl(37,2); /* "/P Pauses after each screenful of information" */
193
    nls_outputnl(37,2); /* "/P Pauses after each screenful of information" */
194
    nls_outputnl(37,3); /* "/W Uses wide list format" */
194
    nls_outputnl(37,3); /* "/W Uses wide list format" */
195
    outputnl("");
195
    outputnl("");
196
    nls_outputnl(37,4); /* "/A Displays files with specified attributes:" */
196
    nls_outputnl(37,4); /* "/A Displays files with specified attributes:" */
197
    nls_outputnl(37,5); /* "    D Directories            R Read-only files        H Hidden files" */
197
    nls_outputnl(37,5); /* "    D Directories            R Read-only files        H Hidden files" */
198
    nls_outputnl(37,6); /* "    A Ready for archiving    S System files           - prefix meaning "not"" */
198
    nls_outputnl(37,6); /* "    A Ready for archiving    S System files           - prefix meaning "not"" */
199
    outputnl("");
199
    outputnl("");
200
    nls_outputnl(37,7); /* "/O List files in sorted order:" */
200
    nls_outputnl(37,7); /* "/O List files in sorted order:" */
201
    nls_outputnl(37,8); /* "    N by name                S by size                E by extension" */
201
    nls_outputnl(37,8); /* "    N by name                S by size                E by extension" */
202
    nls_outputnl(37,9); /* "    D by date                G group dirs first       - prefix to reverse order" */
202
    nls_outputnl(37,9); /* "    D by date                G group dirs first       - prefix to reverse order" */
203
    outputnl("");
203
    outputnl("");
204
    nls_outputnl(37,10); /* "/S Displays files in specified directory and all subdirectories" */
204
    nls_outputnl(37,10); /* "/S Displays files in specified directory and all subdirectories" */
205
    nls_outputnl(37,11); /* "/B Uses bare format (no heading information or summary)" */
205
    nls_outputnl(37,11); /* "/B Uses bare format (no heading information or summary)" */
206
    nls_outputnl(37,12); /* "/L Uses lowercases" */
206
    nls_outputnl(37,12); /* "/L Uses lowercases" */
207
    return(CMD_OK);
207
    return(CMD_OK);
208
  }
208
  }
209
 
209
 
210
  i = nls_getpatterns(nls);
210
  i = nls_getpatterns(nls);
211
  if (i != 0) nls_outputnl_doserr(i);
211
  if (i != 0) nls_outputnl_doserr(i);
212
 
212
 
213
  /* disable usage of thousands separator on narrow screens */
213
  /* disable usage of thousands separator on narrow screens */
214
  if (screenw < 80) nls->thousep[0] = 0;
214
  if (screenw < 80) nls->thousep[0] = 0;
215
 
215
 
216
  /* parse command line */
216
  /* parse command line */
217
  for (i = 0; i < p->argc; i++) {
217
  for (i = 0; i < p->argc; i++) {
218
    if (p->argv[i][0] == '/') {
218
    if (p->argv[i][0] == '/') {
219
      const char *arg = p->argv[i] + 1;
219
      const char *arg = p->argv[i] + 1;
220
      char neg = 0;
220
      char neg = 0;
221
      /* detect negations and get actual argument */
221
      /* detect negations and get actual argument */
222
      if (*arg == '-') {
222
      if (*arg == '-') {
223
        neg = 1;
223
        neg = 1;
224
        arg++;
224
        arg++;
225
      }
225
      }
226
      /* */
226
      /* */
227
      switch (*arg) {
227
      switch (*arg) {
228
        case 'a':
228
        case 'a':
229
        case 'A':
229
        case 'A':
230
          arg++;
230
          arg++;
231
          /* preset defaults */
231
          /* preset defaults */
232
          attrfilter_may = DIR_ATTR_DEFAULT;
232
          attrfilter_may = DIR_ATTR_DEFAULT;
233
          attrfilter_must = 0;
233
          attrfilter_must = 0;
234
          /* /-A only allowed without further parameters (used to cancel possible previous /Asmth) */
234
          /* /-A only allowed without further parameters (used to cancel possible previous /Asmth) */
235
          if (neg) {
235
          if (neg) {
236
            if (*arg != 0) {
236
            if (*arg != 0) {
237
              nls_outputnl_err(0, 2); /* invalid switch */
237
              nls_outputnl_err(0, 2); /* invalid switch */
238
              return(CMD_FAIL);
238
              return(CMD_FAIL);
239
            }
239
            }
240
          } else {
240
          } else {
241
            /* skip colon if present */
241
            /* skip colon if present */
242
            if (*arg == ':') arg++;
242
            if (*arg == ':') arg++;
243
            /* start with "allow everything" */
243
            /* start with "allow everything" */
244
            attrfilter_may = (DOS_ATTR_ARC | DOS_ATTR_DIR | DOS_ATTR_HID | DOS_ATTR_SYS | DOS_ATTR_RO);
244
            attrfilter_may = (DOS_ATTR_ARC | DOS_ATTR_DIR | DOS_ATTR_HID | DOS_ATTR_SYS | DOS_ATTR_RO);
245
            if (dir_parse_attr_list(arg, &attrfilter_may, &attrfilter_must) != 0) {
245
            if (dir_parse_attr_list(arg, &attrfilter_may, &attrfilter_must) != 0) {
246
              nls_outputnl_err(0, 3); /* invalid parameter format */
246
              nls_outputnl_err(0, 3); /* invalid parameter format */
247
              return(CMD_FAIL);
247
              return(CMD_FAIL);
248
            }
248
            }
249
          }
249
          }
250
          break;
250
          break;
251
        case 'b':
251
        case 'b':
252
        case 'B':
252
        case 'B':
253
          format = DIR_OUTPUT_BARE;
253
          format = DIR_OUTPUT_BARE;
254
          break;
254
          break;
255
        case 'l':
255
        case 'l':
256
        case 'L':
256
        case 'L':
257
          flags |= DIR_FLAG_LCASE;
257
          flags |= DIR_FLAG_LCASE;
258
          break;
258
          break;
259
        case 'o':
259
        case 'o':
260
        case 'O':
260
        case 'O':
261
          /* TODO */
261
          /* TODO */
262
          outputnl("/O NOT IMPLEMENTED YET");
262
          outputnl("/O NOT IMPLEMENTED YET");
263
          return(CMD_FAIL);
263
          return(CMD_FAIL);
264
          break;
264
          break;
265
        case 'p':
265
        case 'p':
266
        case 'P':
266
        case 'P':
267
          flags |= DIR_FLAG_PAUSE;
267
          flags |= DIR_FLAG_PAUSE;
268
          if (neg) flags &= (0xff ^ DIR_FLAG_PAUSE);
268
          if (neg) flags &= (0xff ^ DIR_FLAG_PAUSE);
269
          break;
269
          break;
270
        case 's':
270
        case 's':
271
        case 'S':
271
        case 'S':
272
          /* TODO */
272
          /* TODO */
273
          outputnl("/S NOT IMPLEMENTED YET");
273
          outputnl("/S NOT IMPLEMENTED YET");
274
          return(CMD_FAIL);
274
          return(CMD_FAIL);
275
          break;
275
          break;
276
        case 'w':
276
        case 'w':
277
        case 'W':
277
        case 'W':
278
          format = DIR_OUTPUT_WIDE;
278
          format = DIR_OUTPUT_WIDE;
279
          break;
279
          break;
280
        default:
280
        default:
281
          nls_outputnl_err(0, 2); /* invalid switch */
281
          nls_outputnl_err(0, 2); /* invalid switch */
282
          return(CMD_FAIL);
282
          return(CMD_FAIL);
283
      }
283
      }
284
    } else {  /* filespec */
284
    } else {  /* filespec */
285
      if (filespecptr != NULL) {
285
      if (filespecptr != NULL) {
286
        nls_outputnl_err(0, 4); /* too many parameters */
286
        nls_outputnl_err(0, 4); /* too many parameters */
287
        return(CMD_FAIL);
287
        return(CMD_FAIL);
288
      }
288
      }
289
      filespecptr = p->argv[i];
289
      filespecptr = p->argv[i];
290
    }
290
    }
291
  }
291
  }
292
 
292
 
293
  if (filespecptr == NULL) filespecptr = ".";
293
  if (filespecptr == NULL) filespecptr = ".";
294
 
294
 
295
  availrows = screen_getheight() - 2;
295
  availrows = screen_getheight() - 2;
296
 
296
 
297
  /* special case: "DIR drive:" (truename() fails on "C:" under MS-DOS 6.0) */
297
  /* special case: "DIR drive:" (truename() fails on "C:" under MS-DOS 6.0) */
298
  if ((filespecptr[0] != 0) && (filespecptr[1] == ':') && (filespecptr[2] == 0)) {
298
  if ((filespecptr[0] != 0) && (filespecptr[1] == ':') && (filespecptr[2] == 0)) {
299
    if ((filespecptr[0] >= 'a') && (filespecptr[0] <= 'z')) {
299
    if ((filespecptr[0] >= 'a') && (filespecptr[0] <= 'z')) {
300
      p->BUFFER[0] = filespecptr[0] - ('a' - 1);
300
      p->BUFFER[0] = filespecptr[0] - ('a' - 1);
301
    } else {
301
    } else {
302
      p->BUFFER[0] = filespecptr[0] - ('A' - 1);
302
      p->BUFFER[0] = filespecptr[0] - ('A' - 1);
303
    }
303
    }
304
    i = curpathfordrv(p->BUFFER, p->BUFFER[0]);
304
    i = curpathfordrv(p->BUFFER, p->BUFFER[0]);
305
  } else {
305
  } else {
306
    i = file_truename(filespecptr, p->BUFFER);
306
    i = file_truename(filespecptr, p->BUFFER);
307
  }
307
  }
308
  if (i != 0) {
308
  if (i != 0) {
309
    nls_outputnl_doserr(i);
309
    nls_outputnl_doserr(i);
310
    return(CMD_FAIL);
310
    return(CMD_FAIL);
311
  }
311
  }
312
 
312
 
313
  if (format != DIR_OUTPUT_BARE) {
313
  if (format != DIR_OUTPUT_BARE) {
314
    drv = p->BUFFER[0];
314
    drv = p->BUFFER[0];
315
    if (drv >= 'a') {
315
    if (drv >= 'a') {
316
      drv -= 'a';
316
      drv -= 'a';
317
    } else {
317
    } else {
318
      drv -= 'A';
318
      drv -= 'A';
319
    }
319
    }
320
    cmd_vol_internal(drv, buff2);
320
    cmd_vol_internal(drv, buff2);
321
    sprintf(buff2, svarlang_str(37,20)/*"Directory of %s"*/, p->BUFFER);
321
    sprintf(buff2, svarlang_str(37,20)/*"Directory of %s"*/, p->BUFFER);
322
    /* trim at first '?', if any */
322
    /* trim at first '?', if any */
323
    for (i = 0; buff2[i] != 0; i++) if (buff2[i] == '?') buff2[i] = 0;
323
    for (i = 0; buff2[i] != 0; i++) if (buff2[i] == '?') buff2[i] = 0;
324
    outputnl(buff2);
324
    outputnl(buff2);
325
    outputnl("");
325
    outputnl("");
326
    availrows -= 3;
326
    availrows -= 3;
327
  }
327
  }
328
 
328
 
329
  /* if dir: append a backslash (also get its len) */
329
  /* if dir: append a backslash (also get its len) */
330
  i = path_appendbkslash_if_dir(p->BUFFER);
330
  i = path_appendbkslash_if_dir(p->BUFFER);
331
 
331
 
332
  /* if ends with a \ then append ????????.??? */
332
  /* if ends with a \ then append ????????.??? */
333
  if (p->BUFFER[i - 1] == '\\') strcat(p->BUFFER, "????????.???");
333
  if (p->BUFFER[i - 1] == '\\') strcat(p->BUFFER, "????????.???");
334
 
334
 
335
  /* ask DOS for list of files, but only with allowed attribs */
335
  /* ask DOS for list of files, but only with allowed attribs */
336
  i = findfirst(dta, p->BUFFER, attrfilter_may);
336
  i = findfirst(dta, p->BUFFER, attrfilter_may);
337
  if (i != 0) {
337
  if (i != 0) {
338
    nls_outputnl_doserr(i);
338
    nls_outputnl_doserr(i);
339
    return(CMD_FAIL);
339
    return(CMD_FAIL);
340
  }
340
  }
341
 
341
 
342
  wcolcount = 0; /* may be used for columns counting with wide mode */
342
  wcolcount = 0; /* may be used for columns counting with wide mode */
343
 
343
 
344
  do {
344
  do {
345
    /* if mandatory attribs are requested, filter them now */
345
    /* if mandatory attribs are requested, filter them now */
346
    if ((attrfilter_must & dta->attr) != attrfilter_must) continue;
346
    if ((attrfilter_must & dta->attr) != attrfilter_must) continue;
347
 
347
 
348
    /* if file contains attributes that are not allowed -> skip */
348
    /* if file contains attributes that are not allowed -> skip */
349
    if ((~attrfilter_may & dta->attr) != 0) continue;
349
    if ((~attrfilter_may & dta->attr) != 0) continue;
350
 
350
 
351
    /* turn string lcase (/L) */
351
    /* turn string lcase (/L) */
352
    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... */
352
    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... */
353
 
353
 
354
    summary_fcount++;
354
    summary_fcount++;
355
    if ((dta->attr & DOS_ATTR_DIR) == 0) summary_totsz += dta->size;
355
    if ((dta->attr & DOS_ATTR_DIR) == 0) summary_totsz += dta->size;
356
 
356
 
357
    switch (format) {
357
    switch (format) {
358
      case DIR_OUTPUT_NORM:
358
      case DIR_OUTPUT_NORM:
359
        /* print fname-space-extension (unless it's "." or "..", then print as-is) */
359
        /* print fname-space-extension (unless it's "." or "..", then print as-is) */
360
        if (dta->fname[0] == '.') {
360
        if (dta->fname[0] == '.') {
361
          output(dta->fname);
361
          output(dta->fname);
362
          i = strlen(dta->fname);
362
          i = strlen(dta->fname);
363
          while (i++ < 12) output(" ");
363
          while (i++ < 12) output(" ");
364
        } else {
364
        } else {
365
          file_fname2fcb(buff2, dta->fname);
365
          file_fname2fcb(buff2, dta->fname);
366
          memmove(buff2 + 9, buff2 + 8, 4);
366
          memmove(buff2 + 9, buff2 + 8, 4);
367
          buff2[8] = ' ';
367
          buff2[8] = ' ';
368
          output(buff2);
368
          output(buff2);
369
        }
369
        }
370
        output(" ");
370
        output(" ");
371
        /* either <DIR> or right aligned 10-chars byte size */
371
        /* either <DIR> or right aligned 10-chars byte size */
372
        memset(buff2, ' ', 10);
372
        memset(buff2, ' ', 10);
373
        if (dta->attr & DOS_ATTR_DIR) {
373
        if (dta->attr & DOS_ATTR_DIR) {
374
          strcpy(buff2 + 10, svarlang_str(37,21));
374
          strcpy(buff2 + 10, svarlang_str(37,21));
375
        } else {
375
        } else {
376
          _ultoa(dta->size, buff2 + 10, 10); /* OpenWatcom extension */
376
          nls_format_number(buff2 + 10, dta->size, nls);
377
        }
377
        }
378
        output(buff2 + strlen(buff2) - 10);
378
        output(buff2 + strlen(buff2) - 10);
379
        /* two spaces and NLS DATE */
379
        /* two spaces and NLS DATE */
380
        buff2[0] = ' ';
380
        buff2[0] = ' ';
381
        buff2[1] = ' ';
381
        buff2[1] = ' ';
382
        if (screenw >= 80) {
382
        if (screenw >= 80) {
383
          nls_format_date(buff2 + 2, dta->date_yr + 1980, dta->date_mo, dta->date_dy, nls);
383
          nls_format_date(buff2 + 2, dta->date_yr + 1980, dta->date_mo, dta->date_dy, nls);
384
        } else {
384
        } else {
385
          nls_format_date(buff2 + 2, (dta->date_yr + 80) % 100, dta->date_mo, dta->date_dy, nls);
385
          nls_format_date(buff2 + 2, (dta->date_yr + 80) % 100, dta->date_mo, dta->date_dy, nls);
386
        }
386
        }
387
        output(buff2);
387
        output(buff2);
388
 
388
 
389
        /* one space and NLS TIME */
389
        /* one space and NLS TIME */
390
        nls_format_time(buff2 + 1, dta->time_hour, dta->time_min, 0xff, nls);
390
        nls_format_time(buff2 + 1, dta->time_hour, dta->time_min, 0xff, nls);
391
        outputnl(buff2);
391
        outputnl(buff2);
392
        break;
392
        break;
393
 
393
 
394
      case DIR_OUTPUT_WIDE: /* display in columns of 12 chars per item */
394
      case DIR_OUTPUT_WIDE: /* display in columns of 12 chars per item */
395
        i = strlen(dta->fname);
395
        i = strlen(dta->fname);
396
        if (dta->attr & DOS_ATTR_DIR) {
396
        if (dta->attr & DOS_ATTR_DIR) {
397
          i += 2;
397
          i += 2;
398
          output("[");
398
          output("[");
399
          output(dta->fname);
399
          output(dta->fname);
400
          output("]");
400
          output("]");
401
        } else {
401
        } else {
402
          output(dta->fname);
402
          output(dta->fname);
403
        }
403
        }
404
        while (i++ < WCOLWIDTH) output(" ");
404
        while (i++ < WCOLWIDTH) output(" ");
405
        if (++wcolcount == wcols) {
405
        if (++wcolcount == wcols) {
406
          wcolcount = 0;
406
          wcolcount = 0;
407
          outputnl("");
407
          outputnl("");
408
        } else {
408
        } else {
409
          availrows++; /* wide mode is the only one that does not write one line per file */
409
          availrows++; /* wide mode is the only one that does not write one line per file */
410
        }
410
        }
411
        break;
411
        break;
412
 
412
 
413
      case DIR_OUTPUT_BARE:
413
      case DIR_OUTPUT_BARE:
414
        outputnl(dta->fname);
414
        outputnl(dta->fname);
415
        break;
415
        break;
416
    }
416
    }
417
 
417
 
418
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
418
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
419
 
419
 
420
  } while (findnext(dta) == 0);
420
  } while (findnext(dta) == 0);
421
 
421
 
422
  if (wcolcount != 0) {
422
  if (wcolcount != 0) {
423
    outputnl(""); /* in wide mode make sure to end on a clear row */
423
    outputnl(""); /* in wide mode make sure to end on a clear row */
424
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
424
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
425
  }
425
  }
426
 
426
 
427
  /* print out summary (unless bare output mode) */
427
  /* print out summary (unless bare output mode) */
428
  if (format != DIR_OUTPUT_BARE) {
428
  if (format != DIR_OUTPUT_BARE) {
429
    unsigned short alignpos;
429
    unsigned short alignpos;
430
    unsigned char uint32maxlen = 13; /* 13 is the max len of a 32 bit number with thousand separators (4'000'000'000) */
430
    unsigned char uint32maxlen = 13; /* 13 is the max len of a 32 bit number with thousand separators (4'000'000'000) */
431
    if (screenw < 80) uint32maxlen = 10;
431
    if (screenw < 80) uint32maxlen = 10;
432
    /* x file(s) */
432
    /* x file(s) */
433
    memset(buff2, ' ', uint32maxlen);
433
    memset(buff2, ' ', uint32maxlen);
434
    i = nls_format_number(buff2 + uint32maxlen, summary_fcount, nls);
434
    i = nls_format_number(buff2 + uint32maxlen, summary_fcount, nls);
435
    alignpos = sprintf(buff2 + uint32maxlen + i, " %s ", svarlang_str(37,22)/*"file(s)"*/);
435
    alignpos = sprintf(buff2 + uint32maxlen + i, " %s ", svarlang_str(37,22)/*"file(s)"*/);
436
    output(buff2 + i);
436
    output(buff2 + i);
437
    /* xxxx bytes */
437
    /* xxxx bytes */
438
    i = nls_format_number(buff2 + uint32maxlen, summary_totsz, nls);
438
    i = nls_format_number(buff2 + uint32maxlen, summary_totsz, nls);
439
    output(buff2 + i + 1);
439
    output(buff2 + i + 1);
440
    output(" ");
440
    output(" ");
441
    nls_outputnl(37,23); /* "bytes" */
441
    nls_outputnl(37,23); /* "bytes" */
442
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
442
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
443
    /* xxxx bytes free */
443
    /* xxxx bytes free */
444
    i = cmd_dir_df(&summary_totsz, drv);
444
    i = cmd_dir_df(&summary_totsz, drv);
445
    if (i != 0) nls_outputnl_doserr(i);
445
    if (i != 0) nls_outputnl_doserr(i);
446
    alignpos += uint32maxlen * 2;
446
    alignpos += uint32maxlen * 2;
447
    memset(buff2, ' ', alignpos); /* align the freebytes value to same column as totbytes */
447
    memset(buff2, ' ', alignpos); /* align the freebytes value to same column as totbytes */
448
    i = nls_format_number(buff2 + alignpos, summary_totsz, nls);
448
    i = nls_format_number(buff2 + alignpos, summary_totsz, nls);
449
    output(buff2 + i + 1);
449
    output(buff2 + i + 1);
450
    output(" ");
450
    output(" ");
451
    nls_outputnl(37,24); /* "bytes free" */
451
    nls_outputnl(37,24); /* "bytes free" */
452
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
452
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
453
  }
453
  }
454
 
454
 
455
  return(CMD_OK);
455
  return(CMD_OK);
456
}
456
}
457
 
457