Subversion Repositories SvarDOS

Rev

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

Rev 1725 Rev 1726
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
} glob_sortcmp_dat;
192
} glob_sortcmp_dat;
193
 
193
 
194
 
194
 
195
/* translates an order string like "GNE-S" into values fed into the order[]
195
/* translates an order string like "GNE-S" into values fed into the order[]
196
 * table of glob_sortcmp_dat. returns 0 on success, non-zero otherwise. */
196
 * table of glob_sortcmp_dat. returns 0 on success, non-zero otherwise. */
197
static int dir_process_order_directive(const char *ordstring) {
197
static int dir_process_order_directive(const char *ordstring) {
198
  const char *gnesd = "gnesd"; /* must be lower case */
198
  const char *gnesd = "gnesd"; /* must be lower case */
199
  int ordi, orderi = 0, i;
199
  int ordi, orderi = 0, i;
200
 
200
 
201
  /* tabula rasa */
201
  /* tabula rasa */
202
  glob_sortcmp_dat.order[0] = 0;
202
  glob_sortcmp_dat.order[0] = 0;
203
 
203
 
204
  /* /O alone is a short hand for /OGN */
204
  /* /O alone is a short hand for /OGN */
205
  if (*ordstring == 0) {
205
  if (*ordstring == 0) {
206
    glob_sortcmp_dat.order[0] = 'G';
206
    glob_sortcmp_dat.order[0] = 'G';
207
    glob_sortcmp_dat.order[1] = 'N';
207
    glob_sortcmp_dat.order[1] = 'N';
208
    glob_sortcmp_dat.order[2] = 0;
208
    glob_sortcmp_dat.order[2] = 0;
209
  }
209
  }
210
 
210
 
-
 
211
  /* stupid MSDOS compatibility ("DIR /O:GNE") */
-
 
212
  if (*ordstring == ':') ordstring++;
-
 
213
 
211
  /* parsing */
214
  /* parsing */
212
  for (ordi = 0; ordstring[ordi] != 0; ordi++) {
215
  for (ordi = 0; ordstring[ordi] != 0; ordi++) {
213
    if (ordstring[ordi] == '-') {
216
    if (ordstring[ordi] == '-') {
214
      if ((ordstring[ordi + 1] == '-') || (ordstring[ordi + 1] == 0)) return(-1);
217
      if ((ordstring[ordi + 1] == '-') || (ordstring[ordi + 1] == 0)) return(-1);
215
      continue;
218
      continue;
216
    }
219
    }
217
    if (orderi == sizeof(glob_sortcmp_dat.order)) return(-1);
220
    if (orderi == sizeof(glob_sortcmp_dat.order)) return(-1);
218
 
221
 
219
    for (i = 0; gnesd[i] != 0; i++) {
222
    for (i = 0; gnesd[i] != 0; i++) {
220
      if ((ordstring[ordi] | 32) == gnesd[i]) { /* | 32 is lcase-ing the char */
223
      if ((ordstring[ordi] | 32) == gnesd[i]) { /* | 32 is lcase-ing the char */
221
        if ((ordi > 0) && (ordstring[ordi - 1] == '-')) {
224
        if ((ordi > 0) && (ordstring[ordi - 1] == '-')) {
222
          glob_sortcmp_dat.order[orderi] = gnesd[i];
225
          glob_sortcmp_dat.order[orderi] = gnesd[i];
223
        } else {
226
        } else {
224
          glob_sortcmp_dat.order[orderi] = gnesd[i] ^ 32;
227
          glob_sortcmp_dat.order[orderi] = gnesd[i] ^ 32;
225
        }
228
        }
226
        orderi++;
229
        orderi++;
227
        break;
230
        break;
228
      }
231
      }
229
    }
232
    }
230
    if (gnesd[i] == 0) return(-1);
233
    if (gnesd[i] == 0) return(-1);
231
  }
234
  }
232
 
235
 
233
  return(0);
236
  return(0);
234
}
237
}
235
 
238
 
236
 
239
 
