Subversion Repositories SvarDOS

Rev

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

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