Subversion Repositories SvarDOS

Rev

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

Rev 968 Rev 985
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-2022 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 = 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) {
128
  if (ptr == NULL) {
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 key = 0;
202
  char key = 0;
203
 
203
 
204
  AGAIN:
204
  AGAIN:
205
  output(s);
205
  output(s);
206
  output(" ");
206
  output(" ");
207
 
207
 
208
  _asm {
208
  _asm {
209
    push ax
209
    push ax
210
    push dx
210
    push dx
211
 
211
 
212
    mov ax, 0x0c01 /* clear input buffer and execute getchar (INT 21h,AH=1) */
212
    mov ax, 0x0c01 /* clear input buffer and execute getchar (INT 21h,AH=1) */
213
    int 0x21
213
    int 0x21
214
    /* if AL == 0 then this is an extended character */
214
    /* if AL == 0 then this is an extended character */
215
    test al, al
215
    test al, al
216
    jnz GOTCHAR
216
    jnz GOTCHAR
217
    mov ah, 0x08   /* read again to flush extended char from input buffer */
217
    mov ah, 0x08   /* read again to flush extended char from input buffer */
218
    int 0x21
218
    int 0x21
219
    xor al, al     /* all extended chars are ignored */
219
    xor al, al     /* all extended chars are ignored */
220
    GOTCHAR:       /* received key is in AL now */
220
    GOTCHAR:       /* received key is in AL now */
221
    mov [key], al  /* save key */
221
    mov [key], al  /* save key */
222
 
222
 
223
    /* print a cr/lf */
223
    /* print a cr/lf */
224
    mov ah, 0x02
224
    mov ah, 0x02
225
    mov dl, 0x0D
225
    mov dl, 0x0D
226
    int 0x21
226
    int 0x21
227
    mov dl, 0x0A
227
    mov dl, 0x0A
228
    int 0x21
228
    int 0x21
229
 
229
 
230
    pop dx
230
    pop dx
231
    pop ax
231
    pop ax
232
  }
232
  }
233
 
233
 
234
  /* ucase() result */
234
  /* ucase() result */
235
  if ((key >= 'a') && (key <= 'z')) key -= ('a' - 'A');
235
  if ((key >= 'a') && (key <= 'z')) key -= ('a' - 'A');
236
 
236
 
237
  /* is there a match? */
237
  /* is there a match? */
238
  for (res = 0; c[res] != 0; res++) if (c[res] == key) return(res);
238
  for (res = 0; c[res] != 0; res++) if (c[res] == key) return(res);
239
 
239
 
240
  goto AGAIN;
240
  goto AGAIN;
241
}
241
}
242
 
242
 
243
 
243
 
244
/* converts a path to its canonic representation, returns 0 on success
244
/* converts a path to its canonic representation, returns 0 on success
245
 * or DOS err on failure (invalid drive) */
245
 * or DOS err on failure (invalid drive) */
246
unsigned short file_truename(const char *src, char *dst) {
246
unsigned short file_truename(const char *src, char *dst) {
247
  unsigned short res = 0;
247
  unsigned short res = 0;
248
  _asm {
248
  _asm {
249
    push es
249
    push es
250
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
250
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
251
    push ds
251
    push ds
252
    pop es
252
    pop es
253
    mov si, src
253
    mov si, src
254
    mov di, dst
254
    mov di, dst
255
    int 0x21
255
    int 0x21
256
    jnc DONE
256
    jnc DONE
257
    mov [res], ax
257
    mov [res], ax
258
    DONE:
258
    DONE:
259
    pop es
259
    pop es
260
  }
260
  }
261
  return(res);
261
  return(res);
262
}
262
}
263
 
263
 
264
 
264
 
265
/* returns DOS attributes of file, or -1 on error */
265
/* returns DOS attributes of file, or -1 on error */
266
int file_getattr(const char *fname) {
266
int file_getattr(const char *fname) {
267
  int res = -1;
267
  int res = -1;
268
  _asm {
268
  _asm {
269
    mov ax, 0x4300  /* query file attributes, fname at DS:DX */
269
    mov ax, 0x4300  /* query file attributes, fname at DS:DX */
270
    mov dx, fname
270
    mov dx, fname
271
    int 0x21        /* CX=attributes if CF=0, otherwise AX=errno */
271
    int 0x21        /* CX=attributes if CF=0, otherwise AX=errno */
272
    jc DONE
272
    jc DONE
273
    mov [res], cx
273
    mov [res], cx
274
    DONE:
274
    DONE:
275
  }
275
  }
276
  return(res);
276
  return(res);
277
}
277
}
278
 
278
 
279
 
279
 
