Subversion Repositories SvarDOS

Rev

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

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