Subversion Repositories SvarDOS

Rev

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

Rev 1743 Rev 1744
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
 
75
 
-
 
76
/* returns current COUNTRY id (1=USA, 7=RU, 33=FR, 48=PL, 49=DE, etc) */
-
 
77
static unsigned short dir_cur_country(void) {
-
 
78
  _Packed struct { /* Extended Country Info Block */
-
 
79
    unsigned char bRecID;   /* Information ID */
-
 
80
    unsigned short wRecLen; /* size of information */
-
 
81
    unsigned short wCountryID; /* country code */
-
 
82
    unsigned short wCodePgID;  /* code page */
-
 
83
    /* the block structure is much larger, but I only need the fields above */
-
 
84
  } buff;
-
 
85
  void *buffptr = &buff;
-
 
86
 
-
 
87
  _asm {
-
 
88
    push ax
-
 
89
    push bx
-
 
90
    push cx
-
 
91
    push dx
-
 
92
    push es
-
 
93
    push di
-
 
94
 
-
 
95
    mov ax, 0x6501  /* DOS 3.3+ - Get Extended Country Information */
-
 
96
    mov bx, 0xffff  /* code page (FFFFh = current) */
-
 
97
    mov cx, 7       /* sizeof(buff) */
-
 
98
    mov dx, bx      /* country code (FFFFh = current) */
-
 
99
    push ds
-
 
100
    pop es
-
 
101
    mov di, buffptr
-
 
102
    int 0x21
-
 
103
 
-
 
104
    pop di
-
 
105
    pop es
-
 
106
    pop dx
-
 
107
    pop cx
-
 
108
    pop bx
-
 
109
    pop ax
-
 
110
  }
-
 
111
 
-
 
112
  return(buff.wCountryID);
-
 
113
}
-
 
114
 
-
 
115
 
76
/* fills freebytes with free bytes for drv (A=0, B=1, etc)
116
/* fills freebytes with free bytes for drv (A=0, B=1, etc)
77
 * returns DOS ERR code on failure */
117
 * returns DOS ERR code on failure */
78
static unsigned short cmd_dir_df(unsigned long *freebytes, unsigned char drv) {
118
static unsigned short cmd_dir_df(unsigned long *freebytes, unsigned char drv) {
79
  unsigned short res = 0;
119
  unsigned short res = 0;
80
  unsigned short sects_per_clust = 0, avail_clusts = 0, bytes_per_sect = 0;
120
  unsigned short sects_per_clust = 0, avail_clusts = 0, bytes_per_sect = 0;
81
 
121
 
82
  _asm {
122
  _asm {
83
    push ax
123
    push ax
84
    push bx
124
    push bx
85
    push cx
125
    push cx
86
    push dx
126
    push dx
87
 
127
 
88
    mov ah, 0x36  /* DOS 2+ -- Get Disk Free Space */
128
    mov ah, 0x36  /* DOS 2+ -- Get Disk Free Space */
89
    mov dl, [drv] /* A=1, B=2, etc (0 = DEFAULT DRIVE) */
129
    mov dl, [drv] /* A=1, B=2, etc (0 = DEFAULT DRIVE) */
90
    inc dl
130
    inc dl
91
    int 0x21      /* AX=sects_per_clust, BX=avail_clusts, CX=bytes_per_sect, DX=tot_clusters */
131
    int 0x21      /* AX=sects_per_clust, BX=avail_clusts, CX=bytes_per_sect, DX=tot_clusters */
92
    cmp ax, 0xffff /* AX=0xffff on error (invalid drive) */
132
    cmp ax, 0xffff /* AX=0xffff on error (invalid drive) */
93
    jne COMPUTEDF
133
    jne COMPUTEDF
94
    mov [res], 0x0f /* fill res with DOS error code 15 ("invalid drive") */
134
    mov [res], 0x0f /* fill res with DOS error code 15 ("invalid drive") */
95
    jmp DONE
135
    jmp DONE
96
 
136
 
97
    COMPUTEDF:
137
    COMPUTEDF:
98
    /* freebytes = AX * BX * CX */
138
    /* freebytes = AX * BX * CX */
99
    mov [sects_per_clust], ax
139
    mov [sects_per_clust], ax
100
    mov [avail_clusts], bx
140
    mov [avail_clusts], bx
101
    mov [bytes_per_sect], cx
141
    mov [bytes_per_sect], cx
102
 
142
 
103
    DONE:
143
    DONE:
104
    pop dx
144
    pop dx
105
    pop cx
145
    pop cx
106
    pop bx
146
    pop bx
107
    pop ax
147
    pop ax
108
  }
148
  }
109
 
149
 
110
  /* multiple steps to avoid uint16 overflow */
150
  /* multiple steps to avoid uint16 overflow */
111
  *freebytes = sects_per_clust;
151
  *freebytes = sects_per_clust;
112
  *freebytes *= avail_clusts;
152
  *freebytes *= avail_clusts;
113
  *freebytes *= bytes_per_sect;
153
  *freebytes *= bytes_per_sect;
114
 
154
 
115
  return(res);
155
  return(res);
116
}
156
}
117
 
157
 
118
 
158
 
119
static void dir_pagination(unsigned short *availrows) {
159
static void dir_pagination(unsigned short *availrows) {
120
  *availrows -= 1;
160
  *availrows -= 1;
121
  if (*availrows == 0) {
161
  if (*availrows == 0) {
122
    press_any_key();
162
    press_any_key();
123
    *availrows = screen_getheight() - 1;
163
    *availrows = screen_getheight() - 1;
124
  }
164
  }
125
}
165
}
126
 
166
 
127
 
167
 
128
/* parse an attr list like "Ar-hS" and fill bitfield into attrfilter_may and attrfilter_must.
168
/* parse an attr list like "Ar-hS" and fill bitfield into attrfilter_may and attrfilter_must.
129
 * /AHS   -> adds S and H to mandatory attribs ("must")
169
 * /AHS   -> adds S and H to mandatory attribs ("must")
130
 * /A-S   -> removes S from allowed attribs ("may")
170
 * /A-S   -> removes S from allowed attribs ("may")
131
 * returns non-zero on error. */
171
 * returns non-zero on error. */
132
static int dir_parse_attr_list(const char *arg, unsigned char *attrfilter_may, unsigned char *attrfilter_must) {
172
static int dir_parse_attr_list(const char *arg, unsigned char *attrfilter_may, unsigned char *attrfilter_must) {
133
  for (; *arg != 0; arg++) {
173
  for (; *arg != 0; arg++) {
134
    unsigned char curattr;
174
    unsigned char curattr;
135
    char not;
175
    char not;
136
    if (*arg == '-') {
176
    if (*arg == '-') {
137
      not = 1;
177
      not = 1;
138
      arg++;
178
      arg++;
139
    } else {
179
    } else {
140
      not = 0;
180
      not = 0;
141
    }
181
    }
142
    switch (*arg) {
182
    switch (*arg) {
143
      case 'd':
183
      case 'd':
144
      case 'D':
184
      case 'D':
145
        curattr = DOS_ATTR_DIR;
185
        curattr = DOS_ATTR_DIR;
146
        break;
186
        break;
147
      case 'r':
187
      case 'r':
148
      case 'R':
188
      case 'R':
149
        curattr = DOS_ATTR_RO;
189
        curattr = DOS_ATTR_RO;
150
        break;
190
        break;
151
      case 'a':
191
      case 'a':
152
      case 'A':
192
      case 'A':
153
        curattr = DOS_ATTR_ARC;
193
        curattr = DOS_ATTR_ARC;
154
        break;
194
        break;
155
      case 'h':
195
      case 'h':
156
      case 'H':
196
      case 'H':
157
        curattr = DOS_ATTR_HID;
197
        curattr = DOS_ATTR_HID;
158
        break;
198
        break;
159
      case 's':
199
      case 's':
160
      case 'S':
200
      case 'S':
161
        curattr = DOS_ATTR_SYS;
201
        curattr = DOS_ATTR_SYS;
162
        break;
202
        break;
163
      default:
203
      default:
164
        return(-1);
204
        return(-1);
165
    }
205
    }
166
    /* update res bitfield */
206
    /* update res bitfield */
167
    if (not) {
207
    if (not) {
168
      *attrfilter_may &= ~curattr;
208
      *attrfilter_may &= ~curattr;
169
    } else {
209
    } else {
170
      *attrfilter_must |= curattr;
210
      *attrfilter_must |= curattr;
171
    }
211
    }
172
  }
212
  }
173
  return(0);
213
  return(0);
174
}
214
}
175
 
