Subversion Repositories SvarDOS

Rev

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

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