Subversion Repositories SvarDOS

Rev

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

Rev 1137 Rev 1629
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-2022 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
 * a variety of helper functions
26
 * a variety of helper functions
27
 */
27
 */
28
 
28
 
29
#include <i86.h>    /* MK_FP() */
29
#include <i86.h>    /* MK_FP() */
30
#include <stdio.h>  /* sprintf() */
30
#include <stdio.h>  /* sprintf() */
31
#include <string.h> /* memcpy() */
31
#include <string.h> /* memcpy() */
32
 
32
 
33
#include "svarlang.lib\svarlang.h"
33
#include "svarlang.lib\svarlang.h"
34
 
34
 
35
#include "env.h"
35
#include "env.h"
36
 
36
 
37
#include "helpers.h"
37
#include "helpers.h"
38
 
38
 
39
 
39
 
40
 
40
 
41
/* case-insensitive comparison of strings, compares up to maxlen characters.
41
/* case-insensitive comparison of strings, compares up to maxlen characters.
42
 * returns non-zero on equality. */
42
 * returns non-zero on equality. */
43
int imatchlim(const char *s1, const char *s2, unsigned short maxlen) {
43
int imatchlim(const char *s1, const char *s2, unsigned short maxlen) {
44
  while (maxlen--) {
44
  while (maxlen--) {
45
    char c1, c2;
45
    char c1, c2;
46
    c1 = *s1;
46
    c1 = *s1;
47
    c2 = *s2;
47
    c2 = *s2;
48
    if ((c1 >= 'a') && (c1 <= 'z')) c1 -= ('a' - 'A');
48
    if ((c1 >= 'a') && (c1 <= 'z')) c1 -= ('a' - 'A');
49
    if ((c2 >= 'a') && (c2 <= 'z')) c2 -= ('a' - 'A');
49
    if ((c2 >= 'a') && (c2 <= 'z')) c2 -= ('a' - 'A');
50
    /* */
50
    /* */
51
    if (c1 != c2) return(0);
51
    if (c1 != c2) return(0);
52
    if (c1 == 0) break;
52
    if (c1 == 0) break;
53
    s1++;
53
    s1++;
54
    s2++;
54
    s2++;
55
  }
55
  }
56
  return(1);
56
  return(1);
57
}
57
}
58
 
58
 
59
 
59
 
60
/* returns zero if s1 starts with s2 */
60
/* returns zero if s1 starts with s2 */
61
int strstartswith(const char *s1, const char *s2) {
61
int strstartswith(const char *s1, const char *s2) {
62
  while (*s2 != 0) {
62
  while (*s2 != 0) {
63
    if (*s1 != *s2) return(-1);
63
    if (*s1 != *s2) return(-1);
64
    s1++;
64
    s1++;
65
    s2++;
65
    s2++;
66
  }
66
  }
67
  return(0);
67
  return(0);
68
}
68
}
69
 
69
 
70
 
70
 
71
/* outputs a NULL-terminated string to handle (1=stdout 2=stderr) */
71
/* outputs a NULL-terminated string to handle (1=stdout 2=stderr) */
72
void output_internal(const char *s, unsigned char nl, unsigned char handle) {
72
void output_internal(const char *s, unsigned char nl, unsigned char handle) {
73
  const static unsigned char *crlf = "\r\n";
73
  const static unsigned char *crlf = "\r\n";
74
  _asm {
74
  _asm {
75
    push ds
75
    push ds
76
    pop es         /* make sure es=ds (scasb uses es) */
76
    pop es         /* make sure es=ds (scasb uses es) */
77
    /* get length of s into CX */
77
    /* get length of s into CX */
78
    mov ax, 0x4000 /* ah=DOS "write to file" and AL=0 for NULL matching */
78
    mov ax, 0x4000 /* ah=DOS "write to file" and AL=0 for NULL matching */
79
    mov dx, s      /* set dx to string (required for later) */
79
    mov dx, s      /* set dx to string (required for later) */
80
    mov di, dx     /* set di to string (for NULL matching) */
80
    mov di, dx     /* set di to string (for NULL matching) */
81
    mov cx, 0xffff /* preset cx to 65535 (-1) */
81
    mov cx, 0xffff /* preset cx to 65535 (-1) */
82
    cld            /* clear DF so scasb increments DI */
82
    cld            /* clear DF so scasb increments DI */
83
    repne scasb    /* cmp al, es:[di], inc di, dec cx until match found */
83
    repne scasb    /* cmp al, es:[di], inc di, dec cx until match found */
84
    /* CX contains (65535 - strlen(s)) now */
84
    /* CX contains (65535 - strlen(s)) now */
85
    not cx         /* reverse all bits so I get (strlen(s) + 1) */
85
    not cx         /* reverse all bits so I get (strlen(s) + 1) */
86
    dec cx         /* this is CX length */
86
    dec cx         /* this is CX length */
87
    jz WRITEDONE   /* do nothing for empty strings */
87
    jz WRITEDONE   /* do nothing for empty strings */
88
 
88
 
89
    /* output by writing to stdout */
89
    /* output by writing to stdout */
90
    /* mov ah, 0x40 */  /* DOS 2+ -- write to file via handle */
90
    /* mov ah, 0x40 */  /* DOS 2+ -- write to file via handle */
91
    xor bh, bh
91
    xor bh, bh
92
    mov bl, handle /* set handle (1=stdout 2=stderr) */
92
    mov bl, handle /* set handle (1=stdout 2=stderr) */
93
    /* mov cx, xxx */ /* write CX bytes */
93
    /* mov cx, xxx */ /* write CX bytes */
94
    /* mov dx, s   */ /* DS:DX is the source of bytes to "write" */
94
    /* mov dx, s   */ /* DS:DX is the source of bytes to "write" */
95
    int 0x21
95
    int 0x21
96
    WRITEDONE:
96
    WRITEDONE:
97
 
97
 
98
    /* print out a CR/LF trailer if nl set */
98
    /* print out a CR/LF trailer if nl set */
99
    test byte ptr [nl], 0xff
99
    test byte ptr [nl], 0xff
100
    jz FINITO
100
    jz FINITO
101
    /* bx still contains handle */
101
    /* bx still contains handle */
102
    mov ah, 0x40 /* "write to file" */
102
    mov ah, 0x40 /* "write to file" */
103
    mov cx, 2
103
    mov cx, 2
104
    mov dx, crlf
104
    mov dx, crlf
105
    int 0x21
105
    int 0x21
106
    FINITO:
106
    FINITO:
107
  }
107
  }
108
}
108
}
109
 
109
 
110
 
110
 
111
void nls_output_internal(unsigned short id, unsigned char nl, unsigned char handle) {
111
void nls_output_internal(unsigned short id, unsigned char nl, unsigned char handle) {
112
  const char *NOTFOUND = "NLS_STRING_NOT_FOUND";
112
  const char *NOTFOUND = "NLS_STRING_NOT_FOUND";
113
  const char *ptr = svarlang_strid(id);
113
  const char *ptr = svarlang_strid(id);
114
  if ((ptr == NULL) || (ptr[0]) == 0) ptr = NOTFOUND;
114
  if ((ptr == NULL) || (ptr[0]) == 0) ptr = NOTFOUND;
115
  output_internal(ptr, nl, handle);
115
  output_internal(ptr, nl, handle);
116
}
116
}
117
 