215
 
176
 
216
 
177
/* compare attributes in a DTA node to mandatory and optional attributes. returns 1 on match, 0 otherwise */
217
/* compare attributes in a DTA node to mandatory and optional attributes. returns 1 on match, 0 otherwise */
178
static int filter_attribs(const struct DTA *dta, unsigned char attrfilter_must, unsigned char attrfilter_may) {
218
static int filter_attribs(const struct DTA *dta, unsigned char attrfilter_must, unsigned char attrfilter_may) {
179
  /* if mandatory attribs are requested, filter them now */
219
  /* if mandatory attribs are requested, filter them now */
180
  if ((attrfilter_must & dta->attr) != attrfilter_must) return(0);
220
  if ((attrfilter_must & dta->attr) != attrfilter_must) return(0);
181
 
221
 
182
  /* if file contains attributes that are not allowed -> skip */
222
  /* if file contains attributes that are not allowed -> skip */
183
  if ((~attrfilter_may & dta->attr) != 0) return(0);
223
  if ((~attrfilter_may & dta->attr) != 0) return(0);
184
 
224
 
185
  return(1);
225
  return(1);
186
}
226
}
187
 
227
 
188
 
228
 
189
static struct {
229
static struct {
190
  struct TINYDTA far *dtabuf_root;
230
  struct TINYDTA far *dtabuf_root;
191
  char order[8]; /* GNESD values (ucase = lower first ; lcase = higher first) */
231
  char order[8]; /* GNESD values (ucase = lower first ; lcase = higher first) */
192
  unsigned char sortownia[256]; /* collation table (used for NLS-aware sorts) */
232
  unsigned char sortownia[256]; /* collation table (used for NLS-aware sorts) */
193
} glob_sortcmp_dat;
233
} glob_sortcmp_dat;
194
 
234
 
195
 
235
 
196
/* translates an order string like "GNE-S" into values fed into the order[]
236
/* translates an order string like "GNE-S" into values fed into the order[]
197
 * table of glob_sortcmp_dat. returns 0 on success, non-zero otherwise. */
237
 * table of glob_sortcmp_dat. returns 0 on success, non-zero otherwise. */
198
static int dir_process_order_directive(const char *ordstring) {
238
static int dir_process_order_directive(const char *ordstring) {
199
  const char *gnesd = "gnesd"; /* must be lower case */
239
  const char *gnesd = "gnesd"; /* must be lower case */
200
  int ordi, orderi = 0, i;
240
  int ordi, orderi = 0, i;
201
 
241
 
202
  /* tabula rasa */
242
  /* tabula rasa */
203
  glob_sortcmp_dat.order[0] = 0;
243
  glob_sortcmp_dat.order[0] = 0;
204
 
244
 
205
  /* /O alone is a short hand for /OGN */
245
  /* /O alone is a short hand for /OGN */
206
  if (*ordstring == 0) {
246
  if (*ordstring == 0) {
207
    glob_sortcmp_dat.order[0] = 'G';
247
    glob_sortcmp_dat.order[0] = 'G';
208
    glob_sortcmp_dat.order[1] = 'N';
248
    glob_sortcmp_dat.order[1] = 'N';
209
    glob_sortcmp_dat.order[2] = 0;
249
    glob_sortcmp_dat.order[2] = 0;
210
  }
250
  }
211
 
251
 
212
  /* stupid MSDOS compatibility ("DIR /O:GNE") */
252
  /* stupid MSDOS compatibility ("DIR /O:GNE") */
213
  if (*ordstring == ':') ordstring++;
253
  if (*ordstring == ':') ordstring++;
214
 
254
 
215
  /* parsing */
255
  /* parsing */
216
  for (ordi = 0; ordstring[ordi] != 0; ordi++) {
256
  for (ordi = 0; ordstring[ordi] != 0; ordi++) {
217
    if (ordstring[ordi] == '-') {
257
    if (ordstring[ordi] == '-') {
218
      if ((ordstring[ordi + 1] == '-') || (ordstring[ordi + 1] == 0)) return(-1);
258
      if ((ordstring[ordi + 1] == '-') || (ordstring[ordi + 1] == 0)) return(-1);
219
      continue;
259
      continue;
220
    }
260
    }
221
    if (orderi == sizeof(glob_sortcmp_dat.order)) return(-1);
261
    if (orderi == sizeof(glob_sortcmp_dat.order)) return(-1);
222
 
262
 
223
    for (i = 0; gnesd[i] != 0; i++) {
263
    for (i = 0; gnesd[i] != 0; i++) {
224
      if ((ordstring[ordi] | 32) == gnesd[i]) { /* | 32 is lcase-ing the char */
264
      if ((ordstring[ordi] | 32) == gnesd[i]) { /* | 32 is lcase-ing the char */
225
        if ((ordi > 0) && (ordstring[ordi - 1] == '-')) {
265
        if ((ordi > 0) && (ordstring[ordi - 1] == '-')) {
226
          glob_sortcmp_dat.order[orderi] = gnesd[i];
266
          glob_sortcmp_dat.order[orderi] = gnesd[i];
227
        } else {
267
        } else {
228
          glob_sortcmp_dat.order[orderi] = gnesd[i] ^ 32;
268
          glob_sortcmp_dat.order[orderi] = gnesd[i] ^ 32;
229
        }
269
        }
230
        orderi++;
270
        orderi++;
231
        break;
271
        break;
232
      }
272
      }
233
    }
273
    }
234
    if (gnesd[i] == 0) return(-1);
274
    if (gnesd[i] == 0) return(-1);
235
  }
275
  }
236
 
276
 
237
  return(0);
277
  return(0);
238
}
278
}
239
 
279
 
240
 
280
 
