Subversion Repositories SvarDOS

Rev

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

Rev 530 Rev 538
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 stdout */
71
/* outputs a NULL-terminated string to handle (1=stdout 2=stderr) */
72
void output_internal(const char *s, unsigned char nl) {
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
    mov bx, 1      /* handle 1 is always stdout */
92
    mov bl, handle /* set handle (1=stdout 2=stderr) */
92
    /* mov cx, xxx */ /* write CX bytes */
93
    /* mov cx, xxx */ /* write CX bytes */
93
    /* 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" */
94
    int 0x21
95
    int 0x21
95
    WRITEDONE:
96
    WRITEDONE:
96
 
97
 
97
    /* print out a CR/LF trailer if nl set */
98
    /* print out a CR/LF trailer if nl set */
98
    or byte ptr [nl], 0
99
    test byte ptr [nl], 0xff
99
    jz FINITO
100
    jz FINITO
-
 
101
    /* bx still contains handle */
-
 
102
    mov ah, 0x40 /* "write to file" */
100
    mov ah, 0x09
103
    mov cx, 2
101
    mov dx, crlf
104
    mov dx, crlf
102
    int 0x21
105
    int 0x21
103
    FINITO:
106
    FINITO:
104
  }
107
  }
105
}
108
}
106
 
109
 
107
 
110
 
108
void nls_output_internal(unsigned short id, unsigned char nl) {
111
static const char *nlsblock_findstr(unsigned short id) {
109
  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 */
110
  const char *NOTFOUND = "NLS_STRING_NOT_FOUND";
-
 
111
  /* find the string id in langblock memory */
113
  /* find the string id in langblock memory */
112
  for (;;) {
114
  for (;;) {
113
    if (((unsigned short *)ptr)[0] == id) {
115
    if (((unsigned short *)ptr)[0] == id) {
114
      ptr += 3;
116
      ptr += 3;
115
      break;
-
 
116
    }
-
 
117
    if (ptr[2] == 0) {
-
 
118
      ptr = NOTFOUND;
-
 
119
      break;
117
      return(ptr);
120
    }
118
    }
-
 
119
    if (ptr[2] == 0) return(NULL);
121
    ptr += ptr[2] + 3;
120
    ptr += ptr[2] + 3;
122
  }
121
  }
-
 
122
}
-
 
123
 
-
 
124
 
-
 
125
void nls_output_internal(unsigned short id, unsigned char nl) {
-
 
126
  const char *NOTFOUND = "NLS_STRING_NOT_FOUND";
-
 
127
  const char *ptr = nlsblock_findstr(id);
-
 
128
  if (ptr == NULL) ptr = NOTFOUND;
123
  output_internal(ptr, nl);
129
  output_internal(ptr, nl, hSTDOUT);
-
 
130
}
-
 
131
 
-
 
132
 
-
 
133
/* output DOS error e to stderr */
-
 
134
void nls_outputnl_doserr(unsigned short e) {
-
 
135
  static char errstr[16];
-
 
136
  const char *ptr = NULL;
-
 
137
  /* find string in nls block */
-
 
138
  if (e < 0xff) ptr = nlsblock_findstr(0xff00 | e);
-
 
139
  /* if not found, use a fallback */
-
 
140
  if (ptr == NULL) {
-
 
141
    sprintf(errstr, "DOS ERR %u", e);
-
 
142
    ptr = errstr;
-
 
143
  }
-
 
144
  /* display */
-
 
145
  output_internal(ptr, 1, hSTDERR);
124
}
146
}
125
 
147
 
126
 
148
 
127
/* find first matching files using a FindFirst DOS call
149
/* find first matching files using a FindFirst DOS call
128
 * returns 0 on success or a DOS err code on failure */
150
 * returns 0 on success or a DOS err code on failure */
129
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) {
130
  unsigned short res = 0;
152
  unsigned short res = 0;
131
  _asm {
153
  _asm {
132
    /* set DTA location */
154
    /* set DTA location */
133
    mov ah, 0x1a
155
    mov ah, 0x1a
134
    mov dx, dta
156
    mov dx, dta
135
    int 0x21
157
    int 0x21
136
    /* */
158
    /* */
137
    mov ah, 0x4e    /* FindFirst */
159
    mov ah, 0x4e    /* FindFirst */
138
    mov dx, pattern
160
    mov dx, pattern
139
    mov cx, attr
161
    mov cx, attr
140
    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 */
141
    jnc DONE
163
    jnc DONE
142
    mov [res], ax
164
    mov [res], ax
143
    DONE:
165
    DONE:
144
  }
166
  }
145
  return(res);
167
  return(res);
146
}
168
}
147
 
169
 
148
 
170
 
149
/* find next matching, ie. continues an action intiated by findfirst() */
171
/* find next matching, ie. continues an action intiated by findfirst() */
150
unsigned short findnext(struct DTA *dta) {
172
unsigned short findnext(struct DTA *dta) {
151
  unsigned short res = 0;
173
  unsigned short res = 0;
152
  _asm {
174
  _asm {
153
    mov ah, 0x4f    /* FindNext */
175
    mov ah, 0x4f    /* FindNext */
154
    mov dx, dta
176
    mov dx, dta
155
    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 */
156
    jnc DONE
178
    jnc DONE
157
    mov [res], ax
179
    mov [res], ax
158
    DONE:
180
    DONE:
159
  }
181
  }
160
  return(res);
182
  return(res);
161
}
183
}
162
 