117
 
118
 
118
 
119
/* output DOS error e to stdout, if stdout is redirected then *additionally*
119
/* output DOS error e to stdout, if stdout is redirected then *additionally*
120
 * also to stderr */
120
 * also to stderr */
121
void nls_outputnl_doserr(unsigned short e) {
121
void nls_outputnl_doserr(unsigned short e) {
122
  static char errstr[16];
122
  static char errstr[16];
123
  const char *ptr = NULL;
123
  const char *ptr = NULL;
124
  unsigned char redirflag = 0;
124
  unsigned char redirflag = 0;
125
  /* find string in nls block */
125
  /* find string in nls block */
126
  if (e < 0xff) ptr = svarlang_strid(0xff00 | e);
126
  if (e < 0xff) ptr = svarlang_strid(0xff00 | e);
127
  /* if not found, use a fallback */
127
  /* if not found, use a fallback */
128
  if ((ptr == NULL) || (ptr[0] == 0)) {
128
  if ((ptr == NULL) || (ptr[0] == 0)) {
129
    sprintf(errstr, "DOS ERR %u", e);
129
    sprintf(errstr, "DOS ERR %u", e);
130
    ptr = errstr;
130
    ptr = errstr;
131
  }
131
  }
132
 
132
 
133
  /* display to stdout */
133
  /* display to stdout */
134
  output_internal(ptr, 1, hSTDOUT);
134
  output_internal(ptr, 1, hSTDOUT);
135
 
135
 
136
  /* is stdout redirected? */
136
  /* is stdout redirected? */
137
  _asm {
137
  _asm {
138
    push bx
138
    push bx
139
    push dx
139
    push dx
140
 
140
 
141
    mov ax, 0x4400   /* query device flags */
141
    mov ax, 0x4400   /* query device flags */
142
    mov bx, 1        /* stdout */
142
    mov bx, 1        /* stdout */
143
    int 0x21
143
    int 0x21
144
    /* CF set on error and AX filled with DOS error,
144
    /* CF set on error and AX filled with DOS error,
145
     * returns flags in DX on succes:
145
     * returns flags in DX on succes:
146
     *  bit 7 reset if handle points to a file, set if handle points to a device  */
146
     *  bit 7 reset if handle points to a file, set if handle points to a device  */
147
    jc FAIL
147
    jc FAIL
148
    mov redirflag, dl
148
    mov redirflag, dl
149
    and redirflag, 128
149
    and redirflag, 128
150
 
150
 
151
    FAIL:
151
    FAIL:
152
    pop dx
152
    pop dx
153
    pop bx
153
    pop bx
154
  }
154
  }
155
 
155
 
156
  if (redirflag == 0) output_internal(ptr, 1, hSTDERR);
156
  if (redirflag == 0) output_internal(ptr, 1, hSTDERR);
157
}
157
}
158
 
158
 
159
 
159
 
160
/* find first matching files using a FindFirst DOS call
160
/* find first matching files using a FindFirst DOS call
161
 * returns 0 on success or a DOS err code on failure */
161
 * returns 0 on success or a DOS err code on failure */
162
unsigned short findfirst(struct DTA *dta, const char *pattern, unsigned short attr) {
162
unsigned short findfirst(struct DTA *dta, const char *pattern, unsigned short attr) {
163
  unsigned short res = 0;
163
  unsigned short res = 0;
164
  _asm {
164
  _asm {
165
    /* set DTA location */
165
    /* set DTA location */
166
    mov ah, 0x1a
166
    mov ah, 0x1a
167
    mov dx, dta
167
    mov dx, dta
168
    int 0x21
168
    int 0x21
169
    /* */
169
    /* */
170
    mov ah, 0x4e    /* FindFirst */
170
    mov ah, 0x4e    /* FindFirst */
171
    mov dx, pattern
171
    mov dx, pattern
172
    mov cx, attr
172
    mov cx, attr
173
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
173
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
174
    jnc DONE
174
    jnc DONE
175
    mov [res], ax
175
    mov [res], ax
176
    DONE:
176
    DONE:
177
  }
177
  }
178
  return(res);
178
  return(res);
179
}
179
}
180
 
180
 
181
 
181
 
182
/* find next matching, ie. continues an action intiated by findfirst() */
182
/* find next matching, ie. continues an action intiated by findfirst() */
183
unsigned short findnext(struct DTA *dta) {
183
unsigned short findnext(struct DTA *dta) {
184
  unsigned short res = 0;
184
  unsigned short res = 0;
185
  _asm {
185
  _asm {
186
    mov ah, 0x4f    /* FindNext */
186
    mov ah, 0x4f    /* FindNext */
187
    mov dx, dta
187
    mov dx, dta
188
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
188
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
189
    jnc DONE
189
    jnc DONE
190
    mov [res], ax
190
    mov [res], ax
191
    DONE:
191
    DONE:
192
  }
192
  }
193
  return(res);
193
  return(res);
194
}
194
}
195
 
195
 
196
 
196
 
197
/* print s string and wait for a single key press from stdin. accepts only
197
/* print s string and wait for a single key press from stdin. accepts only
198
 * key presses defined in the c ASCIIZ string. returns offset of pressed key
198
 * key presses defined in the c ASCIIZ string. returns offset of pressed key
199
 * in string. keys in c MUST BE UPPERCASE! */
199
 * in string. keys in c MUST BE UPPERCASE! */
200
unsigned short askchoice(const char *s, const char *c) {
200
unsigned short askchoice(const char *s, const char *c) {
201
  unsigned short res;
201
  unsigned short res;
202
  char cstr[2] = {0,0};
202
  char cstr[2] = {0,0};
203
  char key = 0;
203
  char key = 0;
204
 
204
 
205
  AGAIN:
205
  AGAIN:
206
  output(s);
206
  output(s);
207
  output(" ");
207
  output(" ");
208
  output("(");
208
  output("(");
209
  for (res = 0; c[res] != 0; res++) {
209
  for (res = 0; c[res] != 0; res++) {
210
    if (res != 0) output("/");
210
    if (res != 0) output("/");
211
    cstr[0] = c[res];
211
    cstr[0] = c[res];
212
    output(cstr);
212
    output(cstr);
213
  }
213
  }
214
  output(") ");
214
  output(") ");
215
 
215
 
216
  _asm {
216
  _asm {
217
    push ax
217
    push ax
218
    push dx
218
    push dx
219
 
219
 
220
    mov ax, 0x0c01 /* clear input buffer and execute getchar (INT 21h,AH=1) */
220
    mov ax, 0x0c01 /* clear input buffer and execute getchar (INT 21h,AH=1) */
221
    int 0x21
221
    int 0x21
222
    /* if AL == 0 then this is an extended character */
222
    /* if AL == 0 then this is an extended character */
223
    test al, al
223
    test al, al
224
    jnz GOTCHAR
224
    jnz GOTCHAR
225
    mov ah, 0x08   /* read again to flush extended char from input buffer */
225
    mov ah, 0x08   /* read again to flush extended char from input buffer */
226
    int 0x21
226
    int 0x21
227
    xor al, al     /* all extended chars are ignored */
227
    xor al, al     /* all extended chars are ignored */
228
    GOTCHAR:       /* received key is in AL now */
228
    GOTCHAR:       /* received key is in AL now */
229
    mov [key], al  /* save key */
229
    mov [key], al  /* save key */
230
 
230
 
231
    /* print a cr/lf */
231
    /* print a cr/lf */
232
    mov ah, 0x02
232
    mov ah, 0x02
233
    mov dl, 0x0D
233
    mov dl, 0x0D
234
    int 0x21
234
    int 0x21
235
    mov dl, 0x0A
235
    mov dl, 0x0A
236
    int 0x21
236
    int 0x21
237
 
237
 
238
    pop dx
238
    pop dx
239
    pop ax
239
    pop ax
240
  }
240
  }
241
 
241
 
242
  /* ucase() result */
242
  /* ucase() result */
243
  if ((key >= 'a') && (key <= 'z')) key -= ('a' - 'A');
243
  if ((key >= 'a') && (key <= 'z')) key -= ('a' - 'A');
244
 
244
 
245
  /* is there a match? */
245
  /* is there a match? */
246
  for (res = 0; c[res] != 0; res++) if (c[res] == key) return(res);
246
  for (res = 0; c[res] != 0; res++) if (c[res] == key) return(res);
247
 
247
 
248
  goto AGAIN;
248
  goto AGAIN;
249
}
249
}
250
 