241
static int sortcmp(const void *dtaid1, const void *dtaid2) {
281
static int sortcmp(const void *dtaid1, const void *dtaid2) {
242
  struct TINYDTA far *dta1 = &(glob_sortcmp_dat.dtabuf_root[*((unsigned short *)dtaid1)]);
282
  struct TINYDTA far *dta1 = &(glob_sortcmp_dat.dtabuf_root[*((unsigned short *)dtaid1)]);
243
  struct TINYDTA far *dta2 = &(glob_sortcmp_dat.dtabuf_root[*((unsigned short *)dtaid2)]);
283
  struct TINYDTA far *dta2 = &(glob_sortcmp_dat.dtabuf_root[*((unsigned short *)dtaid2)]);
244
  char *ordconf = glob_sortcmp_dat.order;
284
  char *ordconf = glob_sortcmp_dat.order;
245
 
285
 
246
  /* debug stuff
286
  /* debug stuff
247
  {
287
  {
248
    int i;
288
    int i;
249
    printf("%lu vs %lu | ", dta1->size, dta2->size);
289
    printf("%lu vs %lu | ", dta1->size, dta2->size);
250
    for (i = 0; dta1->fname[i] != 0; i++) printf("%c", dta1->fname[i]);
290
    for (i = 0; dta1->fname[i] != 0; i++) printf("%c", dta1->fname[i]);
251
    printf(" vs ");
291
    printf(" vs ");
252
    for (i = 0; dta2->fname[i] != 0; i++) printf("%c", dta2->fname[i]);
292
    for (i = 0; dta2->fname[i] != 0; i++) printf("%c", dta2->fname[i]);
253
    printf("\n");
293
    printf("\n");
254
  } */
294
  } */
255
 
295
 
256
  for (;;) {
296
  for (;;) {
257
    int r = -1;
297
    int r = -1;
258
    if (*ordconf & 32) r = 1;
298
    if (*ordconf & 32) r = 1;
259
 
299
 
260
    switch (*ordconf | 32) {
300
    switch (*ordconf | 32) {
261
      case 'g': /* sort by type (directories first, then files) */
301
      case 'g': /* sort by type (directories first, then files) */
262
        if ((dta1->time_sec2 & DOS_ATTR_DIR) > (dta2->time_sec2 & DOS_ATTR_DIR)) return(0 - r);
302
        if ((dta1->time_sec2 & DOS_ATTR_DIR) > (dta2->time_sec2 & DOS_ATTR_DIR)) return(0 - r);
263
        if ((dta1->time_sec2 & DOS_ATTR_DIR) < (dta2->time_sec2 & DOS_ATTR_DIR)) return(r);
303
        if ((dta1->time_sec2 & DOS_ATTR_DIR) < (dta2->time_sec2 & DOS_ATTR_DIR)) return(r);
264
        break;
304
        break;
265
      case ' ': /* default (last resort) sort: by name */
305
      case ' ': /* default (last resort) sort: by name */
266
      case 'e': /* sort by extension */
306
      case 'e': /* sort by extension */
267
      case 'n': /* sort by filename */
307
      case 'n': /* sort by filename */
268
      {
308
      {
269
        const char far *f1 = dta1->fname;
309
        const char far *f1 = dta1->fname;
270
        const char far *f2 = dta2->fname;
310
        const char far *f2 = dta2->fname;
271
        int i, limit = 12;
311
        int i, limit = 12;
272
        /* special handling for '.' and '..' entries */
312
        /* special handling for '.' and '..' entries */
273
        if ((f1[0] == '.') && (f2[0] != '.')) return(0 - r);
313
        if ((f1[0] == '.') && (f2[0] != '.')) return(0 - r);
274
        if ((f2[0] == '.') && (f1[0] != '.')) return(r);
314
        if ((f2[0] == '.') && (f1[0] != '.')) return(r);
275
 
315
 
276
        if ((*ordconf | 32) == 'e') {
316
        if ((*ordconf | 32) == 'e') {
277
          /* fast-forward to extension or end of filename */
317
          /* fast-forward to extension or end of filename */
278
          while ((*f1 != 0) && (*f1 != '.')) f1++;
318
          while ((*f1 != 0) && (*f1 != '.')) f1++;
279
          while ((*f2 != 0) && (*f2 != '.')) f2++;
319
          while ((*f2 != 0) && (*f2 != '.')) f2++;
280
          limit = 4; /* TINYDTA structs are not nul-terminated */
320
          limit = 4; /* TINYDTA structs are not nul-terminated */
281
        }
321
        }
282
        /* cmp */
322
        /* cmp */
283
        for (i = 0; i < limit; i++) {
323
        for (i = 0; i < limit; i++) {
284
          if ((glob_sortcmp_dat.sortownia[(unsigned char)(*f1)]) < (glob_sortcmp_dat.sortownia[(unsigned char)(*f2)])) return(0 - r);
324
          if ((glob_sortcmp_dat.sortownia[(unsigned char)(*f1)]) < (glob_sortcmp_dat.sortownia[(unsigned char)(*f2)])) return(0 - r);
285
          if ((glob_sortcmp_dat.sortownia[(unsigned char)(*f1)]) > (glob_sortcmp_dat.sortownia[(unsigned char)(*f2)])) return(r);
325
          if ((glob_sortcmp_dat.sortownia[(unsigned char)(*f1)]) > (glob_sortcmp_dat.sortownia[(unsigned char)(*f2)])) return(r);
286
          if (*f1 == 0) break;
326
          if (*f1 == 0) break;
287
          f1++;
327
          f1++;
288
          f2++;
328
          f2++;
289
        }
329
        }
290
      }
330
      }
291
        break;
331
        break;
292
      case 's': /* sort by size */
332
      case 's': /* sort by size */
293
        if (dta1->size > dta2->size) return(r);
333
        if (dta1->size > dta2->size) return(r);
294
        if (dta1->size < dta2->size) return(0 - r);
334
        if (dta1->size < dta2->size) return(0 - r);
295
        break;
335
        break;
296
      case 'd': /* sort by date */
336
      case 'd': /* sort by date */
297
        if (dta1->date_yr < dta2->date_yr) return(0 - r);
337
        if (dta1->date_yr < dta2->date_yr) return(0 - r);
298
        if (dta1->date_yr > dta2->date_yr) return(r);
338
        if (dta1->date_yr > dta2->date_yr) return(r);
299
        if (dta1->date_mo < dta2->date_mo) return(0 - r);
339
        if (dta1->date_mo < dta2->date_mo) return(0 - r);
300
        if (dta1->date_mo > dta2->date_mo) return(r);
340
        if (dta1->date_mo > dta2->date_mo) return(r);
301
        if (dta1->date_dy < dta2->date_dy) return(0 - r);
341
        if (dta1->date_dy < dta2->date_dy) return(0 - r);
302
        if (dta1->date_dy > dta2->date_dy) return(r);
342
        if (dta1->date_dy > dta2->date_dy) return(r);
303
        if (dta1->time_hour < dta2->time_hour) return(0 - r);
343
        if (dta1->time_hour < dta2->time_hour) return(0 - r);
304
        if (dta1->time_hour > dta2->time_hour) return(r);
344
        if (dta1->time_hour > dta2->time_hour) return(r);
305
        if (dta1->time_min < dta2->time_min) return(0 - r);
345
        if (dta1->time_min < dta2->time_min) return(0 - r);
306
        if (dta1->time_min > dta2->time_min) return(r);
346
        if (dta1->time_min > dta2->time_min) return(r);
307
        break;
347
        break;
308
    }
348
    }
309
 
349
 
310
    if (*ordconf == 0) break;
350
    if (*ordconf == 0) break;
311
    ordconf++;
351
    ordconf++;
312
  }
352
  }
313
 
353
 
314
  return(0);
354
  return(0);
315
}
355
}
316
 
356
 
317
 
357
 
318
#define DIR_ATTR_DEFAULT (DOS_ATTR_RO | DOS_ATTR_DIR | DOS_ATTR_ARC)
358
#define DIR_ATTR_DEFAULT (DOS_ATTR_RO | DOS_ATTR_DIR | DOS_ATTR_ARC)
319
 
359
 
320
struct dirrequest {
360
struct dirrequest {
321
  unsigned char attrfilter_may;
361
  unsigned char attrfilter_may;
322
  unsigned char attrfilter_must;
362
  unsigned char attrfilter_must;
323
  const char *filespecptr;
363
  const char *filespecptr;
324
 
364
 
325
  #define DIR_FLAG_PAUSE  1
365
  #define DIR_FLAG_PAUSE  1
326
  #define DIR_FLAG_RECUR  4
366
  #define DIR_FLAG_RECUR  4
327
  #define DIR_FLAG_LCASE  8
367
  #define DIR_FLAG_LCASE  8
328
  #define DIR_FLAG_SORT  16
368
  #define DIR_FLAG_SORT  16
329
  unsigned char flags;
369
  unsigned char flags;
330
 
370
 
331
  #define DIR_OUTPUT_NORM 1
371
  #define DIR_OUTPUT_NORM 1
332
  #define DIR_OUTPUT_WIDE 2
372
  #define DIR_OUTPUT_WIDE 2
333
  #define DIR_OUTPUT_BARE 3
373
  #define DIR_OUTPUT_BARE 3
334
  unsigned char format;
374
  unsigned char format;
335
};
375
};
336
 
376
 
337
 
377
 
