Subversion Repositories SvarDOS

Rev

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

Rev 1716 Rev 1717
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-2024 Mateusz Viste
4
 * Copyright (C) 2021-2024 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
/* a "tiny" DTA is a DTA that is stripped from bytes that are not needed for
59
/* a "tiny" DTA is a DTA that is stripped from bytes that are not needed for
60
 * DIR operations */
60
 * DIR operations */
61
_Packed struct TINYDTA {
61
_Packed struct TINYDTA {
62
/*  char reserved[21];
62
/*  char reserved[21];
63
  unsigned char attr; */
63
  unsigned char attr; */
64
  unsigned short time_sec2:5;
64
  unsigned short time_sec2:5;
65
  unsigned short time_min:6;
65
  unsigned short time_min:6;
66
  unsigned short time_hour:5;
66
  unsigned short time_hour:5;
67
  unsigned short date_dy:5;
67
  unsigned short date_dy:5;
68
  unsigned short date_mo:4;
68
  unsigned short date_mo:4;
69
  unsigned short date_yr:7;
69
  unsigned short date_yr:7;
70
  unsigned long size;
70
  unsigned long size;
71
/*  char fname[13]; */
71
/*  char fname[13]; */
72
  char fname[12];
72
  char fname[12];
73
};
73
};
74
 
74
 
75
/* max amount of DTAs that can be buffered (not using 65535 because fmalloc a bit of overhead space) */
-
 
76
#define MAX_DTA_BUFCOUNT (65500 / sizeof(struct TINYDTA))
-
 
77
 
-
 
78
 
75
 
79
/* fills freebytes with free bytes for drv (A=0, B=1, etc)
76
/* fills freebytes with free bytes for drv (A=0, B=1, etc)
80
 * returns DOS ERR code on failure */
77
 * returns DOS ERR code on failure */
81
static unsigned short cmd_dir_df(unsigned long *freebytes, unsigned char drv) {
78
static unsigned short cmd_dir_df(unsigned long *freebytes, unsigned char drv) {
82
  unsigned short res = 0;
79
  unsigned short res = 0;
83
  unsigned short sects_per_clust = 0, avail_clusts = 0, bytes_per_sect = 0;
80
  unsigned short sects_per_clust = 0, avail_clusts = 0, bytes_per_sect = 0;
84
 
81
 
85
  _asm {
82
  _asm {
86
    push ax
83
    push ax
87
    push bx
84
    push bx
88
    push cx
85
    push cx
89
    push dx
86
    push dx
90
 
87
 
91
    mov ah, 0x36  /* DOS 2+ -- Get Disk Free Space */
88
    mov ah, 0x36  /* DOS 2+ -- Get Disk Free Space */
92
    mov dl, [drv] /* A=1, B=2, etc (0 = DEFAULT DRIVE) */
89
    mov dl, [drv] /* A=1, B=2, etc (0 = DEFAULT DRIVE) */
93
    inc dl
90
    inc dl
94
    int 0x21      /* AX=sects_per_clust, BX=avail_clusts, CX=bytes_per_sect, DX=tot_clusters */
91
    int 0x21      /* AX=sects_per_clust, BX=avail_clusts, CX=bytes_per_sect, DX=tot_clusters */
95
    cmp ax, 0xffff /* AX=0xffff on error (invalid drive) */
92
    cmp ax, 0xffff /* AX=0xffff on error (invalid drive) */
96
    jne COMPUTEDF
93
    jne COMPUTEDF
97
    mov [res], 0x0f /* fill res with DOS error code 15 ("invalid drive") */
94
    mov [res], 0x0f /* fill res with DOS error code 15 ("invalid drive") */
98
    jmp DONE
95
    jmp DONE
99
 
96
 
100
    COMPUTEDF:
97
    COMPUTEDF:
101
    /* freebytes = AX * BX * CX */
98
    /* freebytes = AX * BX * CX */
102
    mov [sects_per_clust], ax
99
    mov [sects_per_clust], ax
103
    mov [avail_clusts], bx
100
    mov [avail_clusts], bx
104
    mov [bytes_per_sect], cx
101
    mov [bytes_per_sect], cx
105
 
102
 
106
    DONE:
103
    DONE:
107
    pop dx
104
    pop dx
108
    pop cx
105
    pop cx
109
    pop bx
106
    pop bx
110
    pop ax
107
    pop ax
111
  }
108
  }
112
 
109
 
113
  /* multiple steps to avoid uint16 overflow */
110
  /* multiple steps to avoid uint16 overflow */
114
  *freebytes = sects_per_clust;
111
  *freebytes = sects_per_clust;
115
  *freebytes *= avail_clusts;
112
  *freebytes *= avail_clusts;
116
  *freebytes *= bytes_per_sect;
113
  *freebytes *= bytes_per_sect;
117
 
114
 
118
  return(res);
115
  return(res);
119
}
116
}
120
 
117
 
121
 
118
 
122
static void dir_pagination(unsigned short *availrows) {
119
static void dir_pagination(unsigned short *availrows) {
123
  *availrows -= 1;
120
  *availrows -= 1;
124
  if (*availrows == 0) {
121
  if (*availrows == 0) {
125
    press_any_key();
122
    press_any_key();
126
    *availrows = screen_getheight() - 1;
123
    *availrows = screen_getheight() - 1;
127
  }
124
  }
128
}
125
}
129
 