250
 
251
 
251
 
252
/* converts a path to its canonic representation, returns 0 on success
252
/* converts a path to its canonic representation, returns 0 on success
253
 * or DOS err on failure (invalid drive) */
253
 * or DOS err on failure (invalid drive) */
254
unsigned short file_truename(const char *src, char *dst) {
254
unsigned short file_truename(const char *src, char *dst) {
255
  unsigned short res = 0;
255
  unsigned short res = 0;
256
  _asm {
256
  _asm {
257
    push es
257
    push es
258
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
258
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
259
    push ds
259
    push ds
260
    pop es
260
    pop es
261
    mov si, src
261
    mov si, src
262
    mov di, dst
262
    mov di, dst
263
    int 0x21
263
    int 0x21
264
    jnc DONE
264
    jnc DONE
265
    mov [res], ax
265
    mov [res], ax
266
    DONE:
266
    DONE:
267
    pop es
267
    pop es
268
  }
268
  }
269
  return(res);
269
  return(res);
270
}
270
}
271
 
271
 
272
 
272
 
273
/* returns DOS attributes of file, or -1 on error */
273
/* returns DOS attributes of file, or -1 on error */
274
int file_getattr(const char *fname) {
274
int file_getattr(const char *fname) {
275
  int res = -1;
275
  int res = -1;
276
  _asm {
276
  _asm {
277
    mov ax, 0x4300  /* query file attributes, fname at DS:DX */
277
    mov ax, 0x4300  /* query file attributes, fname at DS:DX */
278
    mov dx, fname
278
    mov dx, fname
279
    int 0x21        /* CX=attributes if CF=0, otherwise AX=errno */
279
    int 0x21        /* CX=attributes if CF=0, otherwise AX=errno */
280
    jc DONE
280
    jc DONE
281
    mov [res], cx
281
    mov [res], cx
282
    DONE:
282
    DONE:
283
  }
283
  }
284
  return(res);
284
  return(res);
285
}
285
}
286
 
286
 
287
 
287
 
288
/* returns screen's width (in columns) */
288
/* returns screen's width (in columns) */
289
unsigned short screen_getwidth(void) {
289
unsigned short screen_getwidth(void) {
290
  /* BIOS 0040:004A = word containing screen width in text columns */
290
  /* BIOS 0040:004A = word containing screen width in text columns */
291
  unsigned short far *scrw = MK_FP(0x40, 0x4a);
291
  unsigned short far *scrw = MK_FP(0x40, 0x4a);
292
  return(*scrw);
292
  return(*scrw);
293
}
293
}
294
 
294
 
295
 
295
 
296
/* returns screen's height (in rows) */
296
/* returns screen's height (in rows) */
297
unsigned short screen_getheight(void) {
297
unsigned short screen_getheight(void) {
298
  /* BIOS 0040:0084 = byte containing maximum valid row value (EGA ONLY) */
298
  /* BIOS 0040:0084 = byte containing maximum valid row value (EGA ONLY) */
299
  unsigned char far *scrh = MK_FP(0x40, 0x84);
299
  unsigned char far *scrh = MK_FP(0x40, 0x84);
300
  if (*scrh == 0) return(25);  /* pre-EGA adapter */
300
  if (*scrh == 0) return(25);  /* pre-EGA adapter */
301
  return(*scrh + 1);
301
  return(*scrh + 1);
302
}
302
}
303
 
303
 
304
 
304
 
305
/* displays the "Press any key to continue" msg and waits for a keypress */
305
/* displays the "Press any key to continue" msg and waits for a keypress */
306
void press_any_key(void) {
306
void press_any_key(void) {
307
  nls_output(15, 1); /* Press any key to continue... */
307
  nls_output(15, 1); /* Press any key to continue... */
308
  _asm {
308
  _asm {
309
    mov ah, 0x08  /* no echo console input */
309
    mov ah, 0x08  /* no echo console input */
310
    int 0x21      /* pressed key in AL now (0 for extended keys) */
310
    int 0x21      /* pressed key in AL now (0 for extended keys) */
311
    test al, al
311
    test al, al
312
    jnz DONE
312
    jnz DONE
313
    int 0x21      /* executed ah=8 again to read the rest of extended key */
313
    int 0x21      /* executed ah=8 again to read the rest of extended key */
314
    DONE:
314
    DONE:
315
    /* output CR/LF */
315
    /* output CR/LF */
316
    mov ah, 0x02
316
    mov ah, 0x02
317
    mov dl, 0x0D
317
    mov dl, 0x0D
318
    int 0x21
318
    int 0x21
319
    mov dl, 0x0A
319
    mov dl, 0x0A
320
    int 0x21
320
    int 0x21
321
  }
321
  }
322
}
322
}
323
 
323
 
324
 
324
 
