Subversion Repositories SvarDOS

Rev

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

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