Subversion Repositories SvarDOS

Rev

Rev 437 | Rev 538 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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