184
 
163
 
185
 
164
/* 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
165
 * 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
166
 * in string. keys in c MUST BE UPPERCASE! */
188
 * in string. keys in c MUST BE UPPERCASE! */
167
unsigned short askchoice(const char *s, const char *c) {
189
unsigned short askchoice(const char *s, const char *c) {
168
  unsigned short res;
190
  unsigned short res;
169
  char key = 0;
191
  char key = 0;
170
 
192
 
171
  AGAIN:
193
  AGAIN:
172
  output(s);
194
  output(s);
173
  output(" ");
195
  output(" ");
174
 
196
 
175
  _asm {
197
  _asm {
176
    push ax
198
    push ax
177
    push dx
199
    push dx
178
 
200
 
179
    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) */
180
    int 0x21
202
    int 0x21
181
    /* if AL == 0 then this is an extended character */
203
    /* if AL == 0 then this is an extended character */
182
    test al, al
204
    test al, al
183
    jnz GOTCHAR
205
    jnz GOTCHAR
184
    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 */
185
    int 0x21
207
    int 0x21
186
    xor al, al     /* all extended chars are ignored */
208
    xor al, al     /* all extended chars are ignored */
187
    GOTCHAR:       /* received key is in AL now */
209
    GOTCHAR:       /* received key is in AL now */
188
    mov [key], al  /* save key */
210
    mov [key], al  /* save key */
189
 
211
 
190
    /* print a cr/lf */
212
    /* print a cr/lf */
191
    mov ah, 0x02
213
    mov ah, 0x02
192
    mov dl, 0x0D
214
    mov dl, 0x0D
193
    int 0x21
215
    int 0x21
194
    mov dl, 0x0A
216
    mov dl, 0x0A
195
    int 0x21
217
    int 0x21
196
 
218
 
197
    pop dx
219
    pop dx
198
    pop ax
220
    pop ax
199
  }
221
  }
200
 
222
 
201
  /* ucase() result */
223
  /* ucase() result */
202
  if ((key >= 'a') && (key <= 'z')) key -= ('a' - 'A');
224
  if ((key >= 'a') && (key <= 'z')) key -= ('a' - 'A');
203
 
225
 
204
  /* is there a match? */
226
  /* is there a match? */
205
  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);
206
 
228
 
207
  goto AGAIN;
229
  goto AGAIN;
208
}
230
}
209
 
231
 
210
 
232
 
211
/* converts a path to its canonic representation, returns 0 on success
233
/* converts a path to its canonic representation, returns 0 on success
212
 * or DOS err on failure (invalid drive) */
234
 * or DOS err on failure (invalid drive) */
213
unsigned short file_truename(const char *src, char *dst) {
235
unsigned short file_truename(const char *src, char *dst) {
214
  unsigned short res = 0;
236
  unsigned short res = 0;
215
  _asm {
237
  _asm {
216
    push es
238
    push es
217
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
239
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
218
    push ds
240
    push ds
219
    pop es
241
    pop es
220
    mov si, src
242
    mov si, src
221
    mov di, dst
243
    mov di, dst
222
    int 0x21
244
    int 0x21
223
    jnc DONE
245
    jnc DONE
224
    mov [res], ax
246
    mov [res], ax
225
    DONE:
247
    DONE:
226
    pop es
248
    pop es
227
  }
249
  }
228
  return(res);
250
  return(res);
229
}
251
}
230
 
252
 
231
 
253
 
232
/* returns DOS attributes of file, or -1 on error */
254
/* returns DOS attributes of file, or -1 on error */
233
int file_getattr(const char *fname) {
255
int file_getattr(const char *fname) {
234
  int res = -1;
256
  int res = -1;
235
  _asm {
257
  _asm {
236
    mov ax, 0x4300  /* query file attributes, fname at DS:DX */
258
    mov ax, 0x4300  /* query file attributes, fname at DS:DX */
237
    mov dx, fname
259
    mov dx, fname
238
    int 0x21        /* CX=attributes if CF=0, otherwise AX=errno */
260
    int 0x21        /* CX=attributes if CF=0, otherwise AX=errno */
239
    jc DONE
261
    jc DONE
240
    mov [res], cx
262
    mov [res], cx
241
    DONE:
263
    DONE:
242
  }
264
  }
243
  return(res);
265
  return(res);
244
}
266
}
245
 
267
 
246
 
268
 
247
/* returns screen's width (in columns) */
269
/* returns screen's width (in columns) */
248
unsigned short screen_getwidth(void) {
270
unsigned short screen_getwidth(void) {
249
  /* BIOS 0040:004A = word containing screen width in text columns */
271
  /* BIOS 0040:004A = word containing screen width in text columns */
250
  unsigned short far *scrw = MK_FP(0x40, 0x4a);
272
  unsigned short far *scrw = MK_FP(0x40, 0x4a);
251
  return(*scrw);
273
  return(*scrw);
252
}
274
}
253
 