338
static int dir_parse_cmdline(struct dirrequest *req, const char **argv) {
378
static int dir_parse_cmdline(struct dirrequest *req, const char **argv) {
339
  for (; *argv != NULL; argv++) {
379
  for (; *argv != NULL; argv++) {
340
    if (*argv[0] == '/') {
380
    if (*argv[0] == '/') {
341
      const char *arg = *argv + 1;
381
      const char *arg = *argv + 1;
342
      char neg = 0;
382
      char neg = 0;
343
      /* detect negations and get actual argument */
383
      /* detect negations and get actual argument */
344
      if (*arg == '-') {
384
      if (*arg == '-') {
345
        neg = 1;
385
        neg = 1;
346
        arg++;
386
        arg++;
347
      }
387
      }
348
      /* */
388
      /* */
349
      switch (*arg) {
389
      switch (*arg) {
350
        case 'a':
390
        case 'a':
351
        case 'A':
391
        case 'A':
352
          arg++;
392
          arg++;
353
          /* preset defaults */
393
          /* preset defaults */
354
          req->attrfilter_may = DIR_ATTR_DEFAULT;
394
          req->attrfilter_may = DIR_ATTR_DEFAULT;
355
          req->attrfilter_must = 0;
395
          req->attrfilter_must = 0;
356
          /* /-A only allowed without further parameters (used to cancel possible previous /Asmth) */
396
          /* /-A only allowed without further parameters (used to cancel possible previous /Asmth) */
357
          if (neg) {
397
          if (neg) {
358
            if (*arg != 0) {
398
            if (*arg != 0) {
359
              nls_outputnl_err(0, 2); /* invalid switch */
399
              nls_outputnl_err(0, 2); /* invalid switch */
360
              return(-1);
400
              return(-1);
361
            }
401
            }
362
          } else {
402
          } else {
363
            /* skip colon if present */
403
            /* skip colon if present */
364
            if (*arg == ':') arg++;
404
            if (*arg == ':') arg++;
365
            /* start with "allow everything" */
405
            /* start with "allow everything" */
366
            req->attrfilter_may = (DOS_ATTR_ARC | DOS_ATTR_DIR | DOS_ATTR_HID | DOS_ATTR_SYS | DOS_ATTR_RO);
406
            req->attrfilter_may = (DOS_ATTR_ARC | DOS_ATTR_DIR | DOS_ATTR_HID | DOS_ATTR_SYS | DOS_ATTR_RO);
367
            if (dir_parse_attr_list(arg, &(req->attrfilter_may), &(req->attrfilter_must)) != 0) {
407
            if (dir_parse_attr_list(arg, &(req->attrfilter_may), &(req->attrfilter_must)) != 0) {
368
              nls_outputnl_err(0, 3); /* invalid parameter format */
408
              nls_outputnl_err(0, 3); /* invalid parameter format */
369
              return(-1);
409
              return(-1);
370
            }
410
            }
371
          }
411
          }
372
          break;
412
          break;
373
        case 'b':
413
        case 'b':
374
        case 'B':
414
        case 'B':
375
          req->format = DIR_OUTPUT_BARE;
415
          req->format = DIR_OUTPUT_BARE;
376
          break;
416
          break;
377
        case 'l':
417
        case 'l':
378
        case 'L':
418
        case 'L':
379
          req->flags |= DIR_FLAG_LCASE;
419
          req->flags |= DIR_FLAG_LCASE;
380
          break;
420
          break;
381
        case 'o':
421
        case 'o':
382
        case 'O':
422
        case 'O':
383
          if (neg) {
423
          if (neg) {
384
            req->flags &= (0xff ^ DIR_FLAG_SORT);
424
            req->flags &= (0xff ^ DIR_FLAG_SORT);
385
            break;
425
            break;
386
          }
426
          }
387
          if (dir_process_order_directive(arg+1) != 0) {
427
          if (dir_process_order_directive(arg+1) != 0) {
388
            nls_output_err(0, 3); /* invalid parameter format */
428
            nls_output_err(0, 3); /* invalid parameter format */
389
            output(": ");
429
            output(": ");
390
            outputnl(arg);
430
            outputnl(arg);
391
            return(-1);
431
            return(-1);
392
          }
432
          }
393
          req->flags |= DIR_FLAG_SORT;
433
          req->flags |= DIR_FLAG_SORT;
394
          break;
434
          break;
395
        case 'p':
435
        case 'p':
396
        case 'P':
436
        case 'P':
397
          req->flags |= DIR_FLAG_PAUSE;
437
          req->flags |= DIR_FLAG_PAUSE;
398
          if (neg) req->flags &= (0xff ^ DIR_FLAG_PAUSE);
438
          if (neg) req->flags &= (0xff ^ DIR_FLAG_PAUSE);
399
          break;
439
          break;
400
        case 's':
440
        case 's':
401
        case 'S':
441
        case 'S':
402
          /* TODO */
442
          /* TODO */
403
          outputnl("/S NOT IMPLEMENTED YET");
443
          outputnl("/S NOT IMPLEMENTED YET");
404
          return(-1);
444
          return(-1);
405
          break;
445
          break;
406
        case 'w':
446
        case 'w':
407
        case 'W':
447
        case 'W':
408
          req->format = DIR_OUTPUT_WIDE;
448
          req->format = DIR_OUTPUT_WIDE;
409
          break;
449
          break;
410
        default:
450
        default:
411
          nls_outputnl_err(0, 2); /* invalid switch */
451
          nls_outputnl_err(0, 2); /* invalid switch */
412
          return(-1);
452
          return(-1);
413
      }
453
      }
414
    } else {  /* filespec */
454
    } else {  /* filespec */
415
      if (req->filespecptr != NULL) {
455
      if (req->filespecptr != NULL) {
416
        nls_outputnl_err(0, 4); /* too many parameters */
456
        nls_outputnl_err(0, 4); /* too many parameters */
417
        return(-1);
457
        return(-1);
418
      }
458
      }
419
      req->filespecptr = *argv;
459
      req->filespecptr = *argv;
420
    }
460
    }
421
  }
461
  }
422
 
462
 
423
  return(0);
463
  return(0);
424
}
464
}
425
 
465
 
426
 
466
 