325
/* validate a drive (A=0, B=1, etc). returns 1 if valid, 0 otherwise */
325
/* validate a drive (A=0, B=1, etc). returns 1 if valid, 0 otherwise */
326
int isdrivevalid(unsigned char drv) {
326
int isdrivevalid(unsigned char drv) {
327
  _asm {
327
  _asm {
328
    mov ah, 0x19  /* query default (current) disk */
328
    mov ah, 0x19  /* query default (current) disk */
329
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
329
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
330
    mov ch, al    /* save current drive to ch */
330
    mov ch, al    /* save current drive to ch */
331
    /* try setting up the drive as current */
331
    /* try setting up the drive as current */
332
    mov ah, 0x0E   /* select default drive */
332
    mov ah, 0x0E   /* select default drive */
333
    mov dl, [drv]  /* 0=A, 1=B, etc */
333
    mov dl, [drv]  /* 0=A, 1=B, etc */
334
    int 0x21
334
    int 0x21
335
    /* this call does not set CF on error, I must check cur drive to look for success */
335
    /* this call does not set CF on error, I must check cur drive to look for success */
336
    mov ah, 0x19  /* query default (current) disk */
336
    mov ah, 0x19  /* query default (current) disk */
337
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
337
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
338
    mov [drv], 1  /* preset result as success */
338
    mov [drv], 1  /* preset result as success */
339
    cmp al, dl    /* is eq? */
339
    cmp al, dl    /* is eq? */
340
    je DONE
340
    je DONE
341
    mov [drv], 0  /* fail */
341
    mov [drv], 0  /* fail */
342
    jmp FAILED
342
    jmp FAILED
343
    DONE:
343
    DONE:
344
    /* set current drive back to what it was initially */
344
    /* set current drive back to what it was initially */
345
    mov ah, 0x0E
345
    mov ah, 0x0E
346
    mov dl, ch
346
    mov dl, ch
347
    int 0x21
347
    int 0x21
348
    FAILED:
348
    FAILED:
349
  }
349
  }
350
  return(drv);
350
  return(drv);
351
}
351
}
352
 
352
 
353
 
353
 
354
/* converts a 8+3 filename into 11-bytes FCB format (MYFILE  EXT) */
354
/* converts a 8+3 filename into 11-bytes FCB format (MYFILE  EXT) */
355
void file_fname2fcb(char *dst, const char *src) {
355
void file_fname2fcb(char *dst, const char *src) {
356
  unsigned short i;
356
  unsigned short i;
357
 
357
 
358
  /* fill dst with 11 spaces and a NULL terminator */
358
  /* fill dst with 11 spaces and a NULL terminator */
359
  for (i = 0; i < 11; i++) dst[i] = ' ';
359
  for (i = 0; i < 11; i++) dst[i] = ' ';
360
  dst[11] = 0;
360
  dst[11] = 0;
361
 
361
 
362
  /* copy fname until dot (.) or 8 characters */
362
  /* copy fname until dot (.) or 8 characters */
363
  for (i = 0; i < 8; i++) {
363
  for (i = 0; i < 8; i++) {
364
    if ((src[i] == '.') || (src[i] == 0)) break;
364
    if ((src[i] == '.') || (src[i] == 0)) break;
365
    dst[i] = src[i];
365
    dst[i] = src[i];
366
  }
366
  }
367
 
367
 
368
  /* advance src until extension or end of string */
368
  /* advance src until extension or end of string */
369
  src += i;
369
  src += i;
370
  for (;;) {
370
  for (;;) {
371
    if (*src == '.') {
371
    if (*src == '.') {
372
      src++; /* next character is extension */
372
      src++; /* next character is extension */
373
      break;
373
      break;
374
    }
374
    }
375
    if (*src == 0) break;
375
    if (*src == 0) break;
376
  }
376
  }
377
 
377
 
378
  /* copy extension to dst (3 chars max) */
378
  /* copy extension to dst (3 chars max) */
379
  dst += 8;
379
  dst += 8;
380
  for (i = 0; i < 3; i++) {
380
  for (i = 0; i < 3; i++) {
381
    if (src[i] == 0) break;
381
    if (src[i] == 0) break;
382
    dst[i] = src[i];
382
    dst[i] = src[i];
383
  }
383
  }
384
}
384
}
385
 
385
 
386
 
386
 
387
/* converts a 11-bytes FCB filename (MYFILE  EXT) into 8+3 format (MYFILE.EXT) */
387
/* converts a 11-bytes FCB filename (MYFILE  EXT) into 8+3 format (MYFILE.EXT) */
388
void file_fcb2fname(char *dst, const char *src) {
388
void file_fcb2fname(char *dst, const char *src) {
389
  unsigned short i, end = 0;
389
  unsigned short i, end = 0;
390
 
390
 
391
  for (i = 0; i < 8; i++) {
391
  for (i = 0; i < 8; i++) {
392
    dst[i] = src[i];
392
    dst[i] = src[i];
393
    if (dst[i] != ' ') end = i + 1;
393
    if (dst[i] != ' ') end = i + 1;
394
  }
394
  }
395
 
395
 
396
  /* is there an extension? */
396
  /* is there an extension? */
397
  if (src[8] == ' ') {
397
  if (src[8] == ' ') {
398
    dst[end] = 0;
398
    dst[end] = 0;
399
  } else { /* found extension: copy it until first space */
399
  } else { /* found extension: copy it until first space */
400
    dst[end++] = '.';
400
    dst[end++] = '.';
401
    for (i = 8; i < 11; i++) {
401
    for (i = 8; i < 11; i++) {
402
      if (src[i] == ' ') break;
402
      if (src[i] == ' ') break;
403
      dst[end++] = src[i];
403
      dst[end++] = src[i];
404
    }
404
    }
405
    dst[end] = 0;
405
    dst[end] = 0;
406
  }
406
  }
407
}
407
}
408
 
408
 
409
 
409
 
410
/* converts an ASCIIZ string into an unsigned short. returns 0 on success.
410
/* converts an ASCIIZ string into an unsigned short. returns 0 on success.
411
 * on error, result will contain all valid digits that were read until
411
 * on error, result will contain all valid digits that were read until
412
 * error occurred (0 on overflow or if parsing failed immediately) */
412
 * error occurred (0 on overflow or if parsing failed immediately) */
413
int atous(unsigned short *r, const char *s) {
413
int atous(unsigned short *r, const char *s) {
414
  int err = 0;
414
  int err = 0;
415
 
415
 
416
  _asm {
416
  _asm {
417
    mov si, s
417
    mov si, s
418
    xor ax, ax  /* general purpose register */
418
    xor ax, ax  /* general purpose register */
419
    xor cx, cx  /* contains the result */
419
    xor cx, cx  /* contains the result */
420
    mov bx, 10  /* used as a multiplicative step */
420
    mov bx, 10  /* used as a multiplicative step */
421
 
421
 
422
    NEXTBYTE:
422
    NEXTBYTE:
423
    xchg cx, ax /* move result into cx temporarily */
423
    xchg cx, ax /* move result into cx temporarily */
424
    lodsb  /* AL = DS:[SI++] */
424
    lodsb  /* AL = DS:[SI++] */
425
    /* is AL 0? if so we're done */
425
    /* is AL 0? if so we're done */
426
    test al, al
426
    test al, al
427
    jz DONE
427
    jz DONE
428
    /* validate that AL is in range '0'-'9' */
428
    /* validate that AL is in range '0'-'9' */
429
    sub al, '0'
429
    sub al, '0'
430
    jc FAIL   /* invalid character detected */
430
    jc FAIL   /* invalid character detected */
431
    cmp al, 9
431
    cmp al, 9
432
    jg FAIL   /* invalid character detected */
432
    jg FAIL   /* invalid character detected */
433
    /* restore result into AX (CX contains the new digit) */
433
    /* restore result into AX (CX contains the new digit) */
434
    xchg cx, ax
434
    xchg cx, ax
435
    /* multiply result by 10 and add cl */
435
    /* multiply result by 10 and add cl */
436
    mul bx    /* DX AX = AX * BX(10) */
436
    mul bx    /* DX AX = AX * BX(10) */
437
    jc OVERFLOW  /* overflow */
437
    jc OVERFLOW  /* overflow */
438
    add ax, cx
438
    add ax, cx
439
    /* if CF is set then overflow occurred (overflow part lands in DX) */
439
    /* if CF is set then overflow occurred (overflow part lands in DX) */
440
    jnc NEXTBYTE
440
    jnc NEXTBYTE
441
 
441
 
442
    OVERFLOW:
442
    OVERFLOW:
443
    xor cx, cx  /* make sure result is zeroed in case overflow occured */
443
    xor cx, cx  /* make sure result is zeroed in case overflow occured */
444
 
444
 
445
    FAIL:
445
    FAIL:
446
    inc [err]
446
    inc [err]
447
 
447
 
448
    DONE: /* save result (CX) into indirect memory address r */
448
    DONE: /* save result (CX) into indirect memory address r */
449
    mov bx, [r]
449
    mov bx, [r]
450
    mov [bx], cx
450
    mov [bx], cx
451
  }
451
  }
452
  return(err);
452
  return(err);
453
}
453
}
454
 
