Subversion Repositories SvarDOS

Rev

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

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