237
static int sortcmp(const void *dtaid1, const void *dtaid2) {
240
static int sortcmp(const void *dtaid1, const void *dtaid2) {
238
  struct TINYDTA far *dta1 = &(glob_sortcmp_dat.dtabuf_root[*((unsigned short *)dtaid1)]);
241
  struct TINYDTA far *dta1 = &(glob_sortcmp_dat.dtabuf_root[*((unsigned short *)dtaid1)]);
239
  struct TINYDTA far *dta2 = &(glob_sortcmp_dat.dtabuf_root[*((unsigned short *)dtaid2)]);
242
  struct TINYDTA far *dta2 = &(glob_sortcmp_dat.dtabuf_root[*((unsigned short *)dtaid2)]);
240
  char *ordconf = glob_sortcmp_dat.order;
243
  char *ordconf = glob_sortcmp_dat.order;
241
 
244
 
242
  /* debug stuff
245
  /* debug stuff
243
  {
246
  {
244
    int i;
247
    int i;
245
    printf("%lu vs %lu | ", dta1->size, dta2->size);
248
    printf("%lu vs %lu | ", dta1->size, dta2->size);
246
    for (i = 0; dta1->fname[i] != 0; i++) printf("%c", dta1->fname[i]);
249
    for (i = 0; dta1->fname[i] != 0; i++) printf("%c", dta1->fname[i]);
247
    printf(" vs ");
250
    printf(" vs ");
248
    for (i = 0; dta2->fname[i] != 0; i++) printf("%c", dta2->fname[i]);
251
    for (i = 0; dta2->fname[i] != 0; i++) printf("%c", dta2->fname[i]);
249
    printf("\n");
252
    printf("\n");
250
  } */
253
  } */
251
 
254
 
252
  for (;;) {
255
  for (;;) {
253
    int r = -1;
256
    int r = -1;
254
    if (*ordconf & 32) r = 1;
257
    if (*ordconf & 32) r = 1;
255
 
258
 
256
    switch (*ordconf | 32) {
259
    switch (*ordconf | 32) {
257
      case 'g': /* sort by type (directories first, then files) */
260
      case 'g': /* sort by type (directories first, then files) */
258
        if ((dta1->time_sec2 & DOS_ATTR_DIR) > (dta2->time_sec2 & DOS_ATTR_DIR)) return(0 - r);
261
        if ((dta1->time_sec2 & DOS_ATTR_DIR) > (dta2->time_sec2 & DOS_ATTR_DIR)) return(0 - r);
259
        if ((dta1->time_sec2 & DOS_ATTR_DIR) < (dta2->time_sec2 & DOS_ATTR_DIR)) return(r);
262
        if ((dta1->time_sec2 & DOS_ATTR_DIR) < (dta2->time_sec2 & DOS_ATTR_DIR)) return(r);
260
        break;
263
        break;
261
      case ' ': /* default (last resort) sort: by name */
264
      case ' ': /* default (last resort) sort: by name */
262
      case 'e': /* sort by extension */
265
      case 'e': /* sort by extension */
263
      case 'n': /* sort by filename */
266
      case 'n': /* sort by filename */
264
      {
267
      {
265
        const char far *f1 = dta1->fname;
268
        const char far *f1 = dta1->fname;
266
        const char far *f2 = dta2->fname;
269
        const char far *f2 = dta2->fname;
267
        int i, limit = 12;
270
        int i, limit = 12;
268
        /* special handling for '.' and '..' entries */
271
        /* special handling for '.' and '..' entries */
269
        if ((f1[0] == '.') && (f2[0] != '.')) return(0 - r);
272
        if ((f1[0] == '.') && (f2[0] != '.')) return(0 - r);
270
        if ((f2[0] == '.') && (f1[0] != '.')) return(r);
273
        if ((f2[0] == '.') && (f1[0] != '.')) return(r);
271
 
274
 
272
        if ((*ordconf | 32) == 'e') {
275
        if ((*ordconf | 32) == 'e') {
273
          /* fast-forward to extension or end of filename */
276
          /* fast-forward to extension or end of filename */
274
          while ((*f1 != 0) && (*f1 != '.')) f1++;
277
          while ((*f1 != 0) && (*f1 != '.')) f1++;
275
          while ((*f2 != 0) && (*f2 != '.')) f2++;
278
          while ((*f2 != 0) && (*f2 != '.')) f2++;
276
          limit = 4; /* TINYDTA structs are not nul-terminated */
279
          limit = 4; /* TINYDTA structs are not nul-terminated */
277
        }
280
        }
278
        /* cmp */
281
        /* cmp */
279
        for (i = 0; i < limit; i++) {
282
        for (i = 0; i < limit; i++) {
280
          if ((*f1 | 32) < (*f2 | 32)) return(0 - r);
283
          if ((*f1 | 32) < (*f2 | 32)) return(0 - r);
281
          if ((*f1 | 32) > (*f2 | 32)) return(r);
284
          if ((*f1 | 32) > (*f2 | 32)) return(r);
282
          if (*f1 == 0) break;
285
          if (*f1 == 0) break;
283
          f1++;
286
          f1++;
284
          f2++;
287
          f2++;
285
        }
288
        }
286
      }
289
      }
287
        break;
290
        break;
288
      case 's': /* sort by size */
291
      case 's': /* sort by size */
289
        if (dta1->size > dta2->size) return(r);
292
        if (dta1->size > dta2->size) return(r);
290
        if (dta1->size < dta2->size) return(0 - r);
293
        if (dta1->size < dta2->size) return(0 - r);
291
        break;
294
        break;
292
      case 'd': /* sort by date */
295
      case 'd': /* sort by date */
293
        if (dta1->date_yr < dta2->date_yr) return(0 - r);
296
        if (dta1->date_yr < dta2->date_yr) return(0 - r);
294
        if (dta1->date_yr > dta2->date_yr) return(r);
297
        if (dta1->date_yr > dta2->date_yr) return(r);
295
        if (dta1->date_mo < dta2->date_mo) return(0 - r);
298
        if (dta1->date_mo < dta2->date_mo) return(0 - r);
296
        if (dta1->date_mo > dta2->date_mo) return(r);
299
        if (dta1->date_mo > dta2->date_mo) return(r);
297
        if (dta1->date_dy < dta2->date_dy) return(0 - r);
300
        if (dta1->date_dy < dta2->date_dy) return(0 - r);
298
        if (dta1->date_dy > dta2->date_dy) return(r);
301
        if (dta1->date_dy > dta2->date_dy) return(r);
299
        if (dta1->time_hour < dta2->time_hour) return(0 - r);
302
        if (dta1->time_hour < dta2->time_hour) return(0 - r);
300
        if (dta1->time_hour > dta2->time_hour) return(r);
303
        if (dta1->time_hour > dta2->time_hour) return(r);
301
        if (dta1->time_min < dta2->time_min) return(0 - r);
304
        if (dta1->time_min < dta2->time_min) return(0 - r);
302
        if (dta1->time_min > dta2->time_min) return(r);
305
        if (dta1->time_min > dta2->time_min) return(r);
303
        break;
306
        break;
304
    }
307
    }
305
 
308
 
306
    if (*ordconf == 0) break;
309
    if (*ordconf == 0) break;
307
    ordconf++;
310
    ordconf++;
308
  }
311
  }
309
 
312
 
310
  return(0);
313
  return(0);
311
}
314
}
312
 
315
 
313
 
316
 
314
#define DIR_ATTR_DEFAULT (DOS_ATTR_RO | DOS_ATTR_DIR | DOS_ATTR_ARC)
317
#define DIR_ATTR_DEFAULT (DOS_ATTR_RO | DOS_ATTR_DIR | DOS_ATTR_ARC)
315
 
318
 
316
struct dirrequest {
319
struct dirrequest {
317
  unsigned char attrfilter_may;
320
  unsigned char attrfilter_may;
318
  unsigned char attrfilter_must;
321
  unsigned char attrfilter_must;
319
  const char *filespecptr;
322
  const char *filespecptr;
320
 
323
 
321
  #define DIR_FLAG_PAUSE  1
324
  #define DIR_FLAG_PAUSE  1
322
  #define DIR_FLAG_RECUR  4
325
  #define DIR_FLAG_RECUR  4
323
  #define DIR_FLAG_LCASE  8
326
  #define DIR_FLAG_LCASE  8
324
  #define DIR_FLAG_SORT  16
327
  #define DIR_FLAG_SORT  16
325
  unsigned char flags;
328
  unsigned char flags;
326
 
329
 
327
  #define DIR_OUTPUT_NORM 1
330
  #define DIR_OUTPUT_NORM 1
328
  #define DIR_OUTPUT_WIDE 2
331
  #define DIR_OUTPUT_WIDE 2
329
  #define DIR_OUTPUT_BARE 3
332
  #define DIR_OUTPUT_BARE 3
330
  unsigned char format;
333
  unsigned char format;
331
};
334
};
332
 