275
 
254
 
276
 
255
/* returns screen's height (in rows) */
277
/* returns screen's height (in rows) */
256
unsigned short screen_getheight(void) {
278
unsigned short screen_getheight(void) {
257
  /* BIOS 0040:0084 = byte containing maximum valid row value (EGA ONLY) */
279
  /* BIOS 0040:0084 = byte containing maximum valid row value (EGA ONLY) */
258
  unsigned char far *scrh = MK_FP(0x40, 0x84);
280
  unsigned char far *scrh = MK_FP(0x40, 0x84);
259
  if (*scrh == 0) return(25);  /* pre-EGA adapter */
281
  if (*scrh == 0) return(25);  /* pre-EGA adapter */
260
  return(*scrh + 1);
282
  return(*scrh + 1);
261
}
283
}
262
 
284
 
263
 
285
 
264
/* 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 */
265
void press_any_key(void) {
287
void press_any_key(void) {
266
  nls_output(15, 1); /* Press any key to continue... */
288
  nls_output(15, 1); /* Press any key to continue... */
267
  _asm {
289
  _asm {
268
    mov ah, 0x08  /* no echo console input */
290
    mov ah, 0x08  /* no echo console input */
269
    int 0x21      /* pressed key in AL now (0 for extended keys) */
291
    int 0x21      /* pressed key in AL now (0 for extended keys) */
270
    test al, al
292
    test al, al
271
    jnz DONE
293
    jnz DONE
272
    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 */
273
    DONE:
295
    DONE:
274
    /* output CR/LF */
296
    /* output CR/LF */
275
    mov ah, 0x02
297
    mov ah, 0x02
276
    mov dl, 0x0D
298
    mov dl, 0x0D
277
    int 0x21
299
    int 0x21
278
    mov dl, 0x0A
300
    mov dl, 0x0A
279
    int 0x21
301
    int 0x21
280
  }
302
  }
281
}
303
}
282
 
304
 
283
 
305
 
284
/* 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 */
285
int isdrivevalid(unsigned char drv) {
307
int isdrivevalid(unsigned char drv) {
286
  _asm {
308
  _asm {
287
    mov ah, 0x19  /* query default (current) disk */
309
    mov ah, 0x19  /* query default (current) disk */
288
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
310
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
289
    mov ch, al    /* save current drive to ch */
311
    mov ch, al    /* save current drive to ch */
290
    /* try setting up the drive as current */
312
    /* try setting up the drive as current */
291
    mov ah, 0x0E   /* select default drive */
313
    mov ah, 0x0E   /* select default drive */
292
    mov dl, [drv]  /* 0=A, 1=B, etc */
314
    mov dl, [drv]  /* 0=A, 1=B, etc */
293
    int 0x21
315
    int 0x21
294
    /* 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 */
295
    mov ah, 0x19  /* query default (current) disk */
317
    mov ah, 0x19  /* query default (current) disk */
296
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
318
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
297
    mov [drv], 1  /* preset result as success */
319
    mov [drv], 1  /* preset result as success */
298
    cmp al, dl    /* is eq? */
320
    cmp al, dl    /* is eq? */
299
    je DONE
321
    je DONE
300
    mov [drv], 0  /* fail */
322
    mov [drv], 0  /* fail */
301
    jmp FAILED
323
    jmp FAILED
302
    DONE:
324
    DONE:
303
    /* set current drive back to what it was initially */
325
    /* set current drive back to what it was initially */
304
    mov ah, 0x0E
326
    mov ah, 0x0E
305
    mov dl, ch
327
    mov dl, ch
306
    int 0x21
328
    int 0x21
307
    FAILED:
329
    FAILED:
308
  }
330
  }
309
  return(drv);
331
  return(drv);
310
}
332
}
311
 
333
 
312
 
334
 
313
/* 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) */
314
void file_fname2fcb(char *dst, const char *src) {
336
void file_fname2fcb(char *dst, const char *src) {
315
  unsigned short i;
337
  unsigned short i;
316
 
338
 
317
  /* fill dst with 11 spaces and a NULL terminator */
339
  /* fill dst with 11 spaces and a NULL terminator */
318
  for (i = 0; i < 11; i++) dst[i] = ' ';
340
  for (i = 0; i < 11; i++) dst[i] = ' ';
319
  dst[11] = 0;
341
  dst[11] = 0;
320
 
342
 
321
  /* copy fname until dot (.) or 8 characters */
343
  /* copy fname until dot (.) or 8 characters */
322
  for (i = 0; i < 8; i++) {
344
  for (i = 0; i < 8; i++) {
323
    if ((src[i] == '.') || (src[i] == 0)) break;
345
    if ((src[i] == '.') || (src[i] == 0)) break;
324
    dst[i] = src[i];
346
    dst[i] = src[i];
325
  }
347
  }
326
 
348
 
327
  /* advance src until extension or end of string */
349
  /* advance src until extension or end of string */
328
  src += i;
350
  src += i;
329
  for (;;) {
351
  for (;;) {
330
    if (*src == '.') {
352
    if (*src == '.') {
331
      src++; /* next character is extension */
353
      src++; /* next character is extension */
332
      break;
354
      break;
333
    }
355
    }
334
    if (*src == 0) break;
356
    if (*src == 0) break;
335
  }
357
  }
336
 
358
 
337
  /* copy extension to dst (3 chars max) */
359
  /* copy extension to dst (3 chars max) */
338
  dst += 8;
360
  dst += 8;
339
  for (i = 0; i < 3; i++) {
361
  for (i = 0; i < 3; i++) {
340
    if (src[i] == 0) break;
362
    if (src[i] == 0) break;
341
    dst[i] = src[i];
363
    dst[i] = src[i];
342
  }
364
  }
343
}
365
}
344
 