280
/* returns screen's width (in columns) */
280
/* returns screen's width (in columns) */
281
unsigned short screen_getwidth(void) {
281
unsigned short screen_getwidth(void) {
282
  /* BIOS 0040:004A = word containing screen width in text columns */
282
  /* BIOS 0040:004A = word containing screen width in text columns */
283
  unsigned short far *scrw = MK_FP(0x40, 0x4a);
283
  unsigned short far *scrw = MK_FP(0x40, 0x4a);
284
  return(*scrw);
284
  return(*scrw);
285
}
285
}
286
 
286
 
287
 
287
 
288
/* returns screen's height (in rows) */
288
/* returns screen's height (in rows) */
289
unsigned short screen_getheight(void) {
289
unsigned short screen_getheight(void) {
290
  /* BIOS 0040:0084 = byte containing maximum valid row value (EGA ONLY) */
290
  /* BIOS 0040:0084 = byte containing maximum valid row value (EGA ONLY) */
291
  unsigned char far *scrh = MK_FP(0x40, 0x84);
291
  unsigned char far *scrh = MK_FP(0x40, 0x84);
292
  if (*scrh == 0) return(25);  /* pre-EGA adapter */
292
  if (*scrh == 0) return(25);  /* pre-EGA adapter */
293
  return(*scrh + 1);
293
  return(*scrh + 1);
294
}
294
}
295
 
295
 
296
 
296
 
297
/* displays the "Press any key to continue" msg and waits for a keypress */
297
/* displays the "Press any key to continue" msg and waits for a keypress */
298
void press_any_key(void) {
298
void press_any_key(void) {
299
  nls_output(15, 1); /* Press any key to continue... */
299
  nls_output(15, 1); /* Press any key to continue... */
300
  _asm {
300
  _asm {
301
    mov ah, 0x08  /* no echo console input */
301
    mov ah, 0x08  /* no echo console input */
302
    int 0x21      /* pressed key in AL now (0 for extended keys) */
302
    int 0x21      /* pressed key in AL now (0 for extended keys) */
303
    test al, al
303
    test al, al
304
    jnz DONE
304
    jnz DONE
305
    int 0x21      /* executed ah=8 again to read the rest of extended key */
305
    int 0x21      /* executed ah=8 again to read the rest of extended key */
306
    DONE:
306
    DONE:
307
    /* output CR/LF */
307
    /* output CR/LF */
308
    mov ah, 0x02
308
    mov ah, 0x02
309
    mov dl, 0x0D
309
    mov dl, 0x0D
310
    int 0x21
310
    int 0x21
311
    mov dl, 0x0A
311
    mov dl, 0x0A
312
    int 0x21
312
    int 0x21
313
  }
313
  }
314
}
314
}
315
 
315
 
316
 
316
 
317
/* validate a drive (A=0, B=1, etc). returns 1 if valid, 0 otherwise */
317
/* validate a drive (A=0, B=1, etc). returns 1 if valid, 0 otherwise */
318
int isdrivevalid(unsigned char drv) {
318
int isdrivevalid(unsigned char drv) {
319
  _asm {
319
  _asm {
320
    mov ah, 0x19  /* query default (current) disk */
320
    mov ah, 0x19  /* query default (current) disk */
321
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
321
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
322
    mov ch, al    /* save current drive to ch */
322
    mov ch, al    /* save current drive to ch */
323
    /* try setting up the drive as current */
323
    /* try setting up the drive as current */
324
    mov ah, 0x0E   /* select default drive */
324
    mov ah, 0x0E   /* select default drive */
325
    mov dl, [drv]  /* 0=A, 1=B, etc */
325
    mov dl, [drv]  /* 0=A, 1=B, etc */
326
    int 0x21
326
    int 0x21
327
    /* this call does not set CF on error, I must check cur drive to look for success */
327
    /* this call does not set CF on error, I must check cur drive to look for success */
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 [drv], 1  /* preset result as success */
330
    mov [drv], 1  /* preset result as success */
331
    cmp al, dl    /* is eq? */
331
    cmp al, dl    /* is eq? */
332
    je DONE
332
    je DONE
333
    mov [drv], 0  /* fail */
333
    mov [drv], 0  /* fail */
334
    jmp FAILED
334
    jmp FAILED
335
    DONE:
335
    DONE:
336
    /* set current drive back to what it was initially */
336
    /* set current drive back to what it was initially */
337
    mov ah, 0x0E
337
    mov ah, 0x0E
338
    mov dl, ch
338
    mov dl, ch
339
    int 0x21
339
    int 0x21
340
    FAILED:
340
    FAILED:
341
  }
341
  }
342
  return(drv);
342
  return(drv);
343
}
343
}
344
 
344
 
345
 
345
 