335
 
333
 
336
 
334
static int dir_parse_cmdline(struct dirrequest *req, const char **argv) {
337
static int dir_parse_cmdline(struct dirrequest *req, const char **argv) {
335
  for (; *argv != NULL; argv++) {
338
  for (; *argv != NULL; argv++) {
336
    if (*argv[0] == '/') {
339
    if (*argv[0] == '/') {
337
      const char *arg = *argv + 1;
340
      const char *arg = *argv + 1;
338
      char neg = 0;
341
      char neg = 0;
339
      /* detect negations and get actual argument */
342
      /* detect negations and get actual argument */
340
      if (*arg == '-') {
343
      if (*arg == '-') {
341
        neg = 1;
344
        neg = 1;
342
        arg++;
345
        arg++;
343
      }
346
      }
344
      /* */
347
      /* */
345
      switch (*arg) {
348
      switch (*arg) {
346
        case 'a':
349
        case 'a':
347
        case 'A':
350
        case 'A':
348
          arg++;
351
          arg++;
349
          /* preset defaults */
352
          /* preset defaults */
350
          req->attrfilter_may = DIR_ATTR_DEFAULT;
353
          req->attrfilter_may = DIR_ATTR_DEFAULT;
351
          req->attrfilter_must = 0;
354
          req->attrfilter_must = 0;
352
          /* /-A only allowed without further parameters (used to cancel possible previous /Asmth) */
355
          /* /-A only allowed without further parameters (used to cancel possible previous /Asmth) */
353
          if (neg) {
356
          if (neg) {
354
            if (*arg != 0) {
357
            if (*arg != 0) {
355
              nls_outputnl_err(0, 2); /* invalid switch */
358
              nls_outputnl_err(0, 2); /* invalid switch */
356
              return(-1);
359
              return(-1);
357
            }
360
            }
358
          } else {
361
          } else {
359
            /* skip colon if present */
362
            /* skip colon if present */
360
            if (*arg == ':') arg++;
363
            if (*arg == ':') arg++;
361
            /* start with "allow everything" */
364
            /* start with "allow everything" */
362
            req->attrfilter_may = (DOS_ATTR_ARC | DOS_ATTR_DIR | DOS_ATTR_HID | DOS_ATTR_SYS | DOS_ATTR_RO);
365
            req->attrfilter_may = (DOS_ATTR_ARC | DOS_ATTR_DIR | DOS_ATTR_HID | DOS_ATTR_SYS | DOS_ATTR_RO);
363
            if (dir_parse_attr_list(arg, &(req->attrfilter_may), &(req->attrfilter_must)) != 0) {
366
            if (dir_parse_attr_list(arg, &(req->attrfilter_may), &(req->attrfilter_must)) != 0) {
364
              nls_outputnl_err(0, 3); /* invalid parameter format */
367
              nls_outputnl_err(0, 3); /* invalid parameter format */
365
              return(-1);
368
              return(-1);
366
            }
369
            }
367
          }
370
          }
368
          break;
371
          break;
369
        case 'b':
372
        case 'b':
370
        case 'B':
373
        case 'B':
371
          req->format = DIR_OUTPUT_BARE;
374
          req->format = DIR_OUTPUT_BARE;
372
          break;
375
          break;
373
        case 'l':
376
        case 'l':
374
        case 'L':
377
        case 'L':
375
          req->flags |= DIR_FLAG_LCASE;
378
          req->flags |= DIR_FLAG_LCASE;
376
          break;
379
          break;
377
        case 'o':
380
        case 'o':
378
        case 'O':
381
        case 'O':
379
          if (neg) {
382
          if (neg) {
380
            req->flags &= (0xff ^ DIR_FLAG_SORT);
383
            req->flags &= (0xff ^ DIR_FLAG_SORT);
381
            break;
384
            break;
382
          }
385
          }
383
          if (dir_process_order_directive(arg+1) != 0) {
386
          if (dir_process_order_directive(arg+1) != 0) {
384
            nls_output_err(0, 3); /* invalid parameter format */
387
            nls_output_err(0, 3); /* invalid parameter format */
385
            output(": ");
388
            output(": ");
386
            outputnl(arg);
389
            outputnl(arg);
387
            return(-1);
390
            return(-1);
388
          }
391
          }
389
          req->flags |= DIR_FLAG_SORT;
392
          req->flags |= DIR_FLAG_SORT;
390
          break;
393
          break;
391
        case 'p':
394
        case 'p':
392
        case 'P':
395
        case 'P':
393
          req->flags |= DIR_FLAG_PAUSE;
396
          req->flags |= DIR_FLAG_PAUSE;
394
          if (neg) req->flags &= (0xff ^ DIR_FLAG_PAUSE);
397
          if (neg) req->flags &= (0xff ^ DIR_FLAG_PAUSE);
395
          break;
398
          break;
396
        case 's':
399
        case 's':
397
        case 'S':
400
        case 'S':
398
          /* TODO */
401
          /* TODO */
399
          outputnl("/S NOT IMPLEMENTED YET");
402
          outputnl("/S NOT IMPLEMENTED YET");
400
          return(-1);
403
          return(-1);
401
          break;
404
          break;
402
        case 'w':
405
        case 'w':
403
        case 'W':
406
        case 'W':
404
          req->format = DIR_OUTPUT_WIDE;
407
          req->format = DIR_OUTPUT_WIDE;
405
          break;
408
          break;
406
        default:
409
        default:
407
          nls_outputnl_err(0, 2); /* invalid switch */
410
          nls_outputnl_err(0, 2); /* invalid switch */
408
          return(-1);
411
          return(-1);
409
      }
412
      }
410
    } else {  /* filespec */
413
    } else {  /* filespec */
411
      if (req->filespecptr != NULL) {
414
      if (req->filespecptr != NULL) {
412
        nls_outputnl_err(0, 4); /* too many parameters */
415
        nls_outputnl_err(0, 4); /* too many parameters */
413
        return(-1);
416
        return(-1);
414
      }
417
      }
415
      req->filespecptr = *argv;
418
      req->filespecptr = *argv;
416
    }
419
    }
417
  }
420
  }
418
 
421
 
419
  return(0);
422
  return(0);
420
}
423
}
421
 