427
static enum cmd_result cmd_dir(struct cmd_funcparam *p) {
467
static enum cmd_result cmd_dir(struct cmd_funcparam *p) {
428
  struct DTA *dta = (void *)0x80; /* set DTA to its default location at 80h in PSP */
468
  struct DTA *dta = (void *)0x80; /* set DTA to its default location at 80h in PSP */
429
  struct TINYDTA far *dtabuf = NULL; /* used to buffer results when sorting is enabled */
469
  struct TINYDTA far *dtabuf = NULL; /* used to buffer results when sorting is enabled */
430
  unsigned short dtabufcount = 0;
470
  unsigned short dtabufcount = 0;
431
  unsigned short i;
471
  unsigned short i;
432
  unsigned short availrows;  /* counter of available rows on display (used for /P) */
472
  unsigned short availrows;  /* counter of available rows on display (used for /P) */
433
  unsigned short screenw = screen_getwidth();
473
  unsigned short screenw = screen_getwidth();
434
  unsigned short wcols = screenw / WCOLWIDTH; /* number of columns in wide mode */
474
  unsigned short wcols = screenw / WCOLWIDTH; /* number of columns in wide mode */
435
  unsigned char wcolcount;
475
  unsigned char wcolcount;
436
  struct {
476
  struct {
437
    struct nls_patterns nls;
477
    struct nls_patterns nls;
438
    char buff64[64];
478
    char buff64[64];
439
    char path[128];
479
    char path[128];
440
    unsigned short orderidx[65535 / sizeof(struct TINYDTA)];
480
    unsigned short orderidx[65535 / sizeof(struct TINYDTA)];
441
  } *buf = (void *)(p->BUFFER);
481
  } *buf = (void *)(p->BUFFER);
442
  unsigned long summary_fcount = 0;
482
  unsigned long summary_fcount = 0;
443
  unsigned long summary_totsz = 0;
483
  unsigned long summary_totsz = 0;
444
  unsigned char drv = 0;
484
  unsigned char drv = 0;
445
  struct dirrequest req;
485
  struct dirrequest req;
446
 
486
 
447
  /* make sure there's no risk of buffer overflow */
487
  /* make sure there's no risk of buffer overflow */
448
  if (sizeof(buf) > p->BUFFERSZ) {
488
  if (sizeof(buf) > p->BUFFERSZ) {
449
    outputnl("INTERNAL MEM ERROR IN " __FILE__);
489
    outputnl("INTERNAL MEM ERROR IN " __FILE__);
450
    return(CMD_FAIL);
490
    return(CMD_FAIL);
451
  }
491
  }
452
 
492
 
453
  if (cmd_ishlp(p)) {
493
  if (cmd_ishlp(p)) {
454
    nls_outputnl(37,0); /* "Displays a list of files and subdirectories in a directory" */
494
    nls_outputnl(37,0); /* "Displays a list of files and subdirectories in a directory" */
455
    outputnl("");
495
    outputnl("");
456
    nls_outputnl(37,1); /* "DIR [drive:][path][filename] [/P] [/W] [/A[:]attributes] [/O[[:]sortorder]] [/S] [/B] [/L]" */
496
    nls_outputnl(37,1); /* "DIR [drive:][path][filename] [/P] [/W] [/A[:]attributes] [/O[[:]sortorder]] [/S] [/B] [/L]" */
457
    outputnl("");
497
    outputnl("");
458
    nls_outputnl(37,2); /* "/P Pauses after each screenful of information" */
498
    nls_outputnl(37,2); /* "/P Pauses after each screenful of information" */
459
    nls_outputnl(37,3); /* "/W Uses wide list format" */
499
    nls_outputnl(37,3); /* "/W Uses wide list format" */
460
    outputnl("");
500
    outputnl("");
461
    nls_outputnl(37,4); /* "/A Displays files with specified attributes:" */
501
    nls_outputnl(37,4); /* "/A Displays files with specified attributes:" */
462
    nls_outputnl(37,5); /* "    D Directories            R Read-only files        H Hidden files" */
502
    nls_outputnl(37,5); /* "    D Directories            R Read-only files        H Hidden files" */
463
    nls_outputnl(37,6); /* "    A Ready for archiving    S System files           - prefix meaning "not"" */
503
    nls_outputnl(37,6); /* "    A Ready for archiving    S System files           - prefix meaning "not"" */
464
    outputnl("");
504
    outputnl("");
465
    nls_outputnl(37,7); /* "/O List files in sorted order:" */
505
    nls_outputnl(37,7); /* "/O List files in sorted order:" */
466
    nls_outputnl(37,8); /* "    N by name                S by size                E by extension" */
506
    nls_outputnl(37,8); /* "    N by name                S by size                E by extension" */
467
    nls_outputnl(37,9); /* "    D by date                G group dirs first       - prefix to reverse order" */
507
    nls_outputnl(37,9); /* "    D by date                G group dirs first       - prefix to reverse order" */
468
    outputnl("");
508
    outputnl("");
469
    nls_outputnl(37,10); /* "/S Displays files in specified directory and all subdirectories" */
509
    nls_outputnl(37,10); /* "/S Displays files in specified directory and all subdirectories" */
470
    nls_outputnl(37,11); /* "/B Uses bare format (no heading information or summary)" */
510
    nls_outputnl(37,11); /* "/B Uses bare format (no heading information or summary)" */
471
    nls_outputnl(37,12); /* "/L Uses lowercases" */
511
    nls_outputnl(37,12); /* "/L Uses lowercases" */
472
    return(CMD_OK);
512
    return(CMD_OK);
473
  }
513
  }
474
 
514
 
475
  /* zero out glob_sortcmp_dat and init the collation table */
515
  /* zero out glob_sortcmp_dat and init the collation table */
476
  bzero(&glob_sortcmp_dat, sizeof(glob_sortcmp_dat));
516
  bzero(&glob_sortcmp_dat, sizeof(glob_sortcmp_dat));
477
  for (i = 0; i < 256; i++) {
517
  for (i = 0; i < 256; i++) {
478
    glob_sortcmp_dat.sortownia[i] = i;
518
    glob_sortcmp_dat.sortownia[i] = i;
479
    /* sorting should be case-insensitive */
519
    /* sorting should be case-insensitive */
480
    if ((i >= 'A') && (i <= 'Z')) glob_sortcmp_dat.sortownia[i] |= 32;
520
    if ((i >= 'A') && (i <= 'Z')) glob_sortcmp_dat.sortownia[i] |= 32;
481
  }
521
  }
482
 
522
 
483
  /* try to replace (or complement) my naive collation table with an NLS-aware
523
  /* try to replace (or complement) my naive collation table with an NLS-aware
484
   * version provided by the kernel (or NLSFUNC) */
524
   * version provided by the kernel (or NLSFUNC)
-
 
525
   * do this ONLY for COUNTRY codes that are higher than 1, because zero is
-
 
526
   * invalid and value 1 can mean either "USA" or "undefined".
-
 
527
   * ref: https://github.com/SvarDOS/bugz/issues/68 */
485
  {
528
  if (dir_cur_country() > 1) {
486
    _Packed struct nlsseqtab {
529
    _Packed struct nlsseqtab {
487
      unsigned char id;
530
      unsigned char id;
488
      unsigned short taboff;
531
      unsigned short taboff;
489
      unsigned short tabseg;
532
      unsigned short tabseg;
490
    } collat;
533
    } collat;
491
    void *colptr = &collat;
534
    void *colptr = &collat;
492
    unsigned char errflag = 1;
535
    unsigned char errflag = 1;
493
    _asm {
536
    _asm {
494
      push ax
537
      push ax
495
      push bx
538
      push bx
496
      push cx
539
      push cx
497
      push dx
540
      push dx
498
      push di
541
      push di
499
      push es
542
      push es
500
 
543
 
501
      mov ax, 0x6506  /* DOS 3.3+ - Get collating sequence table */
544
      mov ax, 0x6506  /* DOS 3.3+ - Get collating sequence table */
502
      mov bx, 0xffff  /* code page, FFFFh = "current" */
545
      mov bx, 0xffff  /* code page, FFFFh = "current" */
503
      mov cx, 5       /* size of buffer at ES:DI */
546
      mov cx, 5       /* size of buffer at ES:DI */
504
      mov dx, 0xffff  /* country id, FFFFh = "current" */
547
      mov dx, 0xffff  /* country id, FFFFh = "current" */
505
      push ds
548
      push ds
506
      pop es          /* ES:DI = address of buffer for the 5-bytes struct */
549
      pop es          /* ES:DI = address of buffer for the 5-bytes struct */
507
      mov di, colptr
550
      mov di, colptr
508
      int 0x21
551
      int 0x21
509
      jc FAIL
552
      jc FAIL
510
      xor al, al
553
      xor al, al
511
      mov errflag, al
554
      mov errflag, al
512
      FAIL:
555
      FAIL:
513
 
556
 
514
      pop es
557
      pop es
515
      pop di
558
      pop di
516
      pop dx
559
      pop dx
517
      pop cx
560
      pop cx
518
      pop bx
561
      pop bx
519
      pop ax
562
      pop ax
520
    }
563
    }
521
 
564
 
522
    if ((errflag == 0) && (collat.id == 6)) {
565
    if ((errflag == 0) && (collat.id == 6)) {
523
      unsigned char far *ptr = MK_FP(collat.tabseg, collat.taboff);
566
      unsigned char far *ptr = MK_FP(collat.tabseg, collat.taboff);
524
      unsigned short count = *(unsigned short far *)ptr;
567
      unsigned short count = *(unsigned short far *)ptr;
525
      /* printf("NLS AT %04X:%04X (%u elements)\n", collat.tabseg, collat.taboff, count); */
568
      /* printf("NLS AT %04X:%04X (%u elements)\n", collat.tabseg, collat.taboff, count); */
526
      if (count <= 256) { /* you never know */
569
      if (count <= 256) { /* you never know */
527
        ptr += 2; /* skip the count header */
570
        ptr += 2; /* skip the count header */
528
        for (i = 0; i < count; i++) {
571
        for (i = 0; i < count; i++) {
529
          glob_sortcmp_dat.sortownia[i] = ptr[i];
572
          glob_sortcmp_dat.sortownia[i] = ptr[i];
530
        }
573
        }
531
      }
574
      }
532
    }
575
    }
533
  }
576
  }
534
 
577
 
535
  i = nls_getpatterns(&(buf->nls));
578
  i = nls_getpatterns(&(buf->nls));
536
  if (i != 0) nls_outputnl_doserr(i);
579
  if (i != 0) nls_outputnl_doserr(i);
537
 
580
 
538
  /* disable usage of thousands separator on narrow screens */
581
  /* disable usage of thousands separator on narrow screens */
539
  if (screenw < 80) buf->nls.thousep[0] = 0;
582
  if (screenw < 80) buf->nls.thousep[0] = 0;
540
 
583
 
541
  /*** PARSING COMMAND LINE STARTS *******************************************/
584
  /*** PARSING COMMAND LINE STARTS *******************************************/
542
 
585
 
543
  /* init req with some defaults */
586
  /* init req with some defaults */
544
  bzero(&req, sizeof(req));
587
  bzero(&req, sizeof(req));
545
  req.attrfilter_may = DIR_ATTR_DEFAULT;
588
  req.attrfilter_may = DIR_ATTR_DEFAULT;
546
  req.format = DIR_OUTPUT_NORM;
589
  req.format = DIR_OUTPUT_NORM;
547
 
590
 
548
  /* process DIRCMD first (so it can be overidden by user's cmdline) */
591
  /* process DIRCMD first (so it can be overidden by user's cmdline) */
549
  {
592
  {
550
  const char far *dircmd = env_lookup_val(p->env_seg, "DIRCMD");
593
  const char far *dircmd = env_lookup_val(p->env_seg, "DIRCMD");
551
  if (dircmd != NULL) {
594
  if (dircmd != NULL) {
552
    const char *argvptrs[32];
595
    const char *argvptrs[32];
553
    cmd_explode(buf->buff64, dircmd, argvptrs);
596
    cmd_explode(buf->buff64, dircmd, argvptrs);
554
    if ((dir_parse_cmdline(&req, argvptrs) != 0) || (req.filespecptr != NULL)) {
597
    if ((dir_parse_cmdline(&req, argvptrs) != 0) || (req.filespecptr != NULL)) {
555
      nls_output(255, 10);/* bad environment */
598
      nls_output(255, 10);/* bad environment */
556
      output(" - ");
599
      output(" - ");
557
      outputnl("DIRCMD");
600
      outputnl("DIRCMD");
558
      return(CMD_FAIL);
601
      return(CMD_FAIL);
559
    }
602
    }
560
  }
603
  }
561
  }
604
  }
562
 
605
 
563
  /* parse user's command line */
606
  /* parse user's command line */
564
  if (dir_parse_cmdline(&req, p->argv) != 0) return(CMD_FAIL);
607
  if (dir_parse_cmdline(&req, p->argv) != 0) return(CMD_FAIL);
565
 
608
 
566
  /* if no filespec provided, then it's about the current directory */
609
  /* if no filespec provided, then it's about the current directory */
567
  if (req.filespecptr == NULL) req.filespecptr = ".";
610
  if (req.filespecptr == NULL) req.filespecptr = ".";
568
 
611
 
569
  /*** PARSING COMMAND LINE DONE *********************************************/
612
  /*** PARSING COMMAND LINE DONE *********************************************/
570
 
613
 
571
 
614
 
572
  availrows = screen_getheight() - 2;
615
  availrows = screen_getheight() - 2;
573
 
616
 
574
  /* special case: "DIR drive:" (truename() fails on "C:" under MS-DOS 6.0) */
617
  /* special case: "DIR drive:" (truename() fails on "C:" under MS-DOS 6.0) */
575
  if ((req.filespecptr[0] != 0) && (req.filespecptr[1] == ':') && (req.filespecptr[2] == 0)) {
618
  if ((req.filespecptr[0] != 0) && (req.filespecptr[1] == ':') && (req.filespecptr[2] == 0)) {
576
    if ((req.filespecptr[0] >= 'a') && (req.filespecptr[0] <= 'z')) {
619
    if ((req.filespecptr[0] >= 'a') && (req.filespecptr[0] <= 'z')) {
577
      buf->path[0] = req.filespecptr[0] - ('a' - 1);
620
      buf->path[0] = req.filespecptr[0] - ('a' - 1);
578
    } else {
621
    } else {
579
      buf->path[0] = req.filespecptr[0] - ('A' - 1);
622
      buf->path[0] = req.filespecptr[0] - ('A' - 1);
580
    }
623
    }
581
    i = curpathfordrv(buf->path, buf->path[0]);
624
    i = curpathfordrv(buf->path, buf->path[0]);
582
  } else {
625
  } else {
583
    i = file_truename(req.filespecptr, buf->path);
626
    i = file_truename(req.filespecptr, buf->path);
584
  }
627
  }
585
  if (i != 0) {
628
  if (i != 0) {
586
    nls_outputnl_doserr(i);
629
    nls_outputnl_doserr(i);
587
    return(CMD_FAIL);
630
    return(CMD_FAIL);
588
  }
631
  }
589
 
632
 
590
  if (req.format != DIR_OUTPUT_BARE) {
633
  if (req.format != DIR_OUTPUT_BARE) {
591
    drv = buf->path[0];
634
    drv = buf->path[0];
592
    if (drv >= 'a') {
635
    if (drv >= 'a') {
593
      drv -= 'a';
636
      drv -= 'a';
594
    } else {
637
    } else {
595
      drv -= 'A';
638
      drv -= 'A';
596
    }
639
    }
597
    cmd_vol_internal(drv, buf->buff64);
640
    cmd_vol_internal(drv, buf->buff64);
598
    sprintf(buf->buff64, svarlang_str(37,20)/*"Directory of %s"*/, buf->path);
641
    sprintf(buf->buff64, svarlang_str(37,20)/*"Directory of %s"*/, buf->path);
599
    /* trim at first '?', if any */
642
    /* trim at first '?', if any */
600
    for (i = 0; buf->buff64[i] != 0; i++) if (buf->buff64[i] == '?') buf->buff64[i] = 0;
643
    for (i = 0; buf->buff64[i] != 0; i++) if (buf->buff64[i] == '?') buf->buff64[i] = 0;
601
    outputnl(buf->buff64);
644
    outputnl(buf->buff64);
602
    outputnl("");
645
    outputnl("");
603
    availrows -= 3;
646
    availrows -= 3;
604
  }
647
  }
605
 
648
 
606
  /* if dir: append a backslash (also get its len) */
649
  /* if dir: append a backslash (also get its len) */
607
  i = path_appendbkslash_if_dir(buf->path);
650
  i = path_appendbkslash_if_dir(buf->path);
608
 
651
 
609
  /* if ends with a \ then append ????????.??? */
652
  /* if ends with a \ then append ????????.??? */
610
  if (buf->path[i - 1] == '\\') strcat(buf->path, "????????.???");
653
  if (buf->path[i - 1] == '\\') strcat(buf->path, "????????.???");
611
 
654
 
612
  /* ask DOS for list of files, but only with allowed attribs */
655
  /* ask DOS for list of files, but only with allowed attribs */
613
  i = findfirst(dta, buf->path, req.attrfilter_may);
656
  i = findfirst(dta, buf->path, req.attrfilter_may);
614
  if (i != 0) {
657
  if (i != 0) {
615
    nls_outputnl_doserr(i);
658
    nls_outputnl_doserr(i);
616
    return(CMD_FAIL);
659
    return(CMD_FAIL);
617
  }
660
  }
618
 
661
 
619
  /* if sorting is involved, then let's buffer all results (and sort them) */
662
  /* if sorting is involved, then let's buffer all results (and sort them) */
620
  if (req.flags & DIR_FLAG_SORT) {
663
  if (req.flags & DIR_FLAG_SORT) {
621
    /* allocate a memory buffer - try several sizes until one succeeds */
664
    /* allocate a memory buffer - try several sizes until one succeeds */
622
    const unsigned short memsz[] = {65500, 32000, 16000, 8000, 4000, 2000, 1000, 0};
665
    const unsigned short memsz[] = {65500, 32000, 16000, 8000, 4000, 2000, 1000, 0};
623
    unsigned short max_dta_bufcount = 0;
666
    unsigned short max_dta_bufcount = 0;
624
    for (i = 0; memsz[i] != 0; i++) {
667
    for (i = 0; memsz[i] != 0; i++) {
625
      dtabuf = _fmalloc(memsz[i]);
668
      dtabuf = _fmalloc(memsz[i]);
626
      if (dtabuf != NULL) break;
669
      if (dtabuf != NULL) break;
627
    }
670
    }
628
 
671
 
629
    if (dtabuf == NULL) {
672
    if (dtabuf == NULL) {
630
      nls_outputnl_doserr(8); /* out of memory */
673
      nls_outputnl_doserr(8); /* out of memory */
631
      return(CMD_FAIL);
674
      return(CMD_FAIL);
632
    }
675
    }
633
 
676
 
634
    /* remember the address so I can free it afterwards */
677
    /* remember the address so I can free it afterwards */
635
    glob_sortcmp_dat.dtabuf_root = dtabuf;
678
    glob_sortcmp_dat.dtabuf_root = dtabuf;
636
 
679
 
637
    /* compute the amount of DTAs I can buffer */
680
    /* compute the amount of DTAs I can buffer */
638
    max_dta_bufcount = memsz[i] / sizeof(struct TINYDTA);
681
    max_dta_bufcount = memsz[i] / sizeof(struct TINYDTA);
639
    /* printf("max_dta_bufcount = %u\n", max_dta_bufcount); */
682
    /* printf("max_dta_bufcount = %u\n", max_dta_bufcount); */
640
 
683
 
641
    do {
684
    do {
642
      /* filter out files with uninteresting attributes */
685
      /* filter out files with uninteresting attributes */
643
      if (filter_attribs(dta, req.attrfilter_must, req.attrfilter_may) == 0) continue;
686
      if (filter_attribs(dta, req.attrfilter_must, req.attrfilter_may) == 0) continue;
644
 
687
 
645
      /* normalize "size" of directories to zero because kernel returns garbage
688
      /* normalize "size" of directories to zero because kernel returns garbage
646
       * sizes for directories which might confuse the sorting routine later */
689
       * sizes for directories which might confuse the sorting routine later */
647
      if (dta->attr & DOS_ATTR_DIR) dta->size = 0;
690
      if (dta->attr & DOS_ATTR_DIR) dta->size = 0;
648
 
691
 
649
      _fmemcpy(&(dtabuf[dtabufcount]), ((char *)dta) + 22, sizeof(struct TINYDTA));
692
      _fmemcpy(&(dtabuf[dtabufcount]), ((char *)dta) + 22, sizeof(struct TINYDTA));
650
 
693
 
651
      /* save attribs in sec field, otherwise zero it (this field is not
694
      /* save attribs in sec field, otherwise zero it (this field is not
652
       * displayed and dropping the attr field saves 2 bytes per entry) */
695
       * displayed and dropping the attr field saves 2 bytes per entry) */
653
      dtabuf[dtabufcount++].time_sec2 = (dta->attr & 31);
696
      dtabuf[dtabufcount++].time_sec2 = (dta->attr & 31);
654
 
697
 
655
      /* do I have any space left? */
698
      /* do I have any space left? */
656
      if (dtabufcount == max_dta_bufcount) {
699
      if (dtabufcount == max_dta_bufcount) {
657
        //TODO some kind of user notification might be nice here
700
        //TODO some kind of user notification might be nice here
658
        //outputnl("TOO MANY ENTRIES FOR SORTING! LIST IS UNSORTED");
701
        //outputnl("TOO MANY ENTRIES FOR SORTING! LIST IS UNSORTED");
659
        break;
702
        break;
660
      }
703
      }
661
 
704
 
662
    } while (findnext(dta) == 0);
705
    } while (findnext(dta) == 0);
663
 
706
 
664
    /* no match? kein gluck! (this can happen when filtering attribs with /A:xxx
707
    /* no match? kein gluck! (this can happen when filtering attribs with /A:xxx
665
     * because while findfirst() succeeds, all entries can be rejected) */
708
     * because while findfirst() succeeds, all entries can be rejected) */
666
    if (dtabufcount == 0) {
709
    if (dtabufcount == 0) {
667
      nls_outputnl_doserr(2); /* "File not found" */
710
      nls_outputnl_doserr(2); /* "File not found" */
668
      return(CMD_FAIL);
711
      return(CMD_FAIL);
669
    }
712
    }
670
 
713
 
671
    /* sort the list - the tricky part is that my array is a far address while
714
    /* sort the list - the tricky part is that my array is a far address while
672
     * qsort works only with near pointers, so I have to use an ugly (and
715
     * qsort works only with near pointers, so I have to use an ugly (and
673
     * global) auxiliary table */
716
     * global) auxiliary table */
674
    for (i = 0; i < dtabufcount; i++) buf->orderidx[i] = i;
717
    for (i = 0; i < dtabufcount; i++) buf->orderidx[i] = i;
675
    qsort(buf->orderidx, dtabufcount, 2, &sortcmp);
718
    qsort(buf->orderidx, dtabufcount, 2, &sortcmp);
676
 
719
 
677
    /* preload first entry (last from orderidx, since entries are sorted in reverse) */
720
    /* preload first entry (last from orderidx, since entries are sorted in reverse) */
678
    dtabufcount--;
721
    dtabufcount--;
679
    _fmemcpy(((unsigned char *)dta) + 22, &(dtabuf[buf->orderidx[dtabufcount]]), sizeof(struct TINYDTA));
722
    _fmemcpy(((unsigned char *)dta) + 22, &(dtabuf[buf->orderidx[dtabufcount]]), sizeof(struct TINYDTA));
680
    dta->attr = dtabuf[buf->orderidx[dtabufcount]].time_sec2; /* restore attr from the abused time_sec2 field */
723
    dta->attr = dtabuf[buf->orderidx[dtabufcount]].time_sec2; /* restore attr from the abused time_sec2 field */
681
  }