346
/* converts a 8+3 filename into 11-bytes FCB format (MYFILE  EXT) */
346
/* converts a 8+3 filename into 11-bytes FCB format (MYFILE  EXT) */
347
void file_fname2fcb(char *dst, const char *src) {
347
void file_fname2fcb(char *dst, const char *src) {
348
  unsigned short i;
348
  unsigned short i;
349
 
349
 
350
  /* fill dst with 11 spaces and a NULL terminator */
350
  /* fill dst with 11 spaces and a NULL terminator */
351
  for (i = 0; i < 11; i++) dst[i] = ' ';
351
  for (i = 0; i < 11; i++) dst[i] = ' ';
352
  dst[11] = 0;
352
  dst[11] = 0;
353
 
353
 
354
  /* copy fname until dot (.) or 8 characters */
354
  /* copy fname until dot (.) or 8 characters */
355
  for (i = 0; i < 8; i++) {
355
  for (i = 0; i < 8; i++) {
356
    if ((src[i] == '.') || (src[i] == 0)) break;
356
    if ((src[i] == '.') || (src[i] == 0)) break;
357
    dst[i] = src[i];
357
    dst[i] = src[i];
358
  }
358
  }
359
 
359
 
360
  /* advance src until extension or end of string */
360
  /* advance src until extension or end of string */
361
  src += i;
361
  src += i;
362
  for (;;) {
362
  for (;;) {
363
    if (*src == '.') {
363
    if (*src == '.') {
364
      src++; /* next character is extension */
364
      src++; /* next character is extension */
365
      break;
365
      break;
366
    }
366
    }
367
    if (*src == 0) break;
367
    if (*src == 0) break;
368
  }
368
  }
369
 
369
 
370
  /* copy extension to dst (3 chars max) */
370
  /* copy extension to dst (3 chars max) */
371
  dst += 8;
371
  dst += 8;
372
  for (i = 0; i < 3; i++) {
372
  for (i = 0; i < 3; i++) {
373
    if (src[i] == 0) break;
373
    if (src[i] == 0) break;
374
    dst[i] = src[i];
374
    dst[i] = src[i];
375
  }
375
  }
376
}
376
}
377
 
377
 
378
 
378
 
379
/* converts a 11-bytes FCB filename (MYFILE  EXT) into 8+3 format (MYFILE.EXT) */
379
/* converts a 11-bytes FCB filename (MYFILE  EXT) into 8+3 format (MYFILE.EXT) */
380
void file_fcb2fname(char *dst, const char *src) {
380
void file_fcb2fname(char *dst, const char *src) {
381
  unsigned short i, end = 0;
381
  unsigned short i, end = 0;
382
 
382
 
383
  for (i = 0; i < 8; i++) {
383
  for (i = 0; i < 8; i++) {
384
    dst[i] = src[i];
384
    dst[i] = src[i];
385
    if (dst[i] != ' ') end = i + 1;
385
    if (dst[i] != ' ') end = i + 1;
386
  }
386
  }
387
 
387
 
388
  /* is there an extension? */
388
  /* is there an extension? */
389
  if (src[8] == ' ') {
389
  if (src[8] == ' ') {
390
    dst[end] = 0;
390
    dst[end] = 0;
391
  } else { /* found extension: copy it until first space */
391
  } else { /* found extension: copy it until first space */
392
    dst[end++] = '.';
392
    dst[end++] = '.';
393
    for (i = 8; i < 11; i++) {
393
    for (i = 8; i < 11; i++) {
394
      if (src[i] == ' ') break;
394
      if (src[i] == ' ') break;
395
      dst[end++] = src[i];
395
      dst[end++] = src[i];
396
    }
396
    }
397
    dst[end] = 0;
397
    dst[end] = 0;
398
  }
398
  }
399
}
399
}
400
 
400
 
401
 
401
 
402
/* converts an ASCIIZ string into an unsigned short. returns 0 on success.
402
/* converts an ASCIIZ string into an unsigned short. returns 0 on success.
403
 * on error, result will contain all valid digits that were read until
403
 * on error, result will contain all valid digits that were read until
404
 * error occurred (0 on overflow or if parsing failed immediately) */
404
 * error occurred (0 on overflow or if parsing failed immediately) */