366
 
345
 
367
 
346
/* 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) */
347
void file_fcb2fname(char *dst, const char *src) {
369
void file_fcb2fname(char *dst, const char *src) {
348
  unsigned short i, end = 0;
370
  unsigned short i, end = 0;
349
 
371
 
350
  for (i = 0; i < 8; i++) {
372
  for (i = 0; i < 8; i++) {
351
    dst[i] = src[i];
373
    dst[i] = src[i];
352
    if (dst[i] != ' ') end = i + 1;
374
    if (dst[i] != ' ') end = i + 1;
353
  }
375
  }
354
 
376
 
355
  /* is there an extension? */
377
  /* is there an extension? */
356
  if (src[8] == ' ') {
378
  if (src[8] == ' ') {
357
    dst[end] = 0;
379
    dst[end] = 0;
358
  } else { /* found extension: copy it until first space */
380
  } else { /* found extension: copy it until first space */
359
    dst[end++] = '.';
381
    dst[end++] = '.';
360
    for (i = 8; i < 11; i++) {
382
    for (i = 8; i < 11; i++) {
361
      if (src[i] == ' ') break;
383
      if (src[i] == ' ') break;
362
      dst[end++] = src[i];
384
      dst[end++] = src[i];
363
    }
385
    }
364
    dst[end] = 0;
386
    dst[end] = 0;
365
  }
387
  }
366
}
388
}
367
 
389
 
368
 
390
 
369
/* 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.
370
 * 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
371
 * error occurred (0 on overflow or if parsing failed immediately) */
393
 * error occurred (0 on overflow or if parsing failed immediately) */
372
int atous(unsigned short *r, const char *s) {
394
int atous(unsigned short *r, const char *s) {
373
  int err = 0;
395
  int err = 0;
374
 
396
 
375
  _asm {
397
  _asm {
376
    mov si, s
398
    mov si, s
377
    xor ax, ax  /* general purpose register */
399
    xor ax, ax  /* general purpose register */
378
    xor cx, cx  /* contains the result */
400
    xor cx, cx  /* contains the result */
379
    mov bx, 10  /* used as a multiplicative step */
401
    mov bx, 10  /* used as a multiplicative step */
380
 
402
 
381
    NEXTBYTE:
403
    NEXTBYTE:
382
    xchg cx, ax /* move result into cx temporarily */
404
    xchg cx, ax /* move result into cx temporarily */
383
    lodsb  /* AL = DS:[SI++] */
405
    lodsb  /* AL = DS:[SI++] */
384
    /* is AL 0? if so we're done */
406
    /* is AL 0? if so we're done */
385
    test al, al
407
    test al, al
386
    jz DONE
408
    jz DONE
387
    /* validate that AL is in range '0'-'9' */
409
    /* validate that AL is in range '0'-'9' */
388
    sub al, '0'
410
    sub al, '0'
389
    jc FAIL   /* invalid character detected */
411
    jc FAIL   /* invalid character detected */
390
    cmp al, 9
412
    cmp al, 9
391
    jg FAIL   /* invalid character detected */
413
    jg FAIL   /* invalid character detected */
392
    /* restore result into AX (CX contains the new digit) */
414
    /* restore result into AX (CX contains the new digit) */
393
    xchg cx, ax
415
    xchg cx, ax
394
    /* multiply result by 10 and add cl */
416
    /* multiply result by 10 and add cl */
395
    mul bx    /* DX AX = AX * BX(10) */
417
    mul bx    /* DX AX = AX * BX(10) */
396
    jc OVERFLOW  /* overflow */
418
    jc OVERFLOW  /* overflow */
397
    add ax, cx
419
    add ax, cx
398
    /* 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) */
399
    jnc NEXTBYTE
421
    jnc NEXTBYTE
400
 
422
 
401
    OVERFLOW:
423
    OVERFLOW:
402
    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 */
403
 
425
 
404
    FAIL:
426
    FAIL:
405
    inc [err]
427
    inc [err]
406
 
428
 
407
    DONE: /* save result (CX) into indirect memory address r */
429
    DONE: /* save result (CX) into indirect memory address r */
408
    mov bx, [r]
430
    mov bx, [r]
409
    mov [bx], cx
431
    mov [bx], cx
410
  }
432
  }
411
  return(err);
433
  return(err);
412
}
434
}
413
 