126
 
130
 
127
 
131
/* parse an attr list like "Ar-hS" and fill bitfield into attrfilter_may and attrfilter_must.
128
/* parse an attr list like "Ar-hS" and fill bitfield into attrfilter_may and attrfilter_must.
132
 * /AHS   -> adds S and H to mandatory attribs ("must")
129
 * /AHS   -> adds S and H to mandatory attribs ("must")
133
 * /A-S   -> removes S from allowed attribs ("may")
130
 * /A-S   -> removes S from allowed attribs ("may")
134
 * returns non-zero on error. */
131
 * returns non-zero on error. */
135
static int dir_parse_attr_list(const char *arg, unsigned char *attrfilter_may, unsigned char *attrfilter_must) {
132
static int dir_parse_attr_list(const char *arg, unsigned char *attrfilter_may, unsigned char *attrfilter_must) {
136
  for (; *arg != 0; arg++) {
133
  for (; *arg != 0; arg++) {
137
    unsigned char curattr;
134
    unsigned char curattr;
138
    char not;
135
    char not;
139
    if (*arg == '-') {
136
    if (*arg == '-') {
140
      not = 1;
137
      not = 1;
141
      arg++;
138
      arg++;
142
    } else {
139
    } else {
143
      not = 0;
140
      not = 0;
144
    }
141
    }
145
    switch (*arg) {
142
    switch (*arg) {
146
      case 'd':
143
      case 'd':
147
      case 'D':
144
      case 'D':
148
        curattr = DOS_ATTR_DIR;
145
        curattr = DOS_ATTR_DIR;
149
        break;
146
        break;
150
      case 'r':
147
      case 'r':
151
      case 'R':
148
      case 'R':
152
        curattr = DOS_ATTR_RO;
149
        curattr = DOS_ATTR_RO;
153
        break;
150
        break;
154
      case 'a':
151
      case 'a':
155
      case 'A':
152
      case 'A':
156
        curattr = DOS_ATTR_ARC;
153
        curattr = DOS_ATTR_ARC;
157
        break;
154
        break;
158
      case 'h':
155
      case 'h':
159
      case 'H':
156
      case 'H':
160
        curattr = DOS_ATTR_HID;
157
        curattr = DOS_ATTR_HID;
161
        break;
158
        break;
162
      case 's':
159
      case 's':
163
      case 'S':
160
      case 'S':
164
        curattr = DOS_ATTR_SYS;
161
        curattr = DOS_ATTR_SYS;
165
        break;
162
        break;
166
      default:
163
      default:
167
        return(-1);
164
        return(-1);
168
    }
165
    }
169
    /* update res bitfield */
166
    /* update res bitfield */
170
    if (not) {
167
    if (not) {
171
      *attrfilter_may &= ~curattr;
168
      *attrfilter_may &= ~curattr;
172
    } else {
169
    } else {
173
      *attrfilter_must |= curattr;
170
      *attrfilter_must |= curattr;
174
    }
171
    }
175
  }
172
  }
176
  return(0);
173
  return(0);
177
}
174
}
178
 
175
 
179
 
176
 
180
/* compare attributes in a DTA node to mandatory and optional attributes. returns 1 on match, 0 otherwise */
177
/* compare attributes in a DTA node to mandatory and optional attributes. returns 1 on match, 0 otherwise */
181
static int filter_attribs(const struct DTA *dta, unsigned char attrfilter_must, unsigned char attrfilter_may) {
178
static int filter_attribs(const struct DTA *dta, unsigned char attrfilter_must, unsigned char attrfilter_may) {
182
  /* if mandatory attribs are requested, filter them now */
179
  /* if mandatory attribs are requested, filter them now */
183
  if ((attrfilter_must & dta->attr) != attrfilter_must) return(0);
180
  if ((attrfilter_must & dta->attr) != attrfilter_must) return(0);
184
 
181
 
185
  /* if file contains attributes that are not allowed -> skip */
182
  /* if file contains attributes that are not allowed -> skip */
186
  if ((~attrfilter_may & dta->attr) != 0) return(0);
183
  if ((~attrfilter_may & dta->attr) != 0) return(0);
187
 
184
 
188
  return(1);
185
  return(1);
189
}
186
}
190
 
187
 
191
 
188
 
192
 
189
 
193
#define DIR_ATTR_DEFAULT (DOS_ATTR_RO | DOS_ATTR_DIR | DOS_ATTR_ARC)
190
#define DIR_ATTR_DEFAULT (DOS_ATTR_RO | DOS_ATTR_DIR | DOS_ATTR_ARC)
194
 
191
 