424
 
422
 
425
 
423
static enum cmd_result cmd_dir(struct cmd_funcparam *p) {
426
static enum cmd_result cmd_dir(struct cmd_funcparam *p) {
424
  struct DTA *dta = (void *)0x80; /* set DTA to its default location at 80h in PSP */
427
  struct DTA *dta = (void *)0x80; /* set DTA to its default location at 80h in PSP */
425
  struct TINYDTA far *dtabuf = NULL; /* used to buffer results when sorting is enabled */
428
  struct TINYDTA far *dtabuf = NULL; /* used to buffer results when sorting is enabled */
426
  unsigned short dtabufcount = 0;
429
  unsigned short dtabufcount = 0;
427
  unsigned short i;
430
  unsigned short i;
428
  unsigned short availrows;  /* counter of available rows on display (used for /P) */
431
  unsigned short availrows;  /* counter of available rows on display (used for /P) */
429
  unsigned short screenw = screen_getwidth();
432
  unsigned short screenw = screen_getwidth();
430
  unsigned short wcols = screenw / WCOLWIDTH; /* number of columns in wide mode */
433
  unsigned short wcols = screenw / WCOLWIDTH; /* number of columns in wide mode */
431
  unsigned char wcolcount;
434
  unsigned char wcolcount;
432
  struct {
435
  struct {
433
    struct nls_patterns nls;
436
    struct nls_patterns nls;
434
    char buff64[64];
437
    char buff64[64];
435
    char path[128];
438
    char path[128];
436
    unsigned short orderidx[65535 / sizeof(struct TINYDTA)];
439
    unsigned short orderidx[65535 / sizeof(struct TINYDTA)];
437
  } *buf = (void *)(p->BUFFER);
440
  } *buf = (void *)(p->BUFFER);
438
  unsigned long summary_fcount = 0;
441
  unsigned long summary_fcount = 0;
439
  unsigned long summary_totsz = 0;
442
  unsigned long summary_totsz = 0;
440
  unsigned char drv = 0;
443
  unsigned char drv = 0;
441
  struct dirrequest req;
444
  struct dirrequest req;
442
 
445
 
443
  /* make sure there's no risk of buffer overflow */
446
  /* make sure there's no risk of buffer overflow */
444
  if (sizeof(buf) > p->BUFFERSZ) {
447
  if (sizeof(buf) > p->BUFFERSZ) {
445
    outputnl("INTERNAL MEM ERROR IN " __FILE__);
448
    outputnl("INTERNAL MEM ERROR IN " __FILE__);
446
    return(CMD_FAIL);
449
    return(CMD_FAIL);
447
  }
450
  }
448
 
451
 
449
  bzero(&glob_sortcmp_dat, sizeof(glob_sortcmp_dat));
452
  bzero(&glob_sortcmp_dat, sizeof(glob_sortcmp_dat));
450
 
453
 
451
  if (cmd_ishlp(p)) {
454
  if (cmd_ishlp(p)) {
452
    nls_outputnl(37,0); /* "Displays a list of files and subdirectories in a directory" */
455
    nls_outputnl(37,0); /* "Displays a list of files and subdirectories in a directory" */
453
    outputnl("");
456
    outputnl("");
454
    nls_outputnl(37,1); /* "DIR [drive:][path][filename] [/P] [/W] [/A[:]attributes] [/O[[:]sortorder]] [/S] [/B] [/L]" */
457
    nls_outputnl(37,1); /* "DIR [drive:][path][filename] [/P] [/W] [/A[:]attributes] [/O[[:]sortorder]] [/S] [/B] [/L]" */
455
    outputnl("");
458
    outputnl("");
456
    nls_outputnl(37,2); /* "/P Pauses after each screenful of information" */
459
    nls_outputnl(37,2); /* "/P Pauses after each screenful of information" */
457
    nls_outputnl(37,3); /* "/W Uses wide list format" */
460
    nls_outputnl(37,3); /* "/W Uses wide list format" */
458
    outputnl("");
461
    outputnl("");
459
    nls_outputnl(37,4); /* "/A Displays files with specified attributes:" */
462
    nls_outputnl(37,4); /* "/A Displays files with specified attributes:" */
460
    nls_outputnl(37,5); /* "    D Directories            R Read-only files        H Hidden files" */
463
    nls_outputnl(37,5); /* "    D Directories            R Read-only files        H Hidden files" */
461
    nls_outputnl(37,6); /* "    A Ready for archiving    S System files           - prefix meaning "not"" */
464
    nls_outputnl(37,6); /* "    A Ready for archiving    S System files           - prefix meaning "not"" */
462
    outputnl("");
465
    outputnl("");
463
    nls_outputnl(37,7); /* "/O List files in sorted order:" */
466
    nls_outputnl(37,7); /* "/O List files in sorted order:" */
464
    nls_outputnl(37,8); /* "    N by name                S by size                E by extension" */
467
    nls_outputnl(37,8); /* "    N by name                S by size                E by extension" */
465
    nls_outputnl(37,9); /* "    D by date                G group dirs first       - prefix to reverse order" */
468
    nls_outputnl(37,9); /* "    D by date                G group dirs first       - prefix to reverse order" */
466
    outputnl("");
469
    outputnl("");
467
    nls_outputnl(37,10); /* "/S Displays files in specified directory and all subdirectories" */
470
    nls_outputnl(37,10); /* "/S Displays files in specified directory and all subdirectories" */
468
    nls_outputnl(37,11); /* "/B Uses bare format (no heading information or summary)" */
471
    nls_outputnl(37,11); /* "/B Uses bare format (no heading information or summary)" */
469
    nls_outputnl(37,12); /* "/L Uses lowercases" */
472
    nls_outputnl(37,12); /* "/L Uses lowercases" */
470
    return(CMD_OK);
473
    return(CMD_OK);
471
  }
474
  }
472
 
475
 
473
  i = nls_getpatterns(&(buf->nls));
476
  i = nls_getpatterns(&(buf->nls));
474
  if (i != 0) nls_outputnl_doserr(i);
477
  if (i != 0) nls_outputnl_doserr(i);
475
 
478
 
476
  /* disable usage of thousands separator on narrow screens */
479
  /* disable usage of thousands separator on narrow screens */
477
  if (screenw < 80) buf->nls.thousep[0] = 0;
480
  if (screenw < 80) buf->nls.thousep[0] = 0;
478
 
481
 
479
  /*** PARSING COMMAND LINE STARTS *******************************************/
482
  /*** PARSING COMMAND LINE STARTS *******************************************/
480
 
483
 
481
  /* init req with some defaults */
484
  /* init req with some defaults */
482
  bzero(&req, sizeof(req));
485
  bzero(&req, sizeof(req));
483
  req.attrfilter_may = DIR_ATTR_DEFAULT;
486
  req.attrfilter_may = DIR_ATTR_DEFAULT;
484
  req.format = DIR_OUTPUT_NORM;
487
  req.format = DIR_OUTPUT_NORM;
485
 
488
 
486
  /* process DIRCMD first (so it can be overidden by user's cmdline) */
489
  /* process DIRCMD first (so it can be overidden by user's cmdline) */
487
  {
490
  {
488
  const char far *dircmd = env_lookup_val(p->env_seg, "DIRCMD");
491
  const char far *dircmd = env_lookup_val(p->env_seg, "DIRCMD");
489
  if (dircmd != NULL) {
492
  if (dircmd != NULL) {
490
    const char *argvptrs[32];
493
    const char *argvptrs[32];
491
    cmd_explode(buf->buff64, dircmd, argvptrs);
494
    cmd_explode(buf->buff64, dircmd, argvptrs);
492
    if ((dir_parse_cmdline(&req, argvptrs) != 0) || (req.filespecptr != NULL)) {
495
    if ((dir_parse_cmdline(&req, argvptrs) != 0) || (req.filespecptr != NULL)) {
493
      nls_output(255, 10);/* bad environment */
496
      nls_output(255, 10);/* bad environment */
494
      output(" - ");
497
      output(" - ");
495
      outputnl("DIRCMD");
498
      outputnl("DIRCMD");
496
      return(CMD_FAIL);
499
      return(CMD_FAIL);
497
    }
500
    }
498
  }
501
  }
499
  }
502
  }
500
 
503
 
501
  /* parse user's command line */
504
  /* parse user's command line */
502
  if (dir_parse_cmdline(&req, p->argv) != 0) return(CMD_FAIL);
505
  if (dir_parse_cmdline(&req, p->argv) != 0) return(CMD_FAIL);
503
 
506
 
504
  /* if no filespec provided, then it's about the current directory */
507
  /* if no filespec provided, then it's about the current directory */
505
  if (req.filespecptr == NULL) req.filespecptr = ".";
508
  if (req.filespecptr == NULL) req.filespecptr = ".";
506
 
509
 
507
  /*** PARSING COMMAND LINE DONE *********************************************/
510
  /*** PARSING COMMAND LINE DONE *********************************************/
508
 
511
 
509
 
512
 
510
  availrows = screen_getheight() - 2;
513
  availrows = screen_getheight() - 2;
511
 
514
 
512
  /* special case: "DIR drive:" (truename() fails on "C:" under MS-DOS 6.0) */
515
  /* special case: "DIR drive:" (truename() fails on "C:" under MS-DOS 6.0) */
513
  if ((req.filespecptr[0] != 0) && (req.filespecptr[1] == ':') && (req.filespecptr[2] == 0)) {
516
  if ((req.filespecptr[0] != 0) && (req.filespecptr[1] == ':') && (req.filespecptr[2] == 0)) {
514
    if ((req.filespecptr[0] >= 'a') && (req.filespecptr[0] <= 'z')) {
517
    if ((req.filespecptr[0] >= 'a') && (req.filespecptr[0] <= 'z')) {
515
      buf->path[0] = req.filespecptr[0] - ('a' - 1);
518
      buf->path[0] = req.filespecptr[0] - ('a' - 1);
516
    } else {
519
    } else {
517
      buf->path[0] = req.filespecptr[0] - ('A' - 1);
520
      buf->path[0] = req.filespecptr[0] - ('A' - 1);
518
    }
521
    }
519
    i = curpathfordrv(buf->path, buf->path[0]);
522
    i = curpathfordrv(buf->path, buf->path[0]);
520
  } else {
523
  } else {
521
    i = file_truename(req.filespecptr, buf->path);
524
    i = file_truename(req.filespecptr, buf->path);
522
  }
525
  }
523
  if (i != 0) {
526
  if (i != 0) {
524
    nls_outputnl_doserr(i);
527
    nls_outputnl_doserr(i);
525
    return(CMD_FAIL);
528
    return(CMD_FAIL);
526
  }
529
  }
527
 
530
 
528
  if (req.format != DIR_OUTPUT_BARE) {
531
  if (req.format != DIR_OUTPUT_BARE) {
529
    drv = buf->path[0];
532
    drv = buf->path[0];
530
    if (drv >= 'a') {
533
    if (drv >= 'a') {
531
      drv -= 'a';
534
      drv -= 'a';
532
    } else {
535
    } else {
533
      drv -= 'A';
536
      drv -= 'A';
534
    }
537
    }
535
    cmd_vol_internal(drv, buf->buff64);
538
    cmd_vol_internal(drv, buf->buff64);
536
    sprintf(buf->buff64, svarlang_str(37,20)/*"Directory of %s"*/, buf->path);
539
    sprintf(buf->buff64, svarlang_str(37,20)/*"Directory of %s"*/, buf->path);
537
    /* trim at first '?', if any */
540
    /* trim at first '?', if any */
538
    for (i = 0; buf->buff64[i] != 0; i++) if (buf->buff64[i] == '?') buf->buff64[i] = 0;
541
    for (i = 0; buf->buff64[i] != 0; i++) if (buf->buff64[i] == '?') buf->buff64[i] = 0;
539
    outputnl(buf->buff64);
542
    outputnl(buf->buff64);
540
    outputnl("");
543
    outputnl("");
541
    availrows -= 3;
544
    availrows -= 3;
542
  }
545
  }
543
 
546
 
544
  /* if dir: append a backslash (also get its len) */
547
  /* if dir: append a backslash (also get its len) */
545
  i = path_appendbkslash_if_dir(buf->path);
548
  i = path_appendbkslash_if_dir(buf->path);
546
 
549
 
547
  /* if ends with a \ then append ????????.??? */
550
  /* if ends with a \ then append ????????.??? */
548
  if (buf->path[i - 1] == '\\') strcat(buf->path, "????????.???");
551
  if (buf->path[i - 1] == '\\') strcat(buf->path, "????????.???");
549
 
552
 
550
  /* ask DOS for list of files, but only with allowed attribs */
553
  /* ask DOS for list of files, but only with allowed attribs */
551
  i = findfirst(dta, buf->path, req.attrfilter_may);
554
  i = findfirst(dta, buf->path, req.attrfilter_may);
552
  if (i != 0) {
555
  if (i != 0) {
553
    nls_outputnl_doserr(i);
556
    nls_outputnl_doserr(i);
554
    return(CMD_FAIL);
557
    return(CMD_FAIL);
555
  }
558
  }
556
 
559
 
557
  /* if sorting is involved, then let's buffer all results (and sort them) */
560
  /* if sorting is involved, then let's buffer all results (and sort them) */
558
  if (req.flags & DIR_FLAG_SORT) {
561
  if (req.flags & DIR_FLAG_SORT) {
559
    /* allocate a memory buffer - try several sizes until one succeeds */
562
    /* allocate a memory buffer - try several sizes until one succeeds */
560
    const unsigned short memsz[] = {65500, 32000, 16000, 8000, 4000, 2000, 1000, 0};
563
    const unsigned short memsz[] = {65500, 32000, 16000, 8000, 4000, 2000, 1000, 0};
561
    unsigned short max_dta_bufcount = 0;
564
    unsigned short max_dta_bufcount = 0;
562
    for (i = 0; memsz[i] != 0; i++) {
565
    for (i = 0; memsz[i] != 0; i++) {
563
      dtabuf = _fmalloc(memsz[i]);
566
      dtabuf = _fmalloc(memsz[i]);
564
      if (dtabuf != NULL) break;
567
      if (dtabuf != NULL) break;
565
    }
568
    }
566
 
569
 
567
    if (dtabuf == NULL) {
570
    if (dtabuf == NULL) {
568
      nls_outputnl_doserr(8); /* out of memory */
571
      nls_outputnl_doserr(8); /* out of memory */
569
      return(CMD_FAIL);
572
      return(CMD_FAIL);
570
    }
573
    }
571
 
574
 
572
    /* remember the address so I can free it afterwards */
575
    /* remember the address so I can free it afterwards */
573
    glob_sortcmp_dat.dtabuf_root = dtabuf;
576
    glob_sortcmp_dat.dtabuf_root = dtabuf;
574
 
577
 
575
    /* compute the amount of DTAs I can buffer */
578
    /* compute the amount of DTAs I can buffer */
576
    max_dta_bufcount = memsz[i] / sizeof(struct TINYDTA);
579
    max_dta_bufcount = memsz[i] / sizeof(struct TINYDTA);
577
    /* printf("max_dta_bufcount = %u\n", max_dta_bufcount); */
580
    /* printf("max_dta_bufcount = %u\n", max_dta_bufcount); */
578
 
581
 
579
    do {
582
    do {
580
      /* filter out files with uninteresting attributes */
583
      /* filter out files with uninteresting attributes */
581
      if (filter_attribs(dta, req.attrfilter_must, req.attrfilter_may) == 0) continue;
584
      if (filter_attribs(dta, req.attrfilter_must, req.attrfilter_may) == 0) continue;
582
 
585
 
583
      /* normalize "size" of directories to zero because kernel returns garbage
586
      /* normalize "size" of directories to zero because kernel returns garbage
584
       * sizes for directories which might confuse the sorting routine later */
587
       * sizes for directories which might confuse the sorting routine later */
585
      if (dta->attr & DOS_ATTR_DIR) dta->size = 0;
588
      if (dta->attr & DOS_ATTR_DIR) dta->size = 0;
586
 
589
 
587
      _fmemcpy(&(dtabuf[dtabufcount]), ((char *)dta) + 22, sizeof(struct TINYDTA));
590
      _fmemcpy(&(dtabuf[dtabufcount]), ((char *)dta) + 22, sizeof(struct TINYDTA));
588
 
591
 
589
      /* save attribs in sec field, otherwise zero it (this field is not
592
      /* save attribs in sec field, otherwise zero it (this field is not
590
       * displayed and dropping the attr field saves 2 bytes per entry) */
593
       * displayed and dropping the attr field saves 2 bytes per entry) */
591
      dtabuf[dtabufcount++].time_sec2 = (dta->attr & 31);
594
      dtabuf[dtabufcount++].time_sec2 = (dta->attr & 31);
592
 
595
 
593
      /* do I have any space left? */
596
      /* do I have any space left? */
594
      if (dtabufcount == max_dta_bufcount) {
597
      if (dtabufcount == max_dta_bufcount) {
595
        //TODO some kind of user notification might be nice here
598
        //TODO some kind of user notification might be nice here
596
        //outputnl("TOO MANY ENTRIES FOR SORTING! LIST IS UNSORTED");
599
        //outputnl("TOO MANY ENTRIES FOR SORTING! LIST IS UNSORTED");
597
        break;
600
        break;
598
      }
601
      }
599
 
602
 
600
    } while (findnext(dta) == 0);
603
    } while (findnext(dta) == 0);
601
 
604
 
602
    /* sort the list - the tricky part is that my array is a far address while
605
    /* sort the list - the tricky part is that my array is a far address while
603
     * qsort works only with near pointers, so I have to use an ugly (and
606
     * qsort works only with near pointers, so I have to use an ugly (and
604
     * global) auxiliary table */
607
     * global) auxiliary table */
605
    for (i = 0; i < dtabufcount; i++) buf->orderidx[i] = i;
608
    for (i = 0; i < dtabufcount; i++) buf->orderidx[i] = i;
606
    qsort(buf->orderidx, dtabufcount, 2, &sortcmp);
609
    qsort(buf->orderidx, dtabufcount, 2, &sortcmp);
607
 
610
 
608
    /* preload first entry (last from orderidx, since entries are sorted in reverse) */
611
    /* preload first entry (last from orderidx, since entries are sorted in reverse) */
609
    dtabufcount--;
612
    dtabufcount--;
610
    _fmemcpy(((unsigned char *)dta) + 22, &(dtabuf[buf->orderidx[dtabufcount]]), sizeof(struct TINYDTA));
613
    _fmemcpy(((unsigned char *)dta) + 22, &(dtabuf[buf->orderidx[dtabufcount]]), sizeof(struct TINYDTA));
611
    dta->attr = dtabuf[buf->orderidx[dtabufcount]].time_sec2; /* restore attr from the abused time_sec2 field */
614
    dta->attr = dtabuf[buf->orderidx[dtabufcount]].time_sec2; /* restore attr from the abused time_sec2 field */
612
  }
615
  }