435
 
414
 
436
 
415
/* appends a backslash if path is a directory
437
/* appends a backslash if path is a directory
416
 * returns the (possibly updated) length of path */
438
 * returns the (possibly updated) length of path */
417
unsigned short path_appendbkslash_if_dir(char *path) {
439
unsigned short path_appendbkslash_if_dir(char *path) {
418
  unsigned short len;
440
  unsigned short len;
419
  int attr;
441
  int attr;
420
  for (len = 0; path[len] != 0; len++);
442
  for (len = 0; path[len] != 0; len++);
421
  if (len == 0) return(0);
443
  if (len == 0) return(0);
422
  if (path[len - 1] == '\\') return(len);
444
  if (path[len - 1] == '\\') return(len);
423
  /* */
445
  /* */
424
  attr = file_getattr(path);
446
  attr = file_getattr(path);
425
  if ((attr > 0) && (attr & DOS_ATTR_DIR)) {
447
  if ((attr > 0) && (attr & DOS_ATTR_DIR)) {
426
    path[len++] = '\\';
448
    path[len++] = '\\';
427
    path[len] = 0;
449
    path[len] = 0;
428
  }
450
  }
429
  return(len);
451
  return(len);
430
}
452
}
431
 
453
 
432
 
454
 
433
/* 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")
434
 * returns 0 on success, doserr otherwise */
456
 * returns 0 on success, doserr otherwise */
435
unsigned short curpathfordrv(char *buff, unsigned char d) {
457
unsigned short curpathfordrv(char *buff, unsigned char d) {
436
  unsigned short r = 0;
458
  unsigned short r = 0;
437
 
459
 
438
  _asm {
460
  _asm {
439
    /* is d == 0? then I need to resolve current drive */
461
    /* is d == 0? then I need to resolve current drive */
440
    cmp byte ptr [d], 0
462
    cmp byte ptr [d], 0
441
    jne GETCWD
463
    jne GETCWD
442
    /* resolve cur drive */
464
    /* resolve cur drive */
443
    mov ah, 0x19  /* get current default drive */
465
    mov ah, 0x19  /* get current default drive */
444
    int 0x21      /* al = drive (00h = A:, 01h = B:, etc) */
466
    int 0x21      /* al = drive (00h = A:, 01h = B:, etc) */
445
    inc al        /* convert to 1=A, 2=B, etc */
467
    inc al        /* convert to 1=A, 2=B, etc */
446
    mov [d], al
468
    mov [d], al
447
 
469
 
448
    GETCWD:
470
    GETCWD:
449
    /* prepend buff with drive:\ */
471
    /* prepend buff with drive:\ */
450
    mov si, buff
472
    mov si, buff
451
    mov dl, [d]
473
    mov dl, [d]
452
    mov [si], dl
474
    mov [si], dl
453
    add byte ptr [si], 'A' - 1
475
    add byte ptr [si], 'A' - 1
454
    inc si
476
    inc si
455
    mov [si], ':'
477
    mov [si], ':'
456
    inc si
478
    inc si
457
    mov [si], '\\'
479
    mov [si], '\\'
458
    inc si
480
    inc si
459
 
481
 
460
    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 */
461
    int 0x21
483
    int 0x21
462
    jnc DONE
484
    jnc DONE
463
    mov [r], ax       /* copy result from ax */
485
    mov [r], ax       /* copy result from ax */
464
 
486
 
465
    DONE:
487
    DONE:
466
  }
488
  }
467
 
489
 
468
  return(r);
490
  return(r);
469
}
491
}
470
 
492
 
471
 
493
 
472
/* 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 */
473
unsigned short nls_getpatterns(struct nls_patterns *p) {
495
unsigned short nls_getpatterns(struct nls_patterns *p) {
474
  unsigned short r = 0;
496
  unsigned short r = 0;
475
 
497
 
476
  _asm {
498
  _asm {
477
    mov ax, 0x3800  /* DOS 2+ -- Get Country Info for current country */
499
    mov ax, 0x3800  /* DOS 2+ -- Get Country Info for current country */
478
    mov dx, p       /* DS:DX points to the CountryInfoRec buffer */
500
    mov dx, p       /* DS:DX points to the CountryInfoRec buffer */
479
    int 0x21
501
    int 0x21
480
    jnc DONE
502
    jnc DONE
481
    mov [r], ax     /* copy DOS err code to r */
503
    mov [r], ax     /* copy DOS err code to r */
482
    DONE:
504
    DONE:
483
  }
505
  }
484
 
506
 
485
  return(r);
507
  return(r);
486
}
508
}
487
 
509
 
488
 
510
 
489
/* computes a formatted date based on NLS patterns found in p
511
/* computes a formatted date based on NLS patterns found in p
490
 * returns length of result */
512
 * returns length of result */