454
 
455
 
455
 
456
/* appends a backslash if path is a directory
456
/* appends a backslash if path is a directory
457
 * returns the (possibly updated) length of path */
457
 * returns the (possibly updated) length of path */
458
unsigned short path_appendbkslash_if_dir(char *path) {
458
unsigned short path_appendbkslash_if_dir(char *path) {
459
  unsigned short len;
459
  unsigned short len;
460
  int attr;
460
  int attr;
461
  for (len = 0; path[len] != 0; len++);
461
  for (len = 0; path[len] != 0; len++);
462
  if (len == 0) return(0);
462
  if (len == 0) return(0);
463
  if (path[len - 1] == '\\') return(len);
463
  if (path[len - 1] == '\\') return(len);
464
  /* */
464
  /* */
465
  attr = file_getattr(path);
465
  attr = file_getattr(path);
466
  if ((attr > 0) && (attr & DOS_ATTR_DIR)) {
466
  if ((attr > 0) && (attr & DOS_ATTR_DIR)) {
467
    path[len++] = '\\';
467
    path[len++] = '\\';
468
    path[len] = 0;
468
    path[len] = 0;
469
  }
469
  }
470
  return(len);
470
  return(len);
471
}
471
}
472
 
472
 
473
 
473
 
474
/* get current path drive d (A=1, B=2, etc - 0 is "current drive")
474
/* get current path drive d (A=1, B=2, etc - 0 is "current drive")
475
 * returns 0 on success, doserr otherwise */
475
 * returns 0 on success, doserr otherwise */
476
unsigned short curpathfordrv(char *buff, unsigned char d) {
476
unsigned short curpathfordrv(char *buff, unsigned char d) {
477
  unsigned short r = 0;
477
  unsigned short r = 0;
478
 
478
 
479
  _asm {
479
  _asm {
480
    /* is d == 0? then I need to resolve current drive */
480
    /* is d == 0? then I need to resolve current drive */
481
    cmp byte ptr [d], 0
481
    cmp byte ptr [d], 0
482
    jne GETCWD
482
    jne GETCWD
483
    /* resolve cur drive */
483
    /* resolve cur drive */
484
    mov ah, 0x19  /* get current default drive */
484
    mov ah, 0x19  /* get current default drive */
485
    int 0x21      /* al = drive (00h = A:, 01h = B:, etc) */
485
    int 0x21      /* al = drive (00h = A:, 01h = B:, etc) */
486
    inc al        /* convert to 1=A, 2=B, etc */
486
    inc al        /* convert to 1=A, 2=B, etc */
487
    mov [d], al
487
    mov [d], al
488
 
488
 
489
    GETCWD:
489
    GETCWD:
490
    /* prepend buff with drive:\ */
490
    /* prepend buff with drive:\ */
491
    mov si, buff
491
    mov si, buff
492
    mov dl, [d]
492
    mov dl, [d]
493
    mov [si], dl
493
    mov [si], dl
494
    add byte ptr [si], 'A' - 1
494
    add byte ptr [si], 'A' - 1
495
    inc si
495
    inc si
496
    mov [si], ':'
496
    mov [si], ':'
497
    inc si
497
    inc si
498
    mov [si], '\\'
498
    mov [si], '\\'
499
    inc si
499
    inc si
500
 
500
 
501
    mov ah, 0x47      /* get current directory of drv DL into DS:SI */
501
    mov ah, 0x47      /* get current directory of drv DL into DS:SI */
502
    int 0x21
502
    int 0x21
503
    jnc DONE
503
    jnc DONE
504
    mov [r], ax       /* copy result from ax */
504
    mov [r], ax       /* copy result from ax */
505
 
505
 
506
    DONE:
506
    DONE:
507
  }
507
  }
508
 
508
 
509
  return(r);
509
  return(r);
510
}
510
}
511
 
511
 
512
 
512
 
513
/* fills a nls_patterns struct with current NLS patterns, returns 0 on success, DOS errcode otherwise */
513
/* fills a nls_patterns struct with current NLS patterns, returns 0 on success, DOS errcode otherwise */
514
unsigned short nls_getpatterns(struct nls_patterns *p) {
514
unsigned short nls_getpatterns(struct nls_patterns *p) {
515
  unsigned short r = 0;
515
  unsigned short r = 0;
516
 
516
 
517
  _asm {
517
  _asm {
518
    mov ax, 0x3800  /* DOS 2+ -- Get Country Info for current country */
518
    mov ax, 0x3800  /* DOS 2+ -- Get Country Info for current country */
519
    mov dx, p       /* DS:DX points to the CountryInfoRec buffer */
519
    mov dx, p       /* DS:DX points to the CountryInfoRec buffer */
520
    int 0x21
520
    int 0x21
521
    jnc DONE
521
    jnc DONE
522
    mov [r], ax     /* copy DOS err code to r */
522
    mov [r], ax     /* copy DOS err code to r */
523
    DONE:
523
    DONE:
524
  }
524
  }
525
 
525
 
526
  return(r);
526
  return(r);
527
}
527
}
528
 
528
 
529
 
529
 
530
/* computes a formatted date based on NLS patterns found in p
530
/* computes a formatted date based on NLS patterns found in p
531
 * returns length of result */
531
 * returns length of result */