613
 
616
 
614
  wcolcount = 0; /* may be used for columns counting with wide mode */
617
  wcolcount = 0; /* may be used for columns counting with wide mode */
615
 
618
 
616
  for (;;) {
619
  for (;;) {
617
 
620
 
618
    /* filter out attributes (skip if entry comes from buffer, then it was already veted) */
621
    /* filter out attributes (skip if entry comes from buffer, then it was already veted) */
619
    if (filter_attribs(dta, req.attrfilter_must, req.attrfilter_may) == 0) continue;
622
    if (filter_attribs(dta, req.attrfilter_must, req.attrfilter_may) == 0) continue;
620
 
623
 
621
    /* turn string lcase (/L) */
624
    /* turn string lcase (/L) */
622
    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... */
625
    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... */
623
 
626
 
624
    summary_fcount++;
627
    summary_fcount++;
625
    if ((dta->attr & DOS_ATTR_DIR) == 0) summary_totsz += dta->size;
628
    if ((dta->attr & DOS_ATTR_DIR) == 0) summary_totsz += dta->size;
626
 
629
 
627
    switch (req.format) {
630
    switch (req.format) {
628
      case DIR_OUTPUT_NORM:
631
      case DIR_OUTPUT_NORM:
629
        /* print fname-space-extension (unless it's "." or "..", then print as-is) */
632
        /* print fname-space-extension (unless it's "." or "..", then print as-is) */
630
        if (dta->fname[0] == '.') {
633
        if (dta->fname[0] == '.') {
631
          output(dta->fname);
634
          output(dta->fname);
632
          i = strlen(dta->fname);
635
          i = strlen(dta->fname);
633
          while (i++ < 12) output(" ");
636
          while (i++ < 12) output(" ");
634
        } else {
637
        } else {
635
          file_fname2fcb(buf->buff64, dta->fname);
638
          file_fname2fcb(buf->buff64, dta->fname);
636
          memmove(buf->buff64 + 9, buf->buff64 + 8, 4);
639
          memmove(buf->buff64 + 9, buf->buff64 + 8, 4);
637
          buf->buff64[8] = ' ';
640
          buf->buff64[8] = ' ';
638
          output(buf->buff64);
641
          output(buf->buff64);
639
        }
642
        }
640
        output(" ");
643
        output(" ");
641
        /* either <DIR> or right aligned 10-chars byte size */
644
        /* either <DIR> or right aligned 10-chars byte size */
642
        memset(buf->buff64, ' ', 10);
645
        memset(buf->buff64, ' ', 10);
643
        if (dta->attr & DOS_ATTR_DIR) {
646
        if (dta->attr & DOS_ATTR_DIR) {
644
          strcpy(buf->buff64 + 10, svarlang_str(37,21));
647
          strcpy(buf->buff64 + 10, svarlang_str(37,21));
645
        } else {
648
        } else {
646
          nls_format_number(buf->buff64 + 10, dta->size, &(buf->nls));
649
          nls_format_number(buf->buff64 + 10, dta->size, &(buf->nls));
647
        }
650
        }
648
        output(buf->buff64 + strlen(buf->buff64) - 10);
651
        output(buf->buff64 + strlen(buf->buff64) - 10);
649
        /* two spaces and NLS DATE */
652
        /* two spaces and NLS DATE */
650
        buf->buff64[0] = ' ';
653
        buf->buff64[0] = ' ';
651
        buf->buff64[1] = ' ';
654
        buf->buff64[1] = ' ';
652
        if (screenw >= 80) {
655
        if (screenw >= 80) {
653
          nls_format_date(buf->buff64 + 2, dta->date_yr + 1980, dta->date_mo, dta->date_dy, &(buf->nls));
656
          nls_format_date(buf->buff64 + 2, dta->date_yr + 1980, dta->date_mo, dta->date_dy, &(buf->nls));
654
        } else {
657
        } else {
655
          nls_format_date(buf->buff64 + 2, (dta->date_yr + 80) % 100, dta->date_mo, dta->date_dy, &(buf->nls));
658
          nls_format_date(buf->buff64 + 2, (dta->date_yr + 80) % 100, dta->date_mo, dta->date_dy, &(buf->nls));
656
        }
659
        }
657
        output(buf->buff64);
660
        output(buf->buff64);
658
 
661
 
659
        /* one space and NLS TIME */
662
        /* one space and NLS TIME */
660
        nls_format_time(buf->buff64 + 1, dta->time_hour, dta->time_min, 0xff, &(buf->nls));
663
        nls_format_time(buf->buff64 + 1, dta->time_hour, dta->time_min, 0xff, &(buf->nls));
661
        outputnl(buf->buff64);
664
        outputnl(buf->buff64);
662
        break;
665
        break;
663
 
666
 
664
      case DIR_OUTPUT_WIDE: /* display in columns of 12 chars per item */
667
      case DIR_OUTPUT_WIDE: /* display in columns of 12 chars per item */
665
        i = strlen(dta->fname);
668
        i = strlen(dta->fname);
666
        if (dta->attr & DOS_ATTR_DIR) {
669
        if (dta->attr & DOS_ATTR_DIR) {
667
          i += 2;
670
          i += 2;
668
          output("[");
671
          output("[");
669
          output(dta->fname);
672
          output(dta->fname);
670
          output("]");
673
          output("]");
671
        } else {
674
        } else {
672
          output(dta->fname);
675
          output(dta->fname);
673
        }
676
        }
674
        while (i++ < WCOLWIDTH) output(" ");
677
        while (i++ < WCOLWIDTH) output(" ");
675
        if (++wcolcount == wcols) {
678
        if (++wcolcount == wcols) {
676
          wcolcount = 0;
679
          wcolcount = 0;
677
          outputnl("");
680
          outputnl("");
678
        } else {
681
        } else {
679
          availrows++; /* wide mode is the only one that does not write one line per file */
682
          availrows++; /* wide mode is the only one that does not write one line per file */
680
        }
683
        }
681
        break;
684
        break;
682
 
685
 
683
      case DIR_OUTPUT_BARE:
686
      case DIR_OUTPUT_BARE:
684
        outputnl(dta->fname);
687
        outputnl(dta->fname);
685
        break;
688
        break;
686
    }
689
    }
687
 
690
 
688
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
691
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
689
 
692
 
690
    /* take next entry, either from buf or disk */
693
    /* take next entry, either from buf or disk */
691
    if (dtabufcount > 0) {
694
    if (dtabufcount > 0) {
692
      dtabufcount--;
695
      dtabufcount--;
693
      _fmemcpy(((unsigned char *)dta) + 22, &(dtabuf[buf->orderidx[dtabufcount]]), sizeof(struct TINYDTA));
696
      _fmemcpy(((unsigned char *)dta) + 22, &(dtabuf[buf->orderidx[dtabufcount]]), sizeof(struct TINYDTA));
694
      dta->attr = dtabuf[buf->orderidx[dtabufcount]].time_sec2; /* restore attr from the abused time_sec2 field */
697
      dta->attr = dtabuf[buf->orderidx[dtabufcount]].time_sec2; /* restore attr from the abused time_sec2 field */
695
    } else {
698
    } else {
696
      if (findnext(dta) != 0) break;
699
      if (findnext(dta) != 0) break;
697
    }
700
    }
698
 
701
 
699
  }
702
  }