491
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) {
492
  unsigned short items[3];
514
  unsigned short items[3];
493
  /* preset date/month/year in proper order depending on date format */
515
  /* preset date/month/year in proper order depending on date format */
494
  switch (p->dateformat) {
516
  switch (p->dateformat) {
495
    case 0:  /* USA style: m d y */
517
    case 0:  /* USA style: m d y */
496
      items[0] = mo;
518
      items[0] = mo;
497
      items[1] = dy;
519
      items[1] = dy;
498
      items[2] = yr;
520
      items[2] = yr;
499
      break;
521
      break;
500
    case 1:  /* EU style: d m y */
522
    case 1:  /* EU style: d m y */
501
      items[0] = dy;
523
      items[0] = dy;
502
      items[1] = mo;
524
      items[1] = mo;
503
      items[2] = yr;
525
      items[2] = yr;
504
      break;
526
      break;
505
    case 2:  /* Japan-style: y m d */
527
    case 2:  /* Japan-style: y m d */
506
    default:
528
    default:
507
      items[0] = yr;
529
      items[0] = yr;
508
      items[1] = mo;
530
      items[1] = mo;
509
      items[2] = dy;
531
      items[2] = dy;
510
      break;
532
      break;
511
  }
533
  }
512
  /* compute the string */
534
  /* compute the string */
513
  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]));
514
}
536
}
515
 
537
 
516
 
538
 
517
/* 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
518
 * returns length of result */
540
 * returns length of result */
519
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) {
520
  char ampm = 0;
542
  char ampm = 0;
521
  unsigned short res;
543
  unsigned short res;
522
 
544
 
523
  if (p->timefmt == 0) {
545
  if (p->timefmt == 0) {
524
    if (ho == 12) {
546
    if (ho == 12) {
525
      ampm = 'p';
547
      ampm = 'p';
526
    } else if (ho > 12) {
548
    } else if (ho > 12) {
527
      ho -= 12;
549
      ho -= 12;
528
      ampm = 'p';
550
      ampm = 'p';
529
    } else { /* ho < 12 */
551
    } else { /* ho < 12 */
530
      if (ho == 0) ho = 12;
552
      if (ho == 0) ho = 12;
531
      ampm = 'a';
553
      ampm = 'a';
532
    }
554
    }
533
    res = sprintf(s, "%2u", ho);
555
    res = sprintf(s, "%2u", ho);
534
  } else {
556
  } else {
535
    res = sprintf(s, "%02u", ho);
557
    res = sprintf(s, "%02u", ho);
536
  }
558
  }
537
 
559
 
538
  /* append separator and minutes */
560
  /* append separator and minutes */
539
  res += sprintf(s + res, "%s%02u", p->timesep, mn);
561
  res += sprintf(s + res, "%s%02u", p->timesep, mn);
540
 
562
 
541
  /* if seconds provided, append them, too */
563
  /* if seconds provided, append them, too */
542
  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);
543
 
565
 
544
  /* finally append AM/PM char */
566
  /* finally append AM/PM char */
545
  if (ampm != 0) s[res++] = ampm;
567
  if (ampm != 0) s[res++] = ampm;
546
  s[res] = 0;
568
  s[res] = 0;
547
 
569
 
548
  return(res);
570
  return(res);
549
}
571
}
550
 
572
 
551
 
573
 
552
/* 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
553
 * returns length of result */
575
 * returns length of result */
554
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) {
555
  unsigned short sl = 0, i;
577
  unsigned short sl = 0, i;
556
  unsigned char thcount = 0;
578
  unsigned char thcount = 0;
557
 
579
 
558
  /* write the value (reverse) with thousand separators (if any defined) */
580
  /* write the value (reverse) with thousand separators (if any defined) */
559
  do {
581
  do {
560
    if ((thcount == 3) && (p->thousep[0] != 0)) {
582
    if ((thcount == 3) && (p->thousep[0] != 0)) {
561
      s[sl++] = p->thousep[0];
583
      s[sl++] = p->thousep[0];
562
      thcount = 0;
584
      thcount = 0;
563
    }
585
    }
564
    s[sl++] = '0' + num % 10;
586
    s[sl++] = '0' + num % 10;
565
    num /= 10;
587
    num /= 10;
566
    thcount++;
588
    thcount++;
567
  } while (num > 0);
589
  } while (num > 0);
568
 
590
 
569
  /* terminate the string */
591
  /* terminate the string */
570
  s[sl] = 0;
592
  s[sl] = 0;
571
 
593
 
572
  /* reverse the string now (has been built in reverse) */
594
  /* reverse the string now (has been built in reverse) */
573
  for (i = sl / 2 + (sl & 1); i < sl; i++) {
595
  for (i = sl / 2 + (sl & 1); i < sl; i++) {
574
    thcount = s[i];
596
    thcount = s[i];
575
    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 */
576
    s[sl - (i + 1)] = thcount;
598
    s[sl - (i + 1)] = thcount;
577
  }
599
  }
578
 
600
 
579
  return(sl);
601
  return(sl);
580
}
602
}
581
 
603
 
582
 
604
 