532
unsigned short nls_format_date(char *s, unsigned short yr, unsigned char mo, unsigned char dy, const struct nls_patterns *p) {
532
unsigned short nls_format_date(char *s, unsigned short yr, unsigned char mo, unsigned char dy, const struct nls_patterns *p) {
533
  unsigned short items[3];
533
  unsigned short items[3];
534
  /* preset date/month/year in proper order depending on date format */
534
  /* preset date/month/year in proper order depending on date format */
535
  switch (p->dateformat) {
535
  switch (p->dateformat) {
536
    case 0:  /* USA style: m d y */
536
    case 0:  /* USA style: m d y */
537
      items[0] = mo;
537
      items[0] = mo;
538
      items[1] = dy;
538
      items[1] = dy;
539
      items[2] = yr;
539
      items[2] = yr;
540
      break;
540
      break;
541
    case 1:  /* EU style: d m y */
541
    case 1:  /* EU style: d m y */
542
      items[0] = dy;
542
      items[0] = dy;
543
      items[1] = mo;
543
      items[1] = mo;
544
      items[2] = yr;
544
      items[2] = yr;
545
      break;
545
      break;
546
    case 2:  /* Japan-style: y m d */
546
    case 2:  /* Japan-style: y m d */
547
    default:
547
    default:
548
      items[0] = yr;
548
      items[0] = yr;
549
      items[1] = mo;
549
      items[1] = mo;
550
      items[2] = dy;
550
      items[2] = dy;
551
      break;
551
      break;
552
  }
552
  }
553
  /* compute the string */
553
  /* compute the string */
554
  return(sprintf(s, "%02u%s%02u%s%02u", items[0], p->datesep, items[1], p->datesep, items[2]));
554
  return(sprintf(s, "%02u%s%02u%s%02u", items[0], p->datesep, items[1], p->datesep, items[2]));
555
}
555
}
556
 
556
 
557
 
557
 
558
/* computes a formatted time based on NLS patterns found in p, sc are ignored if set 0xff
558
/* computes a formatted time based on NLS patterns found in p, sc are ignored if set 0xff
559
 * returns length of result */
559
 * returns length of result */
560
unsigned short nls_format_time(char *s, unsigned char ho, unsigned char mn, unsigned char sc, const struct nls_patterns *p) {
560
unsigned short nls_format_time(char *s, unsigned char ho, unsigned char mn, unsigned char sc, const struct nls_patterns *p) {
561
  char ampm = 0;
561
  char ampm = 0;
562
  unsigned short res;
562
  unsigned short res;
563
 
563
 
564
  if (p->timefmt == 0) {
564
  if (p->timefmt == 0) {
565
    if (ho == 12) {
565
    if (ho == 12) {
566
      ampm = 'p';
566
      ampm = 'p';
567
    } else if (ho > 12) {
567
    } else if (ho > 12) {
568
      ho -= 12;
568
      ho -= 12;
569
      ampm = 'p';
569
      ampm = 'p';
570
    } else { /* ho < 12 */
570
    } else { /* ho < 12 */
571
      if (ho == 0) ho = 12;
571
      if (ho == 0) ho = 12;
572
      ampm = 'a';
572
      ampm = 'a';
573
    }
573
    }
574
    res = sprintf(s, "%2u", ho);
574
    res = sprintf(s, "%2u", ho);
575
  } else {
575
  } else {
576
    res = sprintf(s, "%02u", ho);
576
    res = sprintf(s, "%02u", ho);
577
  }
577
  }
578
 
578
 
579
  /* append separator and minutes */
579
  /* append separator and minutes */
580
  res += sprintf(s + res, "%s%02u", p->timesep, mn);
580
  res += sprintf(s + res, "%s%02u", p->timesep, mn);
581
 
581
 
582
  /* if seconds provided, append them, too */
582
  /* if seconds provided, append them, too */
583
  if (sc != 0xff) res += sprintf(s + res, "%s%02u", p->timesep, sc);
583
  if (sc != 0xff) res += sprintf(s + res, "%s%02u", p->timesep, sc);
584
 
584
 
585
  /* finally append AM/PM char */
585
  /* finally append AM/PM char */
586
  if (ampm != 0) s[res++] = ampm;
586
  if (ampm != 0) s[res++] = ampm;
587
  s[res] = 0;
587
  s[res] = 0;
588
 
588
 
589
  return(res);
589
  return(res);
590
}
590
}
591
 
591
 
592
 
592
 
593
/* computes a formatted integer number based on NLS patterns found in p
593
/* computes a formatted integer number based on NLS patterns found in p
594
 * returns length of result */
594
 * returns length of result */
595
unsigned short nls_format_number(char *s, unsigned long num, const struct nls_patterns *p) {
595
unsigned short nls_format_number(char *s, unsigned long num, const struct nls_patterns *p) {
596
  unsigned short sl = 0, i;
596
  unsigned short sl = 0, i;
597
  unsigned char thcount = 0;
597
  unsigned char thcount = 0;
598
 
598
 
599
  /* write the value (reverse) with thousand separators (if any defined) */
599
  /* write the value (reverse) with thousand separators (if any defined) */
600
  do {
600
  do {
601
    if ((thcount == 3) && (p->thousep[0] != 0)) {
601
    if ((thcount == 3) && (p->thousep[0] != 0)) {
602
      s[sl++] = p->thousep[0];
602
      s[sl++] = p->thousep[0];
603
      thcount = 0;
603
      thcount = 0;
604
    }
604
    }
605
    s[sl++] = '0' + num % 10;
605
    s[sl++] = '0' + num % 10;
606
    num /= 10;
606
    num /= 10;
607
    thcount++;
607
    thcount++;
608
  } while (num > 0);
608
  } while (num > 0);
609
 
609
 
610
  /* terminate the string */
610
  /* terminate the string */
611
  s[sl] = 0;
611
  s[sl] = 0;
612
 
612
 
613
  /* reverse the string now (has been built in reverse) */
613
  /* reverse the string now (has been built in reverse) */
614
  for (i = sl / 2 + (sl & 1); i < sl; i++) {
614
  for (i = sl / 2 + (sl & 1); i < sl; i++) {
615
    thcount = s[i];
615
    thcount = s[i];
616
    s[i] = s[sl - (i + 1)];   /* abc'de  if i=3 then ' <-> c */
616
    s[i] = s[sl - (i + 1)];   /* abc'de  if i=3 then ' <-> c */
617
    s[sl - (i + 1)] = thcount;
617
    s[sl - (i + 1)] = thcount;
618
  }
618
  }
619
 
619
 
620
  return(sl);
620
  return(sl);
621
}
621
}
622
 
622
 
623
 
623
 
624
/* capitalize an ASCIZ string following country-dependent rules */
624
/* capitalize an ASCIZ string following country-dependent rules */
625
void nls_strtoup(char *buff) {
625
void nls_strtoup(char *buff) {
626
  unsigned short errcode = 0;
626
  unsigned short errcode = 0;
627
  /* requires DOS 4+ */
627
  /* requires DOS 4+ */
628
  _asm {
628
  _asm {
629
    push ax
629
    push ax
630
    push dx
630
    push dx
631
 
631
 
632
    mov ax, 0x6522 /* country-dependent capitalize string (DOS 4+) */
632
    mov ax, 0x6522 /* country-dependent capitalize string (DOS 4+) */
633
    mov dx, buff   /* DS:DX -> string to capitalize */
633
    mov dx, buff   /* DS:DX -> string to capitalize */
634
    int 0x21
634
    int 0x21
635
    jnc DONE
635
    jnc DONE
636
 
636
 
637
    mov errcode, ax /* set errcode on failure */
637
    mov errcode, ax /* set errcode on failure */
638
    DONE:
638
    DONE:
639
 
639
 
640
    pop dx
640
    pop dx
641
    pop ax
641
    pop ax
642
  }
642
  }
643
 
643
 
644
  /* rely on OpenWatcom's strupr() if DOS has no NLS support */
644
  /* rely on OpenWatcom's strupr() if DOS has no NLS support */
645
  if (errcode != 0) strupr(buff);
645
  if (errcode != 0) strupr(buff);
646
}
646
}
647
 