700
 
703
 
701
  if (wcolcount != 0) {
704
  if (wcolcount != 0) {
702
    outputnl(""); /* in wide mode make sure to end on a clear row */
705
    outputnl(""); /* in wide mode make sure to end on a clear row */
703
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
706
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
704
  }
707
  }
705
 
708
 
706
  /* print out summary (unless bare output mode) */
709
  /* print out summary (unless bare output mode) */
707
  if (req.format != DIR_OUTPUT_BARE) {
710
  if (req.format != DIR_OUTPUT_BARE) {
708
    unsigned short alignpos;
711
    unsigned short alignpos;
709
    unsigned char uint32maxlen = 13; /* 13 is the max len of a 32 bit number with thousand separators (4'000'000'000) */
712
    unsigned char uint32maxlen = 13; /* 13 is the max len of a 32 bit number with thousand separators (4'000'000'000) */
710
    if (screenw < 80) uint32maxlen = 10;
713
    if (screenw < 80) uint32maxlen = 10;
711
    /* x file(s) */
714
    /* x file(s) */
712
    memset(buf->buff64, ' ', uint32maxlen);
715
    memset(buf->buff64, ' ', uint32maxlen);
713
    i = nls_format_number(buf->buff64 + uint32maxlen, summary_fcount, &(buf->nls));
716
    i = nls_format_number(buf->buff64 + uint32maxlen, summary_fcount, &(buf->nls));
714
    alignpos = sprintf(buf->buff64 + uint32maxlen + i, " %s ", svarlang_str(37,22)/*"file(s)"*/);
717
    alignpos = sprintf(buf->buff64 + uint32maxlen + i, " %s ", svarlang_str(37,22)/*"file(s)"*/);
715
    output(buf->buff64 + i);
718
    output(buf->buff64 + i);
716
    /* xxxx bytes */
719
    /* xxxx bytes */
717
    i = nls_format_number(buf->buff64 + uint32maxlen, summary_totsz, &(buf->nls));
720
    i = nls_format_number(buf->buff64 + uint32maxlen, summary_totsz, &(buf->nls));
718
    output(buf->buff64 + i + 1);
721
    output(buf->buff64 + i + 1);
719
    output(" ");
722
    output(" ");
720
    nls_outputnl(37,23); /* "bytes" */
723
    nls_outputnl(37,23); /* "bytes" */
721
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
724
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
722
    /* xxxx bytes free */
725
    /* xxxx bytes free */
723
    i = cmd_dir_df(&summary_totsz, drv);
726
    i = cmd_dir_df(&summary_totsz, drv);
724
    if (i != 0) nls_outputnl_doserr(i);
727
    if (i != 0) nls_outputnl_doserr(i);
725
    alignpos += uint32maxlen * 2;
728
    alignpos += uint32maxlen * 2;
726
    memset(buf->buff64, ' ', alignpos); /* align the freebytes value to same column as totbytes */
729
    memset(buf->buff64, ' ', alignpos); /* align the freebytes value to same column as totbytes */
727
    i = nls_format_number(buf->buff64 + alignpos, summary_totsz, &(buf->nls));
730
    i = nls_format_number(buf->buff64 + alignpos, summary_totsz, &(buf->nls));
728
    output(buf->buff64 + i + 1);
731
    output(buf->buff64 + i + 1);
729
    output(" ");
732
    output(" ");
730
    nls_outputnl(37,24); /* "bytes free" */
733
    nls_outputnl(37,24); /* "bytes free" */
731
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
734
    if (req.flags & DIR_FLAG_PAUSE) dir_pagination(&availrows);
732
  }
735
  }
733
 
736
 
734
  /* free the buffer memory (if used) */
737
  /* free the buffer memory (if used) */
735
  if (glob_sortcmp_dat.dtabuf_root != NULL) _ffree(glob_sortcmp_dat.dtabuf_root);
738
  if (glob_sortcmp_dat.dtabuf_root != NULL) _ffree(glob_sortcmp_dat.dtabuf_root);
736
 
739
 
737
  return(CMD_OK);
740
  return(CMD_OK);
738
}
741
}
739
 
742