Subversion Repositories SvarDOS

Rev

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

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