405
int atous(unsigned short *r, const char *s) {
405
int atous(unsigned short *r, const char *s) {
406
  int err = 0;
406
  int err = 0;
407
 
407
 
408
  _asm {
408
  _asm {
409
    mov si, s
409
    mov si, s
410
    xor ax, ax  /* general purpose register */
410
    xor ax, ax  /* general purpose register */
411
    xor cx, cx  /* contains the result */
411
    xor cx, cx  /* contains the result */
412
    mov bx, 10  /* used as a multiplicative step */
412
    mov bx, 10  /* used as a multiplicative step */
413
 
413
 
414
    NEXTBYTE:
414
    NEXTBYTE:
415
    xchg cx, ax /* move result into cx temporarily */
415
    xchg cx, ax /* move result into cx temporarily */
416
    lodsb  /* AL = DS:[SI++] */
416
    lodsb  /* AL = DS:[SI++] */
417
    /* is AL 0? if so we're done */
417
    /* is AL 0? if so we're done */
418
    test al, al
418
    test al, al
419
    jz DONE
419
    jz DONE
420
    /* validate that AL is in range '0'-'9' */
420
    /* validate that AL is in range '0'-'9' */
421
    sub al, '0'
421
    sub al, '0'
422
    jc FAIL   /* invalid character detected */
422
    jc FAIL   /* invalid character detected */
423
    cmp al, 9
423
    cmp al, 9
424
    jg FAIL   /* invalid character detected */
424
    jg FAIL   /* invalid character detected */
425
    /* restore result into AX (CX contains the new digit) */
425
    /* restore result into AX (CX contains the new digit) */
426
    xchg cx, ax
426
    xchg cx, ax
427
    /* multiply result by 10 and add cl */
427
    /* multiply result by 10 and add cl */
428
    mul bx    /* DX AX = AX * BX(10) */
428
    mul bx    /* DX AX = AX * BX(10) */
429
    jc OVERFLOW  /* overflow */
429
    jc OVERFLOW  /* overflow */
430
    add ax, cx
430
    add ax, cx
431
    /* if CF is set then overflow occurred (overflow part lands in DX) */
431
    /* if CF is set then overflow occurred (overflow part lands in DX) */
432
    jnc NEXTBYTE
432
    jnc NEXTBYTE
433
 
433
 
434
    OVERFLOW:
434
    OVERFLOW:
435
    xor cx, cx  /* make sure result is zeroed in case overflow occured */
435
    xor cx, cx  /* make sure result is zeroed in case overflow occured */
436
 
436
 
437
    FAIL:
437
    FAIL:
438
    inc [err]
438
    inc [err]
439
 
439
 
440
    DONE: /* save result (CX) into indirect memory address r */
440
    DONE: /* save result (CX) into indirect memory address r */
441
    mov bx, [r]
441
    mov bx, [r]
442
    mov [bx], cx
442
    mov [bx], cx
443
  }
443
  }
444
  return(err);
444
  return(err);
445
}
445
}
446
 
446
 
447
 
447
 
448
/* appends a backslash if path is a directory
448
/* appends a backslash if path is a directory
449
 * returns the (possibly updated) length of path */
449
 * returns the (possibly updated) length of path */
450
unsigned short path_appendbkslash_if_dir(char *path) {
450
unsigned short path_appendbkslash_if_dir(char *path) {
451
  unsigned short len;
451
  unsigned short len;
452
  int attr;
452
  int attr;
453
  for (len = 0; path[len] != 0; len++);
453
  for (len = 0; path[len] != 0; len++);
454
  if (len == 0) return(0);
454
  if (len == 0) return(0);
455
  if (path[len - 1] == '\\') return(len);
455
  if (path[len - 1] == '\\') return(len);
456
  /* */
456
  /* */
457
  attr = file_getattr(path);
457
  attr = file_getattr(path);
458
  if ((attr > 0) && (attr & DOS_ATTR_DIR)) {
458
  if ((attr > 0) && (attr & DOS_ATTR_DIR)) {
459
    path[len++] = '\\';
459
    path[len++] = '\\';
460
    path[len] = 0;
460
    path[len] = 0;
461
  }
461
  }
462
  return(len);
462
  return(len);
463
}
463
}
464
 
464
 
465
 
465
 
466
/* get current path drive d (A=1, B=2, etc - 0 is "current drive")
466
/* get current path drive d (A=1, B=2, etc - 0 is "current drive")
467
 * returns 0 on success, doserr otherwise */
467
 * returns 0 on success, doserr otherwise */