583
/* reload nls ressources from svarcom.lng into langblock */
605
/* reload nls ressources from svarcom.lng into langblock */
584
void nls_langreload(char *buff, unsigned short env) {
606
void nls_langreload(char *buff, unsigned short env) {
585
  unsigned short i;
607
  unsigned short i;
586
  const char far *nlspath;
608
  const char far *nlspath;
587
  char *langblockptr = langblock;
609
  char *langblockptr = langblock;
588
  unsigned short lang;
610
  unsigned short lang;
589
  unsigned short errcode = 0;
611
  unsigned short errcode = 0;
590
 
612
 
591
  /* 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 */
592
  nlspath = env_lookup_val(env, "LANG");
614
  nlspath = env_lookup_val(env, "LANG");
593
  if ((nlspath == NULL) || (nlspath[0] == 0)) return;
615
  if ((nlspath == NULL) || (nlspath[0] == 0)) return;
594
  buff[0] = nlspath[0];
616
  buff[0] = nlspath[0];
595
  buff[1] = nlspath[1];
617
  buff[1] = nlspath[1];
596
  buff[2] = 0;
618
  buff[2] = 0;
597
 
619
 
598
  if (buff[0] >= 'a') buff[0] -= 'a' - 'A';
620
  if (buff[0] >= 'a') buff[0] -= 'a' - 'A';
599
  if (buff[1] >= 'a') buff[1] -= 'a' - 'A';
621
  if (buff[1] >= 'a') buff[1] -= 'a' - 'A';
600
  memcpy(&lang, buff, 2);
622
  memcpy(&lang, buff, 2);
601
 
623
 
602
  /* check if there is need to reload at all */
624
  /* check if there is need to reload at all */
603
  if (((unsigned short *)langblock)[0] == lang) return;
625
  if (((unsigned short *)langblock)[0] == lang) return;
604
 
626
 
605
  /* 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); */
606
 
628
 
607
  nlspath = env_lookup_val(env, "NLSPATH");
629
  nlspath = env_lookup_val(env, "NLSPATH");
608
  if ((nlspath == NULL) || (nlspath[0] == 0)) return;
630
  if ((nlspath == NULL) || (nlspath[0] == 0)) return;
609
 
631
 
610
  /* copy NLSPATH(far) to buff */
632
  /* copy NLSPATH(far) to buff */
611
  for (i = 0; nlspath[i] != 0; i++) buff[i] = nlspath[i];
633
  for (i = 0; nlspath[i] != 0; i++) buff[i] = nlspath[i];
612
 
634
 
613
  /* terminate with a bkslash, if not already the case */
635
  /* terminate with a bkslash, if not already the case */
614
  if (buff[i - 1] != '\\') buff[i++] = '\\';
636
  if (buff[i - 1] != '\\') buff[i++] = '\\';
615
 
637
 
616
  /* append "svarcom.lng" */
638
  /* append "svarcom.lng" */
617
  strcpy(buff + i, "SVARCOM.LNG");
639
  strcpy(buff + i, "SVARCOM.LNG");
618
 
640
 
619
  /* copy file content to langblock */
641
  /* copy file content to langblock */
620
  _asm {
642
  _asm {
621
    push ax
643
    push ax
622
    push bx
644
    push bx
623
    push cx
645
    push cx
624
    push dx
646
    push dx
625
    push si
647
    push si
626
    push di
648
    push di
627
 
649
 
628
    /* 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) */
629
    push ds
651
    push ds
630
    pop es
652
    pop es
631
    cld
653
    cld
632
 
654
 
633
    /* preset SI to buff */
655
    /* preset SI to buff */
634
    mov si, buff
656
    mov si, buff
635
 
657
 
636
    /* Open File */
658
    /* Open File */
637
    mov bx, 0xffff /* bx holds the file handle (0xffff = not set) */
659
    mov bx, 0xffff /* bx holds the file handle (0xffff = not set) */
638
    mov ax, 0x3d00 /* DOS 2+ -- Open File for read */
660
    mov ax, 0x3d00 /* DOS 2+ -- Open File for read */
639
    mov dx, si     /* fname */
661
    mov dx, si     /* fname */
640
    int 0x21       /* cf set on error, otherwise handle in AX */
662
    int 0x21       /* cf set on error, otherwise handle in AX */
641
    jnc OPENOK
663
    jnc OPENOK
642
    jmp FAIL
664
    jmp FAIL
643
    OPENOK:
665
    OPENOK:
644
    mov bx, ax    /* save file handle to bx */
666
    mov bx, ax    /* save file handle to bx */
645
 
667
 
646
    /* read hdr */
668
    /* read hdr */
647
    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 */
648
    mov cx, 4      /* read 4 bytes */
670
    mov cx, 4      /* read 4 bytes */
649
    mov dx, si
671
    mov dx, si
650
    int 0x21
672
    int 0x21
651
    jnc READHDROK
673
    jnc READHDROK
652
    jmp FAIL
674
    jmp FAIL
653
 
675
 
654
    READHDROK:
676
    READHDROK:
655
 
677
 
656
    cmp ax, cx  /* hdr must be 4 bytes long (SvL\x1b) */
678
    cmp ax, cx  /* hdr must be 4 bytes long (SvL\x1b) */
657
    jne FAIL
679
    jne FAIL
658
    /* check that sig is Svl\x1b */
680
    /* check that sig is Svl\x1b */
659
    mov di, si
681
    mov di, si
660
    cld         /* scasw must inc DI */
682
    cld         /* scasw must inc DI */
661
    mov ax, 'vS'
683
    mov ax, 'vS'
662
    scasw       /* cmp ax, ES:[DI] and DI += 2*/
684
    scasw       /* cmp ax, ES:[DI] and DI += 2*/
663
    jne FAIL
685
    jne FAIL
664
    mov ax, 0x1B4C
686
    mov ax, 0x1B4C
665
    scasw       /* cmp ax, ES:[DI] and DI += 2*/
687
    scasw       /* cmp ax, ES:[DI] and DI += 2*/
666
    jne FAIL
688
    jne FAIL
667
 
689
 
668
    READLANGID:
690
    READLANGID:
669
    /* read lang id */
691
    /* read lang id */
670
    mov ah, 0x3f   /* Read from File via Handle in BX */
692
    mov ah, 0x3f   /* Read from File via Handle in BX */
671
    /* mov bx, [i]  already set */
693
    /* mov bx, [i]  already set */
672
    mov cx, 4
694
    mov cx, 4
673
    mov dx, si
695
    mov dx, si
674
    int 0x21
696
    int 0x21
675
    jc FAIL
697
    jc FAIL
676
    cmp ax, cx
698
    cmp ax, cx
677
    jne FAIL
699
    jne FAIL
678
    /* is this the LANG I am looking for? */
700
    /* is this the LANG I am looking for? */
679
    mov ax, [lang]
701
    mov ax, [lang]
680
    mov di, si
702
    mov di, si
681
    scasw       /* cmp ax, ES:[DI] and DI += 2*/
703
    scasw       /* cmp ax, ES:[DI] and DI += 2*/
682
    je LOADSTRINGS
704
    je LOADSTRINGS
683
    /* skip to next lang */
705
    /* skip to next lang */
684
    mov ax, 0x4201   /* move file pointer CX:DX bytes forward */
706
    mov ax, 0x4201   /* move file pointer CX:DX bytes forward */
685
    /* mov bx, [i]  file handle */
707
    /* mov bx, [i]  file handle */
686
    xor cx, cx
708
    xor cx, cx
687
    mov dx, [di]
709
    mov dx, [di]
688
    int 0x21
710
    int 0x21
689
    jc FAIL
711
    jc FAIL
690
    jmp READLANGID
712
    jmp READLANGID
691
 
713
 
692
    LOADSTRINGS:
714
    LOADSTRINGS:
693
 
715
 
694
    /* copy langid and langlen to langblockptr */
716
    /* copy langid and langlen to langblockptr */
695
    mov di, langblockptr
717
    mov di, langblockptr
696
    mov ax, [si]
718
    mov ax, [si]
697
    stosw   /* mov [di], ax and di += 2 */
719
    stosw   /* mov [di], ax and di += 2 */
698
    mov ax, [si+2]
720
    mov ax, [si+2]
699
    stosw
721
    stosw
700
    /* read strings (buff+2 bytes) into langblock */
722
    /* read strings (buff+2 bytes) into langblock */
701
    mov ah, 0x3f   /* Read from File via Handle in BX */
723
    mov ah, 0x3f   /* Read from File via Handle in BX */
702
    mov cx, [si+2]
724
    mov cx, [si+2]
703
    mov dx, di
725
    mov dx, di
704
    int 0x21
726
    int 0x21
705
    jnc DONE
727
    jnc DONE
706
 
728
 
707
    /* on error make sure to zero out langblock's header */
729
    /* on error make sure to zero out langblock's header */
708
    xor cx, cx
730
    xor cx, cx
709
    mov [di], cx                 /* langblock id*/
731
    mov [di], cx                 /* langblock id*/
710
    mov [di + 2], cx /* langblock len */
732
    mov [di + 2], cx /* langblock len */
711
    mov [di + 4], cx /* 1st string id */
733
    mov [di + 4], cx /* 1st string id */
712
    mov [di + 6], cx /* 1st string len */
734
    mov [di + 6], cx /* 1st string len */
713
 
735
 
714
    /* cleanup and quit */
736
    /* cleanup and quit */
715
    FAIL:
737
    FAIL:
716
    mov [errcode], ax
738
    mov [errcode], ax
717
    DONE:
739
    DONE:
718
    /* close file handle if set */
740
    /* close file handle if set */
719
    cmp bx, 0xffff
741
    cmp bx, 0xffff
720
    je FNOTOPEN
742
    je FNOTOPEN
721
    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) */
722
    int 0x21
744
    int 0x21
723
    FNOTOPEN:
745
    FNOTOPEN:
724
 
746
 
725
    pop di
747
    pop di
726
    pop si
748
    pop si
727
    pop dx
749
    pop dx
728
    pop cx
750
    pop cx
729
    pop bx
751
    pop bx
730
    pop ax
752
    pop ax
731
  }
753
  }
732
 
754
 
733
  if (errcode != 0) printf("AX=%04x\r\n", errcode);
755
  if (errcode != 0) printf("AX=%04x\r\n", errcode);
734
}
756
}
735
 
757