724
  }
682
 
725
 
683
  wcolcount = 0; /* may be used for columns counting with wide mode */
726
  wcolcount = 0; /* may be used for columns counting with wide mode */
684
 
727
 
685
  for (;;) {
728
  for (;;) {
686
 
729
 
687
    /* filter out attributes (skip if entry comes from buffer, then it was already veted) */
730
    /* filter out attributes (skip if entry comes from buffer, then it was already veted) */
688
    if (filter_attribs(dta, req.attrfilter_must, req.attrfilter_may) == 0) goto NEXT_ENTRY;
731
    if (filter_attribs(dta, req.attrfilter_must, req.attrfilter_may) == 0) goto NEXT_ENTRY;
689
 
732
 
690
    /* turn string lcase (/L) */
733
    /* turn string lcase (/L) */
691
    if (req.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... */
734
    if (req.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... */
692
 
735
 
693
    summary_fcount++;
736
    summary_fcount++;
694
    if ((dta->attr & DOS_ATTR_DIR) == 0) summary_totsz += dta->size;
737
    if ((dta->attr & DOS_ATTR_DIR) == 0) summary_totsz += dta->size;
695
 
738
 
696
    switch (req.format) {
739
    switch (req.format) {
697
      case DIR_OUTPUT_NORM:
740
      case DIR_OUTPUT_NORM:
698
        /* print fname-space-extension (unless it's "." or "..", then print as-is) */
741
        /* print fname-space-extension (unless it's "." or "..", then print as-is) */
699
        if (dta->fname[0] == '.') {
742
        if (dta->fname[0] == '.') {
700
          output(dta->fname);
743
          output(dta->fname);
701
          i = strlen(dta->fname);
744
          i = strlen(dta->fname);
702
          while (i++ < 12) output(" ");
745
          while (i++ < 12) output(" ");
703
        } else {
746
        } else {
704
          file_fname2fcb(buf->buff64, dta->fname);
747
          file_fname2fcb(buf->buff64, dta->fname);
705
          memmove(buf->buff64 + 9, buf->buff64 + 8, 4);
748
          memmove(buf->buff64 + 9, buf->buff64 + 8, 4);
706
          buf->buff64[8] = ' ';
749
          buf->buff64[8] = ' ';
707
          output(buf->buff64);
750
          output(buf->buff64);
708
        }
751
        }
709
        output(" ");
752
        output(" ");
710
        /* either <DIR> or right aligned 10-chars byte size */
753
        /* either <DIR> or right aligned 10-chars byte size */
711
        memset(buf->buff64, ' ', 10);
754
        memset(buf->buff64, ' ', 10);
712
        if (dta->attr & DOS_ATTR_DIR) {
755
        if (dta->attr & DOS_ATTR_DIR) {
713
          strcpy(buf->buff64 + 10, svarlang_str(37,21));
756
          strcpy(buf->buff64 + 10, svarlang_str(37,21));
714
        } else {
757
        } else {
715
          nls_format_number(buf->buff64 + 10, dta->size, &(buf->nls));
758
          nls_format_number(buf->buff64 + 10, dta->size, &(buf->nls));
716
        }
759
        }
717
        output(buf->buff64 + strlen(buf->buff64) - 10);
760
        output(buf->buff64 + strlen(buf->buff64) - 10);
718
        /* two spaces and NLS DATE */
761
        /* two spaces and NLS DATE */
719
        buf->buff64[0] = ' ';
762
        buf->buff64[0] = ' ';
720
        buf->buff64[1] = ' ';
763
        buf->buff64[1] = ' ';
721
        if (screenw >= 80) {
764
        if (screenw >= 80) {
722
          nls_format_date(buf->buff64 + 2, dta->date_yr + 1980, dta->date_mo, dta->date_dy, &(buf->nls));
765
          nls_format_date(buf->buff64 + 2, dta->date_yr + 1980, dta->date_mo, dta->date_dy, &(buf->nls));
723
        } else {
766
        } else {
724
          nls_format_date(buf->buff64 + 2, (dta->date_yr + 80) % 100, dta->date_mo, dta->date_dy, &(buf->nls));
767
          nls_format_date(buf->buff64 + 2, (dta->date_yr + 80) % 100, dta->date_mo, dta->date_dy, &(buf->nls));
725
        }
768
        }
726
        output(buf->buff64);
769
        output(buf->buff64);
727
 
770
 
728
        /* one space and NLS TIME */
771
        /* one space and NLS TIME */
729
        nls_format_time(buf->buff64 + 1, dta->time_hour, dta->time_min, 0xff, &(buf->nls));
772
        nls_format_time(buf->buff64 + 1, dta->time_hour, dta->time_min, 0xff, &(buf->nls));
730
        outputnl(buf->buff64);
773
        outputnl(buf->buff64);
731
        break;
774
        break;
732
 
775
 
733
      case DIR_OUTPUT_WIDE: /* display in columns of 12 chars per item */
776
      case DIR_OUTPUT_WIDE: /* display in columns of 12 chars per item */
734
        i = strlen(dta->fname);
777
        i = strlen(dta->fname);
735
        if (dta->attr & DOS_ATTR_DIR) {
778
        if (dta->attr & DOS_ATTR_DIR) {
736
          i += 2;
779
          i += 2;
737
          output("[");
780
          output("[");
738
          output(dta->fname);
781
          output(dta->fname);
739
          output("]");
782
          output("]");
740
        } else {
783
        } else {
741
          output(dta->fname);
784
          output(dta->fname);
742
        }
785
        }
743
        while (i++ < WCOLWIDTH) output(" ");
786
        while (i++ < WCOLWIDTH) output(" ");
744
        if (++wcolcount == wcols) {
787
        if (++wcolcount == wcols) {
745
          wcolcount = 0;
788
          wcolcount = 0;
746
          outputnl("");
789
          outputnl("");
747
        } else {
790
        } else {
748
          availrows++; /* wide mode is the only one that does not write one line per file */
791
          availrows++; /* wide mode is the only one that does not write one line per file */
749
        }
792
        }
750
        break;
793
        break;
751
 
794
 
752
      case DIR_OUTPUT_BARE:
795
      case DIR_OUTPUT_BARE:
753
        outputnl(dta->fname);
796
        outputnl(dta->fname);
754
        break;
797
        break;
755
    }
798
    }
756
 
799
 
757
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
800
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
758
 
801
 
759
    NEXT_ENTRY:
802
    NEXT_ENTRY:
760
    /* take next entry, either from buf or disk */
803
    /* take next entry, either from buf or disk */
761
    if (dtabufcount > 0) {
804
    if (dtabufcount > 0) {
762
      dtabufcount--;
805
      dtabufcount--;
763
      _fmemcpy(((unsigned char *)dta) + 22, &(dtabuf[buf->orderidx[dtabufcount]]), sizeof(struct TINYDTA));
806
      _fmemcpy(((unsigned char *)dta) + 22, &(dtabuf[buf->orderidx[dtabufcount]]), sizeof(struct TINYDTA));
764
      dta->attr = dtabuf[buf->orderidx[dtabufcount]].time_sec2; /* restore attr from the abused time_sec2 field */
807
      dta->attr = dtabuf[buf->orderidx[dtabufcount]].time_sec2; /* restore attr from the abused time_sec2 field */
765
    } else {
808
    } else {
766
      if (findnext(dta) != 0) break;
809
      if (findnext(dta) != 0) break;
767
    }
810
    }
768
 
811
 
769
  }
812
  }
770
 
813
 
771
  if (wcolcount != 0) {
814
  if (wcolcount != 0) {
772
    outputnl(""); /* in wide mode make sure to end on a clear row */
815
    outputnl(""); /* in wide mode make sure to end on a clear row */
773
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
816
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
774
  }
817
  }
775
 
818
 
776
  /* print out summary (unless bare output mode) */
819
  /* print out summary (unless bare output mode) */
777
  if (req.format != DIR_OUTPUT_BARE) {
820
  if (req.format != DIR_OUTPUT_BARE) {
778
    unsigned short alignpos;
821
    unsigned short alignpos;
779
    unsigned char uint32maxlen = 13; /* 13 is the max len of a 32 bit number with thousand separators (4'000'000'000) */
822
    unsigned char uint32maxlen = 13; /* 13 is the max len of a 32 bit number with thousand separators (4'000'000'000) */
780
    if (screenw < 80) uint32maxlen = 10;
823
    if (screenw < 80) uint32maxlen = 10;
781
    /* x file(s) */
824
    /* x file(s) */
782
    memset(buf->buff64, ' ', uint32maxlen);
825
    memset(buf->buff64, ' ', uint32maxlen);
783
    i = nls_format_number(buf->buff64 + uint32maxlen, summary_fcount, &(buf->nls));
826
    i = nls_format_number(buf->buff64 + uint32maxlen, summary_fcount, &(buf->nls));
784
    alignpos = sprintf(buf->buff64 + uint32maxlen + i, " %s ", svarlang_str(37,22)/*"file(s)"*/);
827
    alignpos = sprintf(buf->buff64 + uint32maxlen + i, " %s ", svarlang_str(37,22)/*"file(s)"*/);
785
    output(buf->buff64 + i);
828
    output(buf->buff64 + i);
786
    /* xxxx bytes */
829
    /* xxxx bytes */
787
    i = nls_format_number(buf->buff64 + uint32maxlen, summary_totsz, &(buf->nls));
830
    i = nls_format_number(buf->buff64 + uint32maxlen, summary_totsz, &(buf->nls));
788
    output(buf->buff64 + i + 1);
831
    output(buf->buff64 + i + 1);
789
    output(" ");
832
    output(" ");
790
    nls_outputnl(37,23); /* "bytes" */
833
    nls_outputnl(37,23); /* "bytes" */
791
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
834
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
792
    /* xxxx bytes free */
835
    /* xxxx bytes free */
793
    i = cmd_dir_df(&summary_totsz, drv);
836
    i = cmd_dir_df(&summary_totsz, drv);
794
    if (i != 0) nls_outputnl_doserr(i);
837
    if (i != 0) nls_outputnl_doserr(i);
795
    alignpos += uint32maxlen * 2;
838
    alignpos += uint32maxlen * 2;
796
    memset(buf->buff64, ' ', alignpos); /* align the freebytes value to same column as totbytes */
839
    memset(buf->buff64, ' ', alignpos); /* align the freebytes value to same column as totbytes */
797
    i = nls_format_number(buf->buff64 + alignpos, summary_totsz, &(buf->nls));
840
    i = nls_format_number(buf->buff64 + alignpos, summary_totsz, &(buf->nls));
798
    output(buf->buff64 + i + 1);
841
    output(buf->buff64 + i + 1);
799
    output(" ");
842
    output(" ");
800
    nls_outputnl(37,24); /* "bytes free" */
843
    nls_outputnl(37,24); /* "bytes free" */
801
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
844
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
802
  }
845
  }
803
 
846
 
804
  /* free the buffer memory (if used) */
847
  /* free the buffer memory (if used) */
805
  if (glob_sortcmp_dat.dtabuf_root != NULL) _ffree(glob_sortcmp_dat.dtabuf_root);
848
  if (glob_sortcmp_dat.dtabuf_root != NULL) _ffree(glob_sortcmp_dat.dtabuf_root);
806
 
849
 
807
  return(CMD_OK);
850
  return(CMD_OK);
808
}
851
}
809
 
852