647
 
648
 
648
 
649
/* reload nls ressources from svarcom.lng into svarlang_mem */
649
/* reload nls ressources from svarcom.lng into svarlang_mem */
650
void nls_langreload(char *buff, unsigned short env) {
650
void nls_langreload(char *buff, unsigned short env) {
651
  const char far *nlspath;
651
  const char far *dosdir;
652
  const char far *lang;
652
  const char far *lang;
653
  static unsigned short lastlang;
653
  static unsigned short lastlang;
-
 
654
  unsigned short dosdirlen;
654
 
655
 
655
  /* look up the LANG env variable, upcase it and copy to lang */
656
  /* look up the LANG env variable, upcase it and copy to lang */
656
  lang = env_lookup_val(env, "LANG");
657
  lang = env_lookup_val(env, "LANG");
657
  if ((lang == NULL) || (lang[0] == 0)) return;
658
  if ((lang == NULL) || (lang[0] == 0)) return;
658
  _fmemcpy(buff, lang, 2);
659
  _fmemcpy(buff, lang, 2);
659
  buff[2] = 0;
660
  buff[2] = 0;
660
 
661
 
661
  /* check if there is need to reload at all */
662
  /* check if there is need to reload at all */
662
  if (memcmp(&lastlang, buff, 2) == 0) return;
663
  if (memcmp(&lastlang, buff, 2) == 0) return;
663
 
664
 
664
  buff[4] = 0;
665
  buff[4] = 0;
665
  nlspath = env_lookup_val(env, "NLSPATH");
666
  dosdir = env_lookup_val(env, "DOSDIR");
666
  if (nlspath != NULL) _fstrcpy(buff + 4, nlspath);
667
  if (dosdir == NULL) return;
667
 
668
 
-
 
669
  _fstrcpy(buff + 4, dosdir);
-
 
670
  dosdirlen = strlen(buff + 4);
-
 
671
  if (buff[4 + dosdirlen - 1] == '\\') dosdirlen--;
-
 
672
  memcpy(buff + 4 + dosdirlen, "\\SVARCOM.LNG", 13);
-
 
673
 
-
 
674
  /* try loading %DOSDIR%\SVARCOM.LNG */
-
 
675
  if (svarlang_load(buff + 4, buff) != 0) {
-
 
676
    /* failed! try %DOSDIR%\BIN\SVARCOM.LNG now */
-
 
677
    memcpy(buff + 4 + dosdirlen, "\\BIN\\SVARCOM.LNG", 17);
668
  if (svarlang_load("SVARCOM", buff, buff + 4) != 0) return;
678
    if (svarlang_load(buff + 4, buff) != 0) return;
-
 
679
  }
669
 
680
 
670
  _fmemcpy(&lastlang, lang, 2);
681
  _fmemcpy(&lastlang, lang, 2);
671
}
682
}
672
 
683
 
673
 
684
 
674
/* locates executable fname in path and fill res with result. returns 0 on success,
685
/* locates executable fname in path and fill res with result. returns 0 on success,
675
 * -1 on failed match and -2 on failed match + "don't even try with other paths"
686
 * -1 on failed match and -2 on failed match + "don't even try with other paths"
676
 * extptr is filled with a ptr to the extension in fname (NULL if no extension) */
687
 * extptr is filled with a ptr to the extension in fname (NULL if no extension) */