468
unsigned short curpathfordrv(char *buff, unsigned char d) {
468
unsigned short curpathfordrv(char *buff, unsigned char d) {
469
  unsigned short r = 0;
469
  unsigned short r = 0;
470
 
470
 
471
  _asm {
471
  _asm {
472
    /* is d == 0? then I need to resolve current drive */
472
    /* is d == 0? then I need to resolve current drive */
473
    cmp byte ptr [d], 0
473
    cmp byte ptr [d], 0
474
    jne GETCWD
474
    jne GETCWD
475
    /* resolve cur drive */
475
    /* resolve cur drive */
476
    mov ah, 0x19  /* get current default drive */
476
    mov ah, 0x19  /* get current default drive */
477
    int 0x21      /* al = drive (00h = A:, 01h = B:, etc) */
477
    int 0x21      /* al = drive (00h = A:, 01h = B:, etc) */
478
    inc al        /* convert to 1=A, 2=B, etc */
478
    inc al        /* convert to 1=A, 2=B, etc */
479
    mov [d], al
479
    mov [d], al
480
 
480
 
481
    GETCWD:
481
    GETCWD:
482
    /* prepend buff with drive:\ */
482
    /* prepend buff with drive:\ */
483
    mov si, buff
483
    mov si, buff
484
    mov dl, [d]
484
    mov dl, [d]
485
    mov [si], dl
485
    mov [si], dl
486
    add byte ptr [si], 'A' - 1
486
    add byte ptr [si], 'A' - 1
487
    inc si
487
    inc si
488
    mov [si], ':'
488
    mov [si], ':'
489
    inc si
489
    inc si
490
    mov [si], '\\'
490
    mov [si], '\\'
491
    inc si
491
    inc si
492
 
492
 
493
    mov ah, 0x47      /* get current directory of drv DL into DS:SI */
493
    mov ah, 0x47      /* get current directory of drv DL into DS:SI */
494
    int 0x21
494
    int 0x21
495
    jnc DONE
495
    jnc DONE
496
    mov [r], ax       /* copy result from ax */
496
    mov [r], ax       /* copy result from ax */
497
 
497
 
498
    DONE:
498
    DONE:
499
  }
499
  }
500
 
500
 
501
  return(r);
501
  return(r);
502
}
502
}
503
 
503
 
504
 
504
 
505
/* fills a nls_patterns struct with current NLS patterns, returns 0 on success, DOS errcode otherwise */
505
/* fills a nls_patterns struct with current NLS patterns, returns 0 on success, DOS errcode otherwise */
506
unsigned short nls_getpatterns(struct nls_patterns *p) {
506
unsigned short nls_getpatterns(struct nls_patterns *p) {
507
  unsigned short r = 0;
507
  unsigned short r = 0;
508
 
508
 
509
  _asm {
509
  _asm {
510
    mov ax, 0x3800  /* DOS 2+ -- Get Country Info for current country */
510
    mov ax, 0x3800  /* DOS 2+ -- Get Country Info for current country */
511
    mov dx, p       /* DS:DX points to the CountryInfoRec buffer */
511
    mov dx, p       /* DS:DX points to the CountryInfoRec buffer */
512
    int 0x21
512
    int 0x21
513
    jnc DONE
513
    jnc DONE
514
    mov [r], ax     /* copy DOS err code to r */
514
    mov [r], ax     /* copy DOS err code to r */
515
    DONE:
515
    DONE:
516
  }
516
  }
517
 
517
 
518
  return(r);
518
  return(r);
519
}
519
}
520
 
520
 
521
 
521
 
522
/* computes a formatted date based on NLS patterns found in p
522
/* computes a formatted date based on NLS patterns found in p
523
 * returns length of result */
523
 * returns length of result */
524
unsigned short nls_format_date(char *s, unsigned short yr, unsigned char mo, unsigned char dy, const struct nls_patterns *p) {
524
unsigned short nls_format_date(char *s, unsigned short yr, unsigned char mo, unsigned char dy, const struct nls_patterns *p) {
525
  unsigned short items[3];
525
  unsigned short items[3];
526
  /* preset date/month/year in proper order depending on date format */
526
  /* preset date/month/year in proper order depending on date format */
527
  switch (p->dateformat) {
527
  switch (p->dateformat) {
528
    case 0:  /* USA style: m d y */
528
    case 0:  /* USA style: m d y */
529
      items[0] = mo;
529
      items[0] = mo;
530
      items[1] = dy;
530
      items[1] = dy;
531
      items[2] = yr;
531
      items[2] = yr;
532
      break;
532
      break;
533
    case 1:  /* EU style: d m y */
533
    case 1:  /* EU style: d m y */
534
      items[0] = dy;
534
      items[0] = dy;
535
      items[1] = mo;
535
      items[1] = mo;
536
      items[2] = yr;
536
      items[2] = yr;
537
      break;
537
      break;
538
    case 2:  /* Japan-style: y m d */
538
    case 2:  /* Japan-style: y m d */
539
    default:
539
    default:
540
      items[0] = yr;
540
      items[0] = yr;
541
      items[1] = mo;
541
      items[1] = mo;
542
      items[2] = dy;
542
      items[2] = dy;
543
      break;
543
      break;
544
  }
544
  }
545
  /* compute the string */
545
  /* compute the string */
546
  return(sprintf(s, "%02u%s%02u%s%02u", items[0], p->datesep, items[1], p->datesep, items[2]));
546
  return(sprintf(s, "%02u%s%02u%s%02u", items[0], p->datesep, items[1], p->datesep, items[2]));
547
}
547
}
548
 
548
 
549
 
549
 
550
/* computes a formatted time based on NLS patterns found in p, sc are ignored if set 0xff
550
/* computes a formatted time based on NLS patterns found in p, sc are ignored if set 0xff
551
 * returns length of result */
551
 * returns length of result */