195
static enum cmd_result cmd_dir(struct cmd_funcparam *p) {
192
static enum cmd_result cmd_dir(struct cmd_funcparam *p) {
196
  const char *filespecptr = NULL;
193
  const char *filespecptr = NULL;
197
  struct DTA *dta = (void *)0x80; /* set DTA to its default location at 80h in PSP */
194
  struct DTA *dta = (void *)0x80; /* set DTA to its default location at 80h in PSP */
198
  struct DTA far *dtabuf = NULL; /* used to buffer results when sorting is enabled */
195
  struct DTA far *dtabuf = NULL; /* used to buffer results when sorting is enabled */
199
  struct DTA far *dtabuf_root = NULL;
196
  struct DTA far *dtabuf_root = NULL;
200
  unsigned short dtabufcount = 0;
197
  unsigned short dtabufcount = 0;
201
  unsigned short i;
198
  unsigned short i;
202
  unsigned short availrows;  /* counter of available rows on display (used for /P) */
199
  unsigned short availrows;  /* counter of available rows on display (used for /P) */
203
  unsigned short screenw = screen_getwidth();
200
  unsigned short screenw = screen_getwidth();
204
  unsigned short wcols = screenw / WCOLWIDTH; /* number of columns in wide mode */
201
  unsigned short wcols = screenw / WCOLWIDTH; /* number of columns in wide mode */
205
  unsigned char wcolcount;
202
  unsigned char wcolcount;
-
 
203
  struct {
206
  struct nls_patterns *nls = (void *)(p->BUFFER + (p->BUFFERSZ / 2));
204
    struct nls_patterns nls;
-
 
205
    char buff64[64];
-
 
206
    char path[128];
207
  char *buff2 = p->BUFFER + (p->BUFFERSZ / 2) + sizeof(*nls);
207
  } *buf = (void *)(p->BUFFER);
208
  unsigned long summary_fcount = 0;
208
  unsigned long summary_fcount = 0;
209
  unsigned long summary_totsz = 0;
209
  unsigned long summary_totsz = 0;
210
  unsigned char drv = 0;
210
  unsigned char drv = 0;
211
  unsigned char attrfilter_may = DIR_ATTR_DEFAULT;
211
  unsigned char attrfilter_may = DIR_ATTR_DEFAULT;
212
  unsigned char attrfilter_must = 0;
212
  unsigned char attrfilter_must = 0;
213
  const char far *order = NULL; /* order string (like "GNE-SD"), this may come
213
  const char far *order = NULL; /* order string (like "GNE-SD"), this may come
214
                                   either from the /O argument or from the
214
                                   either from the /O argument or from the
215
                                   DIRCMD env variable (NULL = "no sort")
215
                                   DIRCMD env variable (NULL = "no sort")
216
                                   note 1: the '-' reverse relates only to
216
                                   note 1: the '-' reverse relates only to
217
                                   the letter that immediately follows it
217
                                   the letter that immediately follows it
218
                                   note 2: '/O' is a shorthand for '/OGNE' */
218
                                   note 2: '/O' is a shorthand for '/OGNE' */
219
 
219
 
220
  #define DIR_FLAG_PAUSE  1
220
  #define DIR_FLAG_PAUSE  1
221
  #define DIR_FLAG_RECUR  4
221
  #define DIR_FLAG_RECUR  4
222
  #define DIR_FLAG_LCASE  8
222
  #define DIR_FLAG_LCASE  8
223
  unsigned char flags = 0;
223
  unsigned char flags = 0;
224
 
224
 
225
  #define DIR_OUTPUT_NORM 1
225
  #define DIR_OUTPUT_NORM 1
226
  #define DIR_OUTPUT_WIDE 2
226
  #define DIR_OUTPUT_WIDE 2
227
  #define DIR_OUTPUT_BARE 3
227
  #define DIR_OUTPUT_BARE 3
228
  unsigned char format = DIR_OUTPUT_NORM;
228
  unsigned char format = DIR_OUTPUT_NORM;
229
 
229
 
230
  if (cmd_ishlp(p)) {
230
  if (cmd_ishlp(p)) {
231
    nls_outputnl(37,0); /* "Displays a list of files and subdirectories in a directory" */
231
    nls_outputnl(37,0); /* "Displays a list of files and subdirectories in a directory" */
232
    outputnl("");
232
    outputnl("");
233
    nls_outputnl(37,1); /* "DIR [drive:][path][filename] [/P] [/W] [/A[:]attributes] [/O[[:]sortorder]] [/S] [/B] [/L]" */
233
    nls_outputnl(37,1); /* "DIR [drive:][path][filename] [/P] [/W] [/A[:]attributes] [/O[[:]sortorder]] [/S] [/B] [/L]" */
234
    outputnl("");
234
    outputnl("");
235
    nls_outputnl(37,2); /* "/P Pauses after each screenful of information" */
235
    nls_outputnl(37,2); /* "/P Pauses after each screenful of information" */
236
    nls_outputnl(37,3); /* "/W Uses wide list format" */
236
    nls_outputnl(37,3); /* "/W Uses wide list format" */
237
    outputnl("");
237
    outputnl("");
238
    nls_outputnl(37,4); /* "/A Displays files with specified attributes:" */
238
    nls_outputnl(37,4); /* "/A Displays files with specified attributes:" */
239
    nls_outputnl(37,5); /* "    D Directories            R Read-only files        H Hidden files" */
239
    nls_outputnl(37,5); /* "    D Directories            R Read-only files        H Hidden files" */
240
    nls_outputnl(37,6); /* "    A Ready for archiving    S System files           - prefix meaning "not"" */
240
    nls_outputnl(37,6); /* "    A Ready for archiving    S System files           - prefix meaning "not"" */
241
    outputnl("");
241
    outputnl("");
242
    nls_outputnl(37,7); /* "/O List files in sorted order:" */
242
    nls_outputnl(37,7); /* "/O List files in sorted order:" */
243
    nls_outputnl(37,8); /* "    N by name                S by size                E by extension" */
243
    nls_outputnl(37,8); /* "    N by name                S by size                E by extension" */
244
    nls_outputnl(37,9); /* "    D by date                G group dirs first       - prefix to reverse order" */
244
    nls_outputnl(37,9); /* "    D by date                G group dirs first       - prefix to reverse order" */
245
    outputnl("");
245
    outputnl("");
246
    nls_outputnl(37,10); /* "/S Displays files in specified directory and all subdirectories" */
246
    nls_outputnl(37,10); /* "/S Displays files in specified directory and all subdirectories" */
247
    nls_outputnl(37,11); /* "/B Uses bare format (no heading information or summary)" */
247
    nls_outputnl(37,11); /* "/B Uses bare format (no heading information or summary)" */
248
    nls_outputnl(37,12); /* "/L Uses lowercases" */
248
    nls_outputnl(37,12); /* "/L Uses lowercases" */
249
    return(CMD_OK);
249
    return(CMD_OK);
250
  }
250
  }
251
 
251
 
252
  i = nls_getpatterns(nls);
252
  i = nls_getpatterns(&(buf->nls));
253
  if (i != 0) nls_outputnl_doserr(i);
253
  if (i != 0) nls_outputnl_doserr(i);
254
 
254
 
255
  /* disable usage of thousands separator on narrow screens */
255
  /* disable usage of thousands separator on narrow screens */
256
  if (screenw < 80) nls->thousep[0] = 0;
256
  if (screenw < 80) buf->nls.thousep[0] = 0;
257
 
257
 
258
  /* parse command line */
258
  /* parse command line */
259
  for (i = 0; i < p->argc; i++) {
259
  for (i = 0; i < p->argc; i++) {
260
    if (p->argv[i][0] == '/') {
260
    if (p->argv[i][0] == '/') {
261
      const char *arg = p->argv[i] + 1;
261
      const char *arg = p->argv[i] + 1;
262
      char neg = 0;
262
      char neg = 0;
263
      /* detect negations and get actual argument */
263
      /* detect negations and get actual argument */
264
      if (*arg == '-') {
264
      if (*arg == '-') {
265
        neg = 1;
265
        neg = 1;
266
        arg++;
266
        arg++;
267
      }
267
      }
268
      /* */
268
      /* */
269
      switch (*arg) {
269
      switch (*arg) {
270
        case 'a':
270
        case 'a':
271
        case 'A':
271
        case 'A':
272
          arg++;
272
          arg++;
273
          /* preset defaults */
273
          /* preset defaults */
274
          attrfilter_may = DIR_ATTR_DEFAULT;
274
          attrfilter_may = DIR_ATTR_DEFAULT;
275
          attrfilter_must = 0;
275
          attrfilter_must = 0;
276
          /* /-A only allowed without further parameters (used to cancel possible previous /Asmth) */
276
          /* /-A only allowed without further parameters (used to cancel possible previous /Asmth) */
277
          if (neg) {
277
          if (neg) {
278
            if (*arg != 0) {
278
            if (*arg != 0) {
279
              nls_outputnl_err(0, 2); /* invalid switch */
279
              nls_outputnl_err(0, 2); /* invalid switch */
280
              return(CMD_FAIL);
280
              return(CMD_FAIL);
281
            }
281
            }
282
          } else {
282
          } else {
283
            /* skip colon if present */
283
            /* skip colon if present */
284
            if (*arg == ':') arg++;
284
            if (*arg == ':') arg++;
285
            /* start with "allow everything" */
285
            /* start with "allow everything" */
286
            attrfilter_may = (DOS_ATTR_ARC | DOS_ATTR_DIR | DOS_ATTR_HID | DOS_ATTR_SYS | DOS_ATTR_RO);
286
            attrfilter_may = (DOS_ATTR_ARC | DOS_ATTR_DIR | DOS_ATTR_HID | DOS_ATTR_SYS | DOS_ATTR_RO);
287
            if (dir_parse_attr_list(arg, &attrfilter_may, &attrfilter_must) != 0) {
287
            if (dir_parse_attr_list(arg, &attrfilter_may, &attrfilter_must) != 0) {
288
              nls_outputnl_err(0, 3); /* invalid parameter format */
288
              nls_outputnl_err(0, 3); /* invalid parameter format */
289
              return(CMD_FAIL);
289
              return(CMD_FAIL);
290
            }
290
            }
291
          }
291
          }
292
          break;
292
          break;
293
        case 'b':
293
        case 'b':
294
        case 'B':
294
        case 'B':
295
          format = DIR_OUTPUT_BARE;
295
          format = DIR_OUTPUT_BARE;
296
          break;
296
          break;
297
        case 'l':
297
        case 'l':
298
        case 'L':
298
        case 'L':
299
          flags |= DIR_FLAG_LCASE;
299
          flags |= DIR_FLAG_LCASE;
300
          break;
300
          break;
301
        case 'o':
301
        case 'o':
302
        case 'O':
302
        case 'O':
303
          order = arg+1;
303
          order = arg+1;
304
          break;
304
          break;
305
        case 'p':
305
        case 'p':
306
        case 'P':
306
        case 'P':
307
          flags |= DIR_FLAG_PAUSE;
307
          flags |= DIR_FLAG_PAUSE;
308
          if (neg) flags &= (0xff ^ DIR_FLAG_PAUSE);
308
          if (neg) flags &= (0xff ^ DIR_FLAG_PAUSE);
309
          break;
309
          break;
310
        case 's':
310
        case 's':
311
        case 'S':
311
        case 'S':
312
          /* TODO */
312
          /* TODO */
313
          outputnl("/S NOT IMPLEMENTED YET");
313
          outputnl("/S NOT IMPLEMENTED YET");
314
          return(CMD_FAIL);
314
          return(CMD_FAIL);
315
          break;
315
          break;
316
        case 'w':
316
        case 'w':
317
        case 'W':
317
        case 'W':
318
          format = DIR_OUTPUT_WIDE;
318
          format = DIR_OUTPUT_WIDE;
319
          break;
319
          break;
320
        default:
320
        default:
321
          nls_outputnl_err(0, 2); /* invalid switch */
321
          nls_outputnl_err(0, 2); /* invalid switch */
322
          return(CMD_FAIL);
322
          return(CMD_FAIL);
323
      }
323
      }
324
    } else {  /* filespec */
324
    } else {  /* filespec */
325
      if (filespecptr != NULL) {
325
      if (filespecptr != NULL) {
326
        nls_outputnl_err(0, 4); /* too many parameters */
326
        nls_outputnl_err(0, 4); /* too many parameters */
327
        return(CMD_FAIL);
327
        return(CMD_FAIL);
328
      }
328
      }
329
      filespecptr = p->argv[i];
329
      filespecptr = p->argv[i];
330
    }
330
    }
331
  }
331
  }
332
 
332
 
333
  if (filespecptr == NULL) filespecptr = ".";
333
  if (filespecptr == NULL) filespecptr = ".";
334
 
334
 
335
  availrows = screen_getheight() - 2;
335
  availrows = screen_getheight() - 2;
336
 
336
 
337
  /* special case: "DIR drive:" (truename() fails on "C:" under MS-DOS 6.0) */
337
  /* special case: "DIR drive:" (truename() fails on "C:" under MS-DOS 6.0) */
338
  if ((filespecptr[0] != 0) && (filespecptr[1] == ':') && (filespecptr[2] == 0)) {
338
  if ((filespecptr[0] != 0) && (filespecptr[1] == ':') && (filespecptr[2] == 0)) {
339
    if ((filespecptr[0] >= 'a') && (filespecptr[0] <= 'z')) {
339
    if ((filespecptr[0] >= 'a') && (filespecptr[0] <= 'z')) {
340
      p->BUFFER[0] = filespecptr[0] - ('a' - 1);
340
      buf->path[0] = filespecptr[0] - ('a' - 1);
341
    } else {
341
    } else {
342
      p->BUFFER[0] = filespecptr[0] - ('A' - 1);
342
      buf->path[0] = filespecptr[0] - ('A' - 1);
343
    }
343
    }
344
    i = curpathfordrv(p->BUFFER, p->BUFFER[0]);
344
    i = curpathfordrv(buf->path, buf->path[0]);
345
  } else {
345
  } else {
346
    i = file_truename(filespecptr, p->BUFFER);
346
    i = file_truename(filespecptr, buf->path);
347
  }
347
  }
348
  if (i != 0) {
348
  if (i != 0) {
349
    nls_outputnl_doserr(i);
349
    nls_outputnl_doserr(i);
350
    return(CMD_FAIL);
350
    return(CMD_FAIL);
351
  }
351
  }
352
 
352
 
353
  if (format != DIR_OUTPUT_BARE) {
353
  if (format != DIR_OUTPUT_BARE) {
354
    drv = p->BUFFER[0];
354
    drv = buf->path[0];
355
    if (drv >= 'a') {
355
    if (drv >= 'a') {
356
      drv -= 'a';
356
      drv -= 'a';
357
    } else {
357
    } else {
358
      drv -= 'A';
358
      drv -= 'A';
359
    }
359
    }
360
    cmd_vol_internal(drv, buff2);
360
    cmd_vol_internal(drv, buf->buff64);
361
    sprintf(buff2, svarlang_str(37,20)/*"Directory of %s"*/, p->BUFFER);
361
    sprintf(buf->buff64, svarlang_str(37,20)/*"Directory of %s"*/, buf->path);
362
    /* trim at first '?', if any */
362
    /* trim at first '?', if any */
363
    for (i = 0; buff2[i] != 0; i++) if (buff2[i] == '?') buff2[i] = 0;
363
    for (i = 0; buf->buff64[i] != 0; i++) if (buf->buff64[i] == '?') buf->buff64[i] = 0;
364
    outputnl(buff2);
364
    outputnl(buf->buff64);
365
    outputnl("");
365
    outputnl("");
366
    availrows -= 3;
366
    availrows -= 3;
367
  }
367
  }
368
 
368
 
369
  /* if dir: append a backslash (also get its len) */
369
  /* if dir: append a backslash (also get its len) */
370
  i = path_appendbkslash_if_dir(p->BUFFER);
370
  i = path_appendbkslash_if_dir(buf->path);
371
 
371
 
372
  /* if ends with a \ then append ????????.??? */
372
  /* if ends with a \ then append ????????.??? */
373
  if (p->BUFFER[i - 1] == '\\') strcat(p->BUFFER, "????????.???");
373
  if (buf->path[i - 1] == '\\') strcat(buf->path, "????????.???");
374
 
374
 
375
  /* ask DOS for list of files, but only with allowed attribs */
375
  /* ask DOS for list of files, but only with allowed attribs */
376
  i = findfirst(dta, p->BUFFER, attrfilter_may);
376
  i = findfirst(dta, buf->path, attrfilter_may);
377
  if (i != 0) {
377
  if (i != 0) {
378
    nls_outputnl_doserr(i);
378
    nls_outputnl_doserr(i);
379
    return(CMD_FAIL);
379
    return(CMD_FAIL);
380
  }
380
  }
381
 
381
 
382
  /* if sorting is involved, then let's buffer all results (and sort them) */
382
  /* if sorting is involved, then let's buffer all results (and sort them) */
383
  if (order != NULL) {
383
  if (order != NULL) {
384
    /* allocate a memory buffer - try several sizes until one succeeds */
384
    /* allocate a memory buffer - try several sizes until one succeeds */
385
    unsigned short memsz[] = {65500, 32000, 16000, 8000, 4000, 2000, 0};
385
    const unsigned short memsz[] = {65500, 32000, 16000, 8000, 4000, 2000, 1000, 0};
386
    unsigned short max_dta_bufcount = 0;
386
    unsigned short max_dta_bufcount = 0;
387
    for (i = 0; memsz[i] != 0; i++) {
387
    for (i = 0; memsz[i] != 0; i++) {
388
      dtabuf = _fmalloc(memsz[i]);
388
      dtabuf = _fmalloc(memsz[i]);
389
      if (dtabuf != NULL) break;
389
      if (dtabuf != NULL) break;
390
    }
390
    }
391
 
391
 
392
    if (dtabuf == NULL) {
392
    if (dtabuf == NULL) {
393
      nls_outputnl_doserr(8); /* out of memory */
393
      nls_outputnl_doserr(8); /* out of memory */
394
      return(CMD_FAIL);
394
      return(CMD_FAIL);
395
    }
395
    }
396
 
396
 
397
    /* remember the address so I can free it afterwards */
397
    /* remember the address so I can free it afterwards */
398
    dtabuf_root = dtabuf;
398
    dtabuf_root = dtabuf;
399
 
399
 
400
    /* compute the amount of DTAs I can buffer */
400
    /* compute the amount of DTAs I can buffer */
401
    max_dta_bufcount = 1; //memsz[i] / sizeof(struct TINYDTA);
401
    max_dta_bufcount = memsz[i] / sizeof(struct TINYDTA);
402
    printf("max_dta_bufcount = %u\n", max_dta_bufcount);
402
    printf("max_dta_bufcount = %u\n", max_dta_bufcount);
403
 
403
 
404
    do {
404
    do {
405
      /* filter out files with uninteresting attributes */
405
      /* filter out files with uninteresting attributes */
406
      if (filter_attribs(dta, attrfilter_must, attrfilter_may) == 0) continue;
406
      if (filter_attribs(dta, attrfilter_must, attrfilter_may) == 0) continue;
407
 
407
 
408
      _fmemcpy(&(dtabuf[dtabufcount]), ((char *)dta) + 22, sizeof(struct TINYDTA));
408
      _fmemcpy(&(dtabuf[dtabufcount]), ((char *)dta) + 22, sizeof(struct TINYDTA));
409
 
409
 
410
      /* save attribs in sec field, otherwise zero it (this field is not
410
      /* save attribs in sec field, otherwise zero it (this field is not
411
       * displayed and dropping the attr field saves 2 bytes per entry) */
411
       * displayed and dropping the attr field saves 2 bytes per entry) */
412
      dtabuf[dtabufcount++].time_sec2 = (dta->attr & 31);
412
      dtabuf[dtabufcount++].time_sec2 = (dta->attr & 31);
413
 
413
 
414
      /* do I have any space left? */
414
      /* do I have any space left? */
415
      if (dtabufcount == max_dta_bufcount) {
415
      if (dtabufcount == max_dta_bufcount) {
416
        //outputnl("TOO MANY ENTRIES FOR SORTING! LIST IS UNSORTED");
416
        //outputnl("TOO MANY ENTRIES FOR SORTING! LIST IS UNSORTED");
417
        break;
417
        break;
418
      }
418
      }
419
 
419
 
420
    } while (findnext(dta) == 0);
420
    } while (findnext(dta) == 0);
421
 
421
 
422
    /* sort the list - the tricky part is that my array is a far address while
422
    /* sort the list - the tricky part is that my array is a far address while
423
     * qsort works only with near pointers, so I have to use an ugly auxiliary
423
     * qsort works only with near pointers, so I have to use an ugly auxiliary
424
     * table */
424
     * table */
425
    // qsort(dt); TODO
425
    // qsort(dt); TODO
426
 
426
 
427
    /* preload first entry */
427
    /* preload first entry */
428
    _fmemcpy(((unsigned char *)dta) + 22, dtabuf, sizeof(struct TINYDTA));
428
    _fmemcpy(((unsigned char *)dta) + 22, dtabuf, sizeof(struct TINYDTA));
429
    dta->attr = dtabuf->time_sec2;
429
    dta->attr = dtabuf->time_sec2;
430
    dtabuf++;
430
    dtabuf++;
431
    dtabufcount--;
431
    dtabufcount--;
432
  }
432
  }
433
 
433
 
434
  wcolcount = 0; /* may be used for columns counting with wide mode */
434
  wcolcount = 0; /* may be used for columns counting with wide mode */
435
 
435
 
436
  for (;;) {
436
  for (;;) {
437
 
437
 
438
    /* filter out attributes (skip if entry comes from buffer, then it was already veted) */
438
    /* filter out attributes (skip if entry comes from buffer, then it was already veted) */
439
    if (filter_attribs(dta, attrfilter_must, attrfilter_may) == 0) continue;
439
    if (filter_attribs(dta, attrfilter_must, attrfilter_may) == 0) continue;
440
 
440
 
441
    /* turn string lcase (/L) */
441
    /* turn string lcase (/L) */
442
    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... */
442
    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... */
443
 
443
 
444
    summary_fcount++;
444
    summary_fcount++;
445
    if ((dta->attr & DOS_ATTR_DIR) == 0) summary_totsz += dta->size;
445
    if ((dta->attr & DOS_ATTR_DIR) == 0) summary_totsz += dta->size;
446
 
446
 
447
    switch (format) {
447
    switch (format) {
448
      case DIR_OUTPUT_NORM:
448
      case DIR_OUTPUT_NORM:
449
        /* print fname-space-extension (unless it's "." or "..", then print as-is) */
449
        /* print fname-space-extension (unless it's "." or "..", then print as-is) */
450
        if (dta->fname[0] == '.') {
450
        if (dta->fname[0] == '.') {
451
          output(dta->fname);
451
          output(dta->fname);
452
          i = strlen(dta->fname);
452
          i = strlen(dta->fname);
453
          while (i++ < 12) output(" ");
453
          while (i++ < 12) output(" ");
454
        } else {
454
        } else {
455
          file_fname2fcb(buff2, dta->fname);
455
          file_fname2fcb(buf->buff64, dta->fname);
456
          memmove(buff2 + 9, buff2 + 8, 4);
456
          memmove(buf->buff64 + 9, buf->buff64 + 8, 4);
457
          buff2[8] = ' ';
457
          buf->buff64[8] = ' ';
458
          output(buff2);
458
          output(buf->buff64);
459
        }
459
        }
460
        output(" ");
460
        output(" ");
461
        /* either <DIR> or right aligned 10-chars byte size */
461
        /* either <DIR> or right aligned 10-chars byte size */
462
        memset(buff2, ' ', 10);
462
        memset(buf->buff64, ' ', 10);
463
        if (dta->attr & DOS_ATTR_DIR) {
463
        if (dta->attr & DOS_ATTR_DIR) {
464
          strcpy(buff2 + 10, svarlang_str(37,21));
464
          strcpy(buf->buff64 + 10, svarlang_str(37,21));
465
        } else {
465
        } else {
466
          nls_format_number(buff2 + 10, dta->size, nls);
466
          nls_format_number(buf->buff64 + 10, dta->size, &(buf->nls));
467
        }
467
        }
468
        output(buff2 + strlen(buff2) - 10);
468
        output(buf->buff64 + strlen(buf->buff64) - 10);
469
        /* two spaces and NLS DATE */
469
        /* two spaces and NLS DATE */
470
        buff2[0] = ' ';
470
        buf->buff64[0] = ' ';
471
        buff2[1] = ' ';
471
        buf->buff64[1] = ' ';
472
        if (screenw >= 80) {
472
        if (screenw >= 80) {
473
          nls_format_date(buff2 + 2, dta->date_yr + 1980, dta->date_mo, dta->date_dy, nls);
473
          nls_format_date(buf->buff64 + 2, dta->date_yr + 1980, dta->date_mo, dta->date_dy, &(buf->nls));
474
        } else {
474
        } else {
475
          nls_format_date(buff2 + 2, (dta->date_yr + 80) % 100, dta->date_mo, dta->date_dy, nls);
475
          nls_format_date(buf->buff64 + 2, (dta->date_yr + 80) % 100, dta->date_mo, dta->date_dy, &(buf->nls));
476
        }
476
        }
477
        output(buff2);
477
        output(buf->buff64);
478
 
478
 
479
        /* one space and NLS TIME */
479
        /* one space and NLS TIME */
480
        nls_format_time(buff2 + 1, dta->time_hour, dta->time_min, 0xff, nls);
480
        nls_format_time(buf->buff64 + 1, dta->time_hour, dta->time_min, 0xff, &(buf->nls));
481
        outputnl(buff2);
481
        outputnl(buf->buff64);
482
        break;
482
        break;
483
 
483
 
484
      case DIR_OUTPUT_WIDE: /* display in columns of 12 chars per item */
484
      case DIR_OUTPUT_WIDE: /* display in columns of 12 chars per item */
485
        i = strlen(dta->fname);
485
        i = strlen(dta->fname);
486
        if (dta->attr & DOS_ATTR_DIR) {
486
        if (dta->attr & DOS_ATTR_DIR) {
487
          i += 2;
487
          i += 2;
488
          output("[");
488
          output("[");
489
          output(dta->fname);
489
          output(dta->fname);
490
          output("]");
490
          output("]");
491
        } else {
491
        } else {
492
          output(dta->fname);
492
          output(dta->fname);
493
        }
493
        }
494
        while (i++ < WCOLWIDTH) output(" ");
494
        while (i++ < WCOLWIDTH) output(" ");
495
        if (++wcolcount == wcols) {
495
        if (++wcolcount == wcols) {
496
          wcolcount = 0;
496
          wcolcount = 0;
497
          outputnl("");
497
          outputnl("");
498
        } else {
498
        } else {
499
          availrows++; /* wide mode is the only one that does not write one line per file */
499
          availrows++; /* wide mode is the only one that does not write one line per file */
500
        }
500
        }
501
        break;
501
        break;
502
 
502
 
503
      case DIR_OUTPUT_BARE:
503
      case DIR_OUTPUT_BARE:
504
        outputnl(dta->fname);
504
        outputnl(dta->fname);
505
        break;
505
        break;
506
    }
506
    }
507
 
507
 
508
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
508
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
509
 
509
 
510
    /* take next entry, either from buf or disk */
510
    /* take next entry, either from buf or disk */
511
    if (dtabufcount > 0) {
511
    if (dtabufcount > 0) {
512
      /* preload first entry */
512
      /* preload first entry */
513
      _fmemcpy(((unsigned char *)dta) + 22, dtabuf, sizeof(struct TINYDTA));
513
      _fmemcpy(((unsigned char *)dta) + 22, dtabuf, sizeof(struct TINYDTA));
514
      dta->attr = dtabuf->time_sec2;
514
      dta->attr = dtabuf->time_sec2;
515
      dtabuf++;
515
      dtabuf++;
516
      dtabufcount--;
516
      dtabufcount--;
517
    } else {
517
    } else {
518
      if (findnext(dta) != 0) break;
518
      if (findnext(dta) != 0) break;
519
    }
519
    }
520
 
520
 
521
  }
521
  }
522
 
522
 
523
  if (wcolcount != 0) {
523
  if (wcolcount != 0) {
524
    outputnl(""); /* in wide mode make sure to end on a clear row */
524
    outputnl(""); /* in wide mode make sure to end on a clear row */
525
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
525
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
526
  }
526
  }
527
 
527
 
528
  /* print out summary (unless bare output mode) */
528
  /* print out summary (unless bare output mode) */
529
  if (format != DIR_OUTPUT_BARE) {
529
  if (format != DIR_OUTPUT_BARE) {
530
    unsigned short alignpos;
530
    unsigned short alignpos;
531
    unsigned char uint32maxlen = 13; /* 13 is the max len of a 32 bit number with thousand separators (4'000'000'000) */
531
    unsigned char uint32maxlen = 13; /* 13 is the max len of a 32 bit number with thousand separators (4'000'000'000) */
532
    if (screenw < 80) uint32maxlen = 10;
532
    if (screenw < 80) uint32maxlen = 10;
533
    /* x file(s) */
533
    /* x file(s) */
534
    memset(buff2, ' ', uint32maxlen);
534
    memset(buf->buff64, ' ', uint32maxlen);
535
    i = nls_format_number(buff2 + uint32maxlen, summary_fcount, nls);
535
    i = nls_format_number(buf->buff64 + uint32maxlen, summary_fcount, &(buf->nls));
536
    alignpos = sprintf(buff2 + uint32maxlen + i, " %s ", svarlang_str(37,22)/*"file(s)"*/);
536
    alignpos = sprintf(buf->buff64 + uint32maxlen + i, " %s ", svarlang_str(37,22)/*"file(s)"*/);
537
    output(buff2 + i);
537
    output(buf->buff64 + i);
538
    /* xxxx bytes */
538
    /* xxxx bytes */
539
    i = nls_format_number(buff2 + uint32maxlen, summary_totsz, nls);
539
    i = nls_format_number(buf->buff64 + uint32maxlen, summary_totsz, &(buf->nls));
540
    output(buff2 + i + 1);
540
    output(buf->buff64 + i + 1);
541
    output(" ");
541
    output(" ");
542
    nls_outputnl(37,23); /* "bytes" */
542
    nls_outputnl(37,23); /* "bytes" */
543
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
543
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
544
    /* xxxx bytes free */
544
    /* xxxx bytes free */
545
    i = cmd_dir_df(&summary_totsz, drv);
545
    i = cmd_dir_df(&summary_totsz, drv);
546
    if (i != 0) nls_outputnl_doserr(i);
546
    if (i != 0) nls_outputnl_doserr(i);
547
    alignpos += uint32maxlen * 2;
547
    alignpos += uint32maxlen * 2;
548
    memset(buff2, ' ', alignpos); /* align the freebytes value to same column as totbytes */
548
    memset(buf->buff64, ' ', alignpos); /* align the freebytes value to same column as totbytes */
549
    i = nls_format_number(buff2 + alignpos, summary_totsz, nls);
549
    i = nls_format_number(buf->buff64 + alignpos, summary_totsz, &(buf->nls));
550
    output(buff2 + i + 1);
550
    output(buf->buff64 + i + 1);
551
    output(" ");
551
    output(" ");
552
    nls_outputnl(37,24); /* "bytes free" */
552
    nls_outputnl(37,24); /* "bytes free" */
553
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
553
    if (flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
554
  }
554
  }
555
 
555
 
556
  /* free the buffer memory (if used) */
556
  /* free the buffer memory (if used) */
557
  if (dtabuf_root != NULL) _ffree(dtabuf_root);
557
  if (dtabuf_root != NULL) _ffree(dtabuf_root);
558
 
558
 
559
  return(CMD_OK);
559
  return(CMD_OK);
560
}
560
}
561
 
561