Subversion Repositories SvarDOS

Rev

Rev 1744 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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