552
unsigned short nls_format_time(char *s, unsigned char ho, unsigned char mn, unsigned char sc, const struct nls_patterns *p) {
552
unsigned short nls_format_time(char *s, unsigned char ho, unsigned char mn, unsigned char sc, const struct nls_patterns *p) {
553
  char ampm = 0;
553
  char ampm = 0;
554
  unsigned short res;
554
  unsigned short res;
555
 
555
 
556
  if (p->timefmt == 0) {
556
  if (p->timefmt == 0) {
557
    if (ho == 12) {
557
    if (ho == 12) {
558
      ampm = 'p';
558
      ampm = 'p';
559
    } else if (ho > 12) {
559
    } else if (ho > 12) {
560
      ho -= 12;
560
      ho -= 12;
561
      ampm = 'p';
561
      ampm = 'p';
562
    } else { /* ho < 12 */
562
    } else { /* ho < 12 */
563
      if (ho == 0) ho = 12;
563
      if (ho == 0) ho = 12;
564
      ampm = 'a';
564
      ampm = 'a';
565
    }
565
    }
566
    res = sprintf(s, "%2u", ho);
566
    res = sprintf(s, "%2u", ho);
567
  } else {
567
  } else {
568
    res = sprintf(s, "%02u", ho);
568
    res = sprintf(s, "%02u", ho);
569
  }
569
  }
570
 
570
 
571
  /* append separator and minutes */
571
  /* append separator and minutes */
572
  res += sprintf(s + res, "%s%02u", p->timesep, mn);
572
  res += sprintf(s + res, "%s%02u", p->timesep, mn);
573
 
573
 
574
  /* if seconds provided, append them, too */
574
  /* if seconds provided, append them, too */
575
  if (sc != 0xff) res += sprintf(s + res, "%s%02u", p->timesep, sc);
575
  if (sc != 0xff) res += sprintf(s + res, "%s%02u", p->timesep, sc);
576
 
576
 
577
  /* finally append AM/PM char */
577
  /* finally append AM/PM char */
578
  if (ampm != 0) s[res++] = ampm;
578
  if (ampm != 0) s[res++] = ampm;
579
  s[res] = 0;
579
  s[res] = 0;
580
 
580
 
581
  return(res);
581
  return(res);
582
}
582
}
583
 
583
 
584
 
584
 
585
/* computes a formatted integer number based on NLS patterns found in p
585
/* computes a formatted integer number based on NLS patterns found in p
586
 * returns length of result */
586
 * returns length of result */
587
unsigned short nls_format_number(char *s, unsigned long num, const struct nls_patterns *p) {
587
unsigned short nls_format_number(char *s, unsigned long num, const struct nls_patterns *p) {
588
  unsigned short sl = 0, i;
588
  unsigned short sl = 0, i;
589
  unsigned char thcount = 0;
589
  unsigned char thcount = 0;
590
 
590
 
591
  /* write the value (reverse) with thousand separators (if any defined) */
591
  /* write the value (reverse) with thousand separators (if any defined) */
592
  do {
592
  do {
593
    if ((thcount == 3) && (p->thousep[0] != 0)) {
593
    if ((thcount == 3) && (p->thousep[0] != 0)) {
594
      s[sl++] = p->thousep[0];
594
      s[sl++] = p->thousep[0];
595
      thcount = 0;
595
      thcount = 0;
596
    }
596
    }
597
    s[sl++] = '0' + num % 10;
597
    s[sl++] = '0' + num % 10;
598
    num /= 10;
598
    num /= 10;
599
    thcount++;
599
    thcount++;
600
  } while (num > 0);
600
  } while (num > 0);
601
 
601
 
602
  /* terminate the string */
602
  /* terminate the string */
603
  s[sl] = 0;
603
  s[sl] = 0;
604
 
604
 
605
  /* reverse the string now (has been built in reverse) */
605
  /* reverse the string now (has been built in reverse) */
606
  for (i = sl / 2 + (sl & 1); i < sl; i++) {
606
  for (i = sl / 2 + (sl & 1); i < sl; i++) {
607
    thcount = s[i];
607
    thcount = s[i];
608
    s[i] = s[sl - (i + 1)];   /* abc'de  if i=3 then ' <-> c */
608
    s[i] = s[sl - (i + 1)];   /* abc'de  if i=3 then ' <-> c */
609
    s[sl - (i + 1)] = thcount;
609
    s[sl - (i + 1)] = thcount;
610
  }
610
  }
611
 
611
 
612
  return(sl);
612
  return(sl);
613
}
613
}
614
 
614
 
615
 
615
 
