Subversion Repositories SvarDOS

Rev

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

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