Subversion Repositories SvarDOS

Rev

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

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