Subversion Repositories SvarDOS

Rev

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

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