616
/* reload nls ressources from svarcom.lng into svarlang_mem */
616
/* reload nls ressources from svarcom.lng into svarlang_mem */
617
void nls_langreload(char *buff, unsigned short env) {
617
void nls_langreload(char *buff, unsigned short env) {
618
  const char far *nlspath;
618
  const char far *nlspath;
619
  const char far *lang;
619
  const char far *lang;
620
  static unsigned short lastlang;
620
  static unsigned short lastlang;
621
 
621
 
622
  /* look up the LANG env variable, upcase it and copy to lang */
622
  /* look up the LANG env variable, upcase it and copy to lang */
623
  lang = env_lookup_val(env, "LANG");
623
  lang = env_lookup_val(env, "LANG");
624
  if ((lang == NULL) || (lang[0] == 0)) return;
624
  if ((lang == NULL) || (lang[0] == 0)) return;
625
  _fmemcpy(buff, lang, 2);
625
  _fmemcpy(buff, lang, 2);
626
  buff[2] = 0;
626
  buff[2] = 0;
627
 
627
 
628
  /* check if there is need to reload at all */
628
  /* check if there is need to reload at all */
629
  if (memcmp(&lastlang, buff, 2) == 0) return;
629
  if (memcmp(&lastlang, buff, 2) == 0) return;
630
 
630
 
631
  buff[4] = 0;
631
  buff[4] = 0;
632
  nlspath = env_lookup_val(env, "NLSPATH");
632
  nlspath = env_lookup_val(env, "NLSPATH");
633
  if (nlspath != NULL) _fstrcpy(buff + 4, nlspath);
633
  if (nlspath != NULL) _fstrcpy(buff + 4, nlspath);
634
 
634
 
635
  if (svarlang_load("SVARCOM", buff, buff + 4) != 0) return;
635
  if (svarlang_load("SVARCOM", buff, buff + 4) != 0) return;
636
 
636
 
637
  _fmemcpy(&lastlang, lang, 2);
637
  _fmemcpy(&lastlang, lang, 2);
638
}
638
}
639
 
639
 
640
 
640
 
641
/* locates executable fname in path and fill res with result. returns 0 on success,
641
/* locates executable fname in path and fill res with result. returns 0 on success,
642
 * -1 on failed match and -2 on failed match + "don't even try with other paths"
642
 * -1 on failed match and -2 on failed match + "don't even try with other paths"
643
 * extptr is filled with a ptr to the extension in fname (NULL if no extension) */
643
 * extptr is filled with a ptr to the extension in fname (NULL if no extension) */