677
int lookup_cmd(char *res, const char *fname, const char *path, const char **extptr) {
688
int lookup_cmd(char *res, const char *fname, const char *path, const char **extptr) {
678
  unsigned short lastbslash = 0;
689
  unsigned short lastbslash = 0;
679
  unsigned short i, len;
690
  unsigned short i, len;
680
  unsigned char explicitpath = 0;
691
  unsigned char explicitpath = 0;
681
  const char *exec_ext[] = {"COM", "EXE", "BAT", NULL};
692
  const char *exec_ext[] = {"COM", "EXE", "BAT", NULL};
682
 
693
 
683
  /* does the original fname has an explicit path prefix or explicit ext? */
694
  /* does the original fname has an explicit path prefix or explicit ext? */
684
  *extptr = NULL;
695
  *extptr = NULL;
685
  for (i = 0; fname[i] != 0; i++) {
696
  for (i = 0; fname[i] != 0; i++) {
686
    switch (fname[i]) {
697
    switch (fname[i]) {
687
      case ':':
698
      case ':':
688
      case '\\':
699
      case '\\':
689
        explicitpath = 1;
700
        explicitpath = 1;
690
        *extptr = NULL; /* extension is the last dot AFTER all path delimiters */
701
        *extptr = NULL; /* extension is the last dot AFTER all path delimiters */
691
        break;
702
        break;
692
      case '.':
703
      case '.':
693
        *extptr = fname + i + 1;
704
        *extptr = fname + i + 1;
694
        break;
705
        break;
695
    }
706
    }
696
  }
707
  }
697
 
708
 
698
  /* if explicit ext found, make sure it is executable */
709
  /* if explicit ext found, make sure it is executable */
699
  if (*extptr != NULL) {
710
  if (*extptr != NULL) {
700
    for (i = 0; exec_ext[i] != NULL; i++) if (imatch(*extptr, exec_ext[i])) break;
711
    for (i = 0; exec_ext[i] != NULL; i++) if (imatch(*extptr, exec_ext[i])) break;
701
    if (exec_ext[i] == NULL) return(-2); /* bad extension - don't try running it ever */
712
    if (exec_ext[i] == NULL) return(-2); /* bad extension - don't try running it ever */
702
  }
713
  }
703
 
714
 
704
  /* normalize filename */
715
  /* normalize filename */
705
  if (file_truename(fname, res) != 0) return(-2);
716
  if (file_truename(fname, res) != 0) return(-2);
706
 
717
 
707
  /* printf("truename: %s\r\n", res); */
718
  /* printf("truename: %s\r\n", res); */
708
 
719
 
709
  /* figure out where the command starts */
720
  /* figure out where the command starts */
710
  for (len = 0; res[len] != 0; len++) {
721
  for (len = 0; res[len] != 0; len++) {
711
    switch (res[len]) {
722
    switch (res[len]) {
712
      case '?':   /* abort on any wildcard character */
723
      case '?':   /* abort on any wildcard character */
713
      case '*':
724
      case '*':
714
        return(-2);
725
        return(-2);
715
      case '\\':
726
      case '\\':
716
        lastbslash = len;
727
        lastbslash = len;
717
        break;
728
        break;
718
    }
729
    }
719
  }
730
  }
720
 
731
 
721
  /* printf("lastbslash=%u\r\n", lastbslash); */
732
  /* printf("lastbslash=%u\r\n", lastbslash); */
722
 
733
 
723
  /* if no path prefix was found in fname (no colon or backslash) AND we have
734
  /* if no path prefix was found in fname (no colon or backslash) AND we have
724
   * a path arg, then assemble path+filename */
735
   * a path arg, then assemble path+filename */
725
  if ((!explicitpath) && (path != NULL) && (path[0] != 0)) {
736
  if ((!explicitpath) && (path != NULL) && (path[0] != 0)) {
726
    i = strlen(path);
737
    i = strlen(path);
727
    if (path[i - 1] != '\\') i++; /* add a byte for inserting a bkslash after path */
738
    if (path[i - 1] != '\\') i++; /* add a byte for inserting a bkslash after path */
728
    /* move the filename at the place where path will end */
739
    /* move the filename at the place where path will end */
729
    memmove(res + i, res + lastbslash + 1, len - lastbslash);
740
    memmove(res + i, res + lastbslash + 1, len - lastbslash);
730
    /* copy path in front of the filename and make sure there is a bkslash sep */
741
    /* copy path in front of the filename and make sure there is a bkslash sep */
731
    memmove(res, path, i);
742
    memmove(res, path, i);
732
    res[i - 1] = '\\';
743
    res[i - 1] = '\\';
733
  }
744
  }
734
 
745
 
735
  /* if no extension was initially provided, try matching COM, EXE, BAT */
746
  /* if no extension was initially provided, try matching COM, EXE, BAT */
736
  if (*extptr == NULL) {
747
  if (*extptr == NULL) {
737
    int attr;
748
    int attr;
738
    len = strlen(res);
749
    len = strlen(res);
739
    res[len++] = '.';
750
    res[len++] = '.';
740
    for (i = 0; exec_ext[i] != NULL; i++) {
751
    for (i = 0; exec_ext[i] != NULL; i++) {
741
      strcpy(res + len, exec_ext[i]);
752
      strcpy(res + len, exec_ext[i]);
742
      /* printf("? '%s'\r\n", res); */
753
      /* printf("? '%s'\r\n", res); */
743
      *extptr = exec_ext[i];
754
      *extptr = exec_ext[i];
744
      attr = file_getattr(res);
755
      attr = file_getattr(res);
745
      if (attr < 0) continue; /* file not found */
756
      if (attr < 0) continue; /* file not found */
746
      if (attr & DOS_ATTR_DIR) continue; /* this is a directory */
757
      if (attr & DOS_ATTR_DIR) continue; /* this is a directory */
747
      if (attr & DOS_ATTR_VOL) continue; /* this is a volume */
758
      if (attr & DOS_ATTR_VOL) continue; /* this is a volume */
748
      return(0);
759
      return(0);
749
    }
760
    }
750
  } else { /* try finding it as-is */
761
  } else { /* try finding it as-is */
751
    /* printf("? '%s'\r\n", res); */
762
    /* printf("? '%s'\r\n", res); */
752
    int attr = file_getattr(res);
763
    int attr = file_getattr(res);
753
    if ((attr >= 0) &&  /* file exists */
764
    if ((attr >= 0) &&  /* file exists */
754
        ((attr & DOS_ATTR_DIR) == 0) && /* is not a directory */
765
        ((attr & DOS_ATTR_DIR) == 0) && /* is not a directory */
755
        ((attr & DOS_ATTR_VOL) == 0)) { /* is not a volume */
766
        ((attr & DOS_ATTR_VOL) == 0)) { /* is not a volume */
756
      return(0);
767
      return(0);
757
    }
768
    }
758
  }
769
  }
759
 
770
 
760
  /* not found */
771
  /* not found */
761
  if (explicitpath) return(-2); /* don't bother trying other paths, the caller had its own path preset anyway */
772
  if (explicitpath) return(-2); /* don't bother trying other paths, the caller had its own path preset anyway */
762
  return(-1);
773
  return(-1);
763
}
774
}
764
 
775
 
765
 
776
 
766
/* fills fname with the path and filename to the linkfile related to the
777
/* fills fname with the path and filename to the linkfile related to the
767
 * executable link "linkname". returns 0 on success. */
778
 * executable link "linkname". returns 0 on success. */
768
int link_computefname(char *fname, const char *linkname, unsigned short env_seg) {
779
int link_computefname(char *fname, const char *linkname, unsigned short env_seg) {
769
  unsigned short pathlen, doserr = 0;
780
  unsigned short pathlen, doserr = 0;
770
 
781
 
771
  /* fetch %DOSDIR% */
782
  /* fetch %DOSDIR% */
772
  pathlen = env_lookup_valcopy(fname, 128, env_seg, "DOSDIR");
783
  pathlen = env_lookup_valcopy(fname, 128, env_seg, "DOSDIR");
773
  if (pathlen == 0) {
784
  if (pathlen == 0) {
774
    nls_outputnl(29,5); /* "%DOSDIR% not defined" */
785
    nls_outputnl(29,5); /* "%DOSDIR% not defined" */
775
    return(-1);
786
    return(-1);
776
  }
787
  }
777
 
788
 
778
  /* prep filename: %DOSDIR%\LINKS\PKG.LNK */
789
  /* prep filename: %DOSDIR%\LINKS\PKG.LNK */
779
  if (fname[pathlen - 1] == '\\') pathlen--;
790
  if (fname[pathlen - 1] == '\\') pathlen--;
780
  pathlen += sprintf(fname + pathlen, "\\LINKS");
791
  pathlen += sprintf(fname + pathlen, "\\LINKS");
781
  /* create \LINKS if not exists */
792
  /* create \LINKS if not exists */
782
  if (file_getattr(fname) < 0) {
793
  if (file_getattr(fname) < 0) {
783
    _asm {
794
    _asm {
784
      push dx
795
      push dx
785
      mov ah, 0x39
796
      mov ah, 0x39
786
      mov dx, fname
797
      mov dx, fname
787
      int 0x21
798
      int 0x21
788
      jnc DONE
799
      jnc DONE
789
      mov doserr, ax
800
      mov doserr, ax
790
      DONE:
801
      DONE:
791
      pop dx
802
      pop dx
792
    }
803
    }
793
    if (doserr) {
804
    if (doserr) {
794
      output(fname);
805
      output(fname);
795
      output(" - ");
806
      output(" - ");
796
      nls_outputnl(255, doserr);
807
      nls_outputnl(255, doserr);
797
      return(-1);
808
      return(-1);
798
    }
809
    }
799
  }
810
  }
800
  /* quit early if dir does not exist (or is not a dir) */
811
  /* quit early if dir does not exist (or is not a dir) */
801
  if (file_getattr(fname) != DOS_ATTR_DIR) {
812
  if (file_getattr(fname) != DOS_ATTR_DIR) {
802
    output(fname);
813
    output(fname);
803
    output(" - ");
814
    output(" - ");
804
    nls_outputnl(255,3); /* path not found */
815
    nls_outputnl(255,3); /* path not found */
805
    return(-1);
816
    return(-1);
806
  }
817
  }
807
  sprintf(fname + pathlen, "\\%s.LNK", linkname);
818
  sprintf(fname + pathlen, "\\%s.LNK", linkname);
808
 
819
 
809
  return(0);
820
  return(0);
810
}
821
}
811
 
822