Subversion Repositories SvarDOS

Rev

Rev 445 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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