644
int lookup_cmd(char *res, const char *fname, const char *path, const char **extptr) {
644
int lookup_cmd(char *res, const char *fname, const char *path, const char **extptr) {
645
  unsigned short lastbslash = 0;
645
  unsigned short lastbslash = 0;
646
  unsigned short i, len;
646
  unsigned short i, len;
647
  unsigned char explicitpath = 0;
647
  unsigned char explicitpath = 0;
648
 
648
 
649
  /* does the original fname has an explicit path prefix or explicit ext? */
649
  /* does the original fname has an explicit path prefix or explicit ext? */
650
  *extptr = NULL;
650
  *extptr = NULL;
651
  for (i = 0; fname[i] != 0; i++) {
651
  for (i = 0; fname[i] != 0; i++) {
652
    switch (fname[i]) {
652
    switch (fname[i]) {
653
      case ':':
653
      case ':':
654
      case '\\':
654
      case '\\':
655
        explicitpath = 1;
655
        explicitpath = 1;
656
        *extptr = NULL; /* extension is the last dot AFTER all path delimiters */
656
        *extptr = NULL; /* extension is the last dot AFTER all path delimiters */
657
        break;
657
        break;
658
      case '.':
658
      case '.':
659
        *extptr = fname + i + 1;
659
        *extptr = fname + i + 1;
660
        break;
660
        break;
661
    }
661
    }
662
  }
662
  }
663
 
663
 
664
  /* normalize filename */
664
  /* normalize filename */
665
  if (file_truename(fname, res) != 0) return(-2);
665
  if (file_truename(fname, res) != 0) return(-2);
666
 
666
 
667
  /* printf("truename: %s\r\n", res); */
667
  /* printf("truename: %s\r\n", res); */
668
 
668
 
669
  /* figure out where the command starts and if it has an explicit extension */
669
  /* figure out where the command starts and if it has an explicit extension */
670
  for (len = 0; res[len] != 0; len++) {
670
  for (len = 0; res[len] != 0; len++) {
671
    switch (res[len]) {
671
    switch (res[len]) {
672
      case '?':   /* abort on any wildcard character */
672
      case '?':   /* abort on any wildcard character */
673
      case '*':
673
      case '*':
674
        return(-2);
674
        return(-2);
675
      case '\\':
675
      case '\\':
676
        lastbslash = len;
676
        lastbslash = len;
677
        break;
677
        break;
678
    }
678
    }
679
  }
679
  }
680
 
680
 
681
  /* printf("lastbslash=%u\r\n", lastbslash); */
681
  /* printf("lastbslash=%u\r\n", lastbslash); */
682
 
682
 
683
  /* if no path prefix was found in fname (no colon or backslash) AND we have
683
  /* if no path prefix was found in fname (no colon or backslash) AND we have
684
   * a path arg, then assemble path+filename */
684
   * a path arg, then assemble path+filename */
685
  if ((!explicitpath) && (path != NULL) && (path[0] != 0)) {
685
  if ((!explicitpath) && (path != NULL) && (path[0] != 0)) {
686
    i = strlen(path);
686
    i = strlen(path);
687
    if (path[i - 1] != '\\') i++; /* add a byte for inserting a bkslash after path */
687
    if (path[i - 1] != '\\') i++; /* add a byte for inserting a bkslash after path */
688
    /* move the filename at the place where path will end */
688
    /* move the filename at the place where path will end */
689
    memmove(res + i, res + lastbslash + 1, len - lastbslash);
689
    memmove(res + i, res + lastbslash + 1, len - lastbslash);
690
    /* copy path in front of the filename and make sure there is a bkslash sep */
690
    /* copy path in front of the filename and make sure there is a bkslash sep */
691
    memmove(res, path, i);
691
    memmove(res, path, i);
692
    res[i - 1] = '\\';
692
    res[i - 1] = '\\';
693
  }
693
  }
694
 
694
 
695
  /* if no extension was initially provided, try matching COM, EXE, BAT */
695
  /* if no extension was initially provided, try matching COM, EXE, BAT */
696
  if (*extptr == NULL) {
696
  if (*extptr == NULL) {
697
    const char *ext[] = {".COM", ".EXE", ".BAT", NULL};
697
    const char *ext[] = {".COM", ".EXE", ".BAT", NULL};
698
    len = strlen(res);
698
    len = strlen(res);
699
    for (i = 0; ext[i] != NULL; i++) {
699
    for (i = 0; ext[i] != NULL; i++) {
700
      strcpy(res + len, ext[i]);
700
      strcpy(res + len, ext[i]);
701
      /* printf("? '%s'\r\n", res); */
701
      /* printf("? '%s'\r\n", res); */
702
      *extptr = ext[i] + 1;
702
      *extptr = ext[i] + 1;
703
      if (file_getattr(res) >= 0) return(0);
703
      if (file_getattr(res) >= 0) return(0);
704
    }
704
    }
705
  } else { /* try finding it as-is */
705
  } else { /* try finding it as-is */
706
    /* printf("? '%s'\r\n", res); */
706
    /* printf("? '%s'\r\n", res); */
707
    if (file_getattr(res) >= 0) return(0);
707
    if (file_getattr(res) >= 0) return(0);
708
  }
708
  }
709
 
709
 
710
  /* not found */
710
  /* not found */
711
  if (explicitpath) return(-2); /* don't bother trying other paths, the caller had its own path preset anyway */
711
  if (explicitpath) return(-2); /* don't bother trying other paths, the caller had its own path preset anyway */
712
  return(-1);
712
  return(-1);
713
}
713
}
714
 
714
 
715
 
715
 
716
/* fills fname with the path and filename to the linkfile related to the
716
/* fills fname with the path and filename to the linkfile related to the
717
 * executable link "linkname". returns 0 on success. */
717
 * executable link "linkname". returns 0 on success. */
718
int link_computefname(char *fname, const char *linkname, unsigned short env_seg) {
718
int link_computefname(char *fname, const char *linkname, unsigned short env_seg) {
719
  unsigned short pathlen;
719
  unsigned short pathlen;
720
 
720
 
721
  /* fetch %DOSDIR% */
721
  /* fetch %DOSDIR% */
722
  pathlen = env_lookup_valcopy(fname, 128, env_seg, "DOSDIR");
722
  pathlen = env_lookup_valcopy(fname, 128, env_seg, "DOSDIR");
723
  if (pathlen == 0) {
723
  if (pathlen == 0) {
724
    outputnl("%DOSDIR% not defined");
724
    outputnl("%DOSDIR% not defined");
725
    return(-1);
725
    return(-1);
726
  }
726
  }
727
 
727
 
728
  /* prep filename: %DOSDIR%\LINKS\PKG.LNK */
728
  /* prep filename: %DOSDIR%\LINKS\PKG.LNK */
729
  if (fname[pathlen - 1] == '\\') pathlen--;
729
  if (fname[pathlen - 1] == '\\') pathlen--;
730
  sprintf(fname + pathlen, "\\LINKS\\%s.LNK", linkname);
730
  sprintf(fname + pathlen, "\\LINKS\\%s.LNK", linkname);
731
 
731
 
732
  return(0);
732
  return(0);
733
}
733
}
734
 
734