Subversion Repositories SvarDOS

Rev

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

Rev 420 Rev 421
-
 
1
/* This file is part of the SvarCOM project and is published under the terms
-
 
2
 * of the MIT license.
-
 
3
 *
-
 
4
 * Copyright (C) 2021 Mateusz Viste
-
 
5
 *
-
 
6
 * Permission is hereby granted, free of charge, to any person obtaining a
-
 
7
 * copy of this software and associated documentation files (the "Software"),
-
 
8
 * to deal in the Software without restriction, including without limitation
-
 
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
-
 
11
 * Software is furnished to do so, subject to the following conditions:
-
 
12
 *
-
 
13
 * The above copyright notice and this permission notice shall be included in
-
 
14
 * all copies or substantial portions of the Software.
-
 
15
 *
-
 
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,
-
 
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
-
 
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
-
 
22
 * DEALINGS IN THE SOFTWARE.
-
 
23
 */
-
 
24
 
1
/*
25
/*
2
 * a variety of helper functions
26
 * a variety of helper functions
3
 * Copyright (C) 2021 Mateusz Viste
27
 * Copyright (C) 2021 Mateusz Viste
4
 */
28
 */
5
 
29
 
6
#include <i86.h>    /* MK_FP() */
30
#include <i86.h>    /* MK_FP() */
7
#include <stdio.h>  /* sprintf() */
31
#include <stdio.h>  /* sprintf() */
8
 
32
 
9
#include "helpers.h"
33
#include "helpers.h"
10
 
34
 
11
 
35
 
12
/* case-insensitive comparison of strings, returns non-zero on equality */
36
/* case-insensitive comparison of strings, returns non-zero on equality */
13
int imatch(const char *s1, const char *s2) {
37
int imatch(const char *s1, const char *s2) {
14
  for (;;) {
38
  for (;;) {
15
    char c1, c2;
39
    char c1, c2;
16
    c1 = *s1;
40
    c1 = *s1;
17
    c2 = *s2;
41
    c2 = *s2;
18
    if ((c1 >= 'a') && (c1 <= 'z')) c1 -= ('a' - 'A');
42
    if ((c1 >= 'a') && (c1 <= 'z')) c1 -= ('a' - 'A');
19
    if ((c2 >= 'a') && (c2 <= 'z')) c2 -= ('a' - 'A');
43
    if ((c2 >= 'a') && (c2 <= 'z')) c2 -= ('a' - 'A');
20
    /* */
44
    /* */
21
    if (c1 != c2) return(0);
45
    if (c1 != c2) return(0);
22
    if (c1 == 0) return(1);
46
    if (c1 == 0) return(1);
23
    s1++;
47
    s1++;
24
    s2++;
48
    s2++;
25
  }
49
  }
26
}
50
}
27
 
51
 
28
 
52
 
29
/* returns zero if s1 starts with s2 */
53
/* returns zero if s1 starts with s2 */
30
int strstartswith(const char *s1, const char *s2) {
54
int strstartswith(const char *s1, const char *s2) {
31
  while (*s2 != 0) {
55
  while (*s2 != 0) {
32
    if (*s1 != *s2) return(-1);
56
    if (*s1 != *s2) return(-1);
33
    s1++;
57
    s1++;
34
    s2++;
58
    s2++;
35
  }
59
  }
36
  return(0);
60
  return(0);
37
}
61
}
38
 
62
 
39
 
63
 
40
/* outputs a NULL-terminated string to stdout */
64
/* outputs a NULL-terminated string to stdout */
41
void output_internal(const char *s, unsigned short nl) {
65
void output_internal(const char *s, unsigned short nl) {
42
  _asm {
66
  _asm {
43
    mov ah, 0x02 /* AH=9 - write character in DL to stdout */
67
    mov ah, 0x02 /* AH=9 - write character in DL to stdout */
44
    mov si, s
68
    mov si, s
45
    cld          /* clear DF so lodsb increments SI */
69
    cld          /* clear DF so lodsb increments SI */
46
    NEXTBYTE:
70
    NEXTBYTE:
47
    lodsb /* load byte from DS:SI into AL, SI++ */
71
    lodsb /* load byte from DS:SI into AL, SI++ */
48
    mov dl, al
72
    mov dl, al
49
    or al, 0  /* is al == 0? */
73
    or al, 0  /* is al == 0? */
50
    jz DONE
74
    jz DONE
51
    int 0x21
75
    int 0x21
52
    jmp NEXTBYTE
76
    jmp NEXTBYTE
53
    DONE:
77
    DONE:
54
    or nl, 0
78
    or nl, 0
55
    jz FINITO
79
    jz FINITO
56
    /* print out a CR/LF trailer if nl set */
80
    /* print out a CR/LF trailer if nl set */
57
    mov dl, 0x0D /* CR */
81
    mov dl, 0x0D /* CR */
58
    int 0x21
82
    int 0x21
59
    mov dl, 0x0A /* LF */
83
    mov dl, 0x0A /* LF */
60
    int 0x21
84
    int 0x21
61
    FINITO:
85
    FINITO:
62
  }
86
  }
63
}
87
}
64
 
88
 
65
 
89
 
66
/* find first matching files using a FindFirst DOS call
90
/* find first matching files using a FindFirst DOS call
67
 * returns 0 on success or a DOS err code on failure */
91
 * returns 0 on success or a DOS err code on failure */
68
unsigned short findfirst(struct DTA *dta, const char *pattern, unsigned short attr) {
92
unsigned short findfirst(struct DTA *dta, const char *pattern, unsigned short attr) {
69
  unsigned short res = 0;
93
  unsigned short res = 0;
70
  _asm {
94
  _asm {
71
    /* set DTA location */
95
    /* set DTA location */
72
    mov ah, 0x1a
96
    mov ah, 0x1a
73
    mov dx, dta
97
    mov dx, dta
74
    int 0x21
98
    int 0x21
75
    /* */
99
    /* */
76
    mov ah, 0x4e    /* FindFirst */
100
    mov ah, 0x4e    /* FindFirst */
77
    mov dx, pattern
101
    mov dx, pattern
78
    mov cx, attr
102
    mov cx, attr
79
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
103
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
80
    jnc DONE
104
    jnc DONE
81
    mov [res], ax
105
    mov [res], ax
82
    DONE:
106
    DONE:
83
  }
107
  }
84
  return(res);
108
  return(res);
85
}
109
}
86
 
110
 
87
 
111
 
88
/* find next matching, ie. continues an action intiated by findfirst() */
112
/* find next matching, ie. continues an action intiated by findfirst() */
89
unsigned short findnext(struct DTA *dta) {
113
unsigned short findnext(struct DTA *dta) {
90
  unsigned short res = 0;
114
  unsigned short res = 0;
91
  _asm {
115
  _asm {
92
    mov ah, 0x4f    /* FindNext */
116
    mov ah, 0x4f    /* FindNext */
93
    mov dx, dta
117
    mov dx, dta
94
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
118
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
95
    jnc DONE
119
    jnc DONE
96
    mov [res], ax
120
    mov [res], ax
97
    DONE:
121
    DONE:
98
  }
122
  }
99
  return(res);
123
  return(res);
100
}
124
}
101
 
125
 
102
 
126
 
103
/* print s string and wait for a single key press from stdin. accepts only
127
/* print s string and wait for a single key press from stdin. accepts only
104
 * key presses defined in the c ASCIIZ string. returns offset of pressed key
128
 * key presses defined in the c ASCIIZ string. returns offset of pressed key
105
 * in string. keys in c MUST BE UPPERCASE! */
129
 * in string. keys in c MUST BE UPPERCASE! */
106
unsigned short askchoice(const char *s, const char *c) {
130
unsigned short askchoice(const char *s, const char *c) {
107
  unsigned short res;
131
  unsigned short res;
108
  char key = 0;
132
  char key = 0;
109
 
133
 
110
  AGAIN:
134
  AGAIN:
111
  output(s);
135
  output(s);
112
  output(" ");
136
  output(" ");
113
 
137
 
114
  _asm {
138
  _asm {
115
    push ax
139
    push ax
116
    push dx
140
    push dx
117
 
141
 
118
    mov ax, 0x0c01 /* clear input buffer and execute getchar (INT 21h,AH=1) */
142
    mov ax, 0x0c01 /* clear input buffer and execute getchar (INT 21h,AH=1) */
119
    int 0x21
143
    int 0x21
120
    /* if AL == 0 then this is an extended character */
144
    /* if AL == 0 then this is an extended character */
121
    test al, al
145
    test al, al
122
    jnz GOTCHAR
146
    jnz GOTCHAR
123
    mov ah, 0x08   /* read again to flush extended char from input buffer */
147
    mov ah, 0x08   /* read again to flush extended char from input buffer */
124
    int 0x21
148
    int 0x21
125
    xor al, al     /* all extended chars are ignored */
149
    xor al, al     /* all extended chars are ignored */
126
    GOTCHAR:       /* received key is in AL now */
150
    GOTCHAR:       /* received key is in AL now */
127
    mov [key], al  /* save key */
151
    mov [key], al  /* save key */
128
 
152
 
129
    /* print a cr/lf */
153
    /* print a cr/lf */
130
    mov ah, 0x02
154
    mov ah, 0x02
131
    mov dl, 0x0D
155
    mov dl, 0x0D
132
    int 0x21
156
    int 0x21
133
    mov dl, 0x0A
157
    mov dl, 0x0A
134
    int 0x21
158
    int 0x21
135
 
159
 
136
    pop dx
160
    pop dx
137
    pop ax
161
    pop ax
138
  }
162
  }
139
 
163
 
140
  /* ucase() result */
164
  /* ucase() result */
141
  if ((key >= 'a') && (key <= 'z')) key -= ('a' - 'A');
165
  if ((key >= 'a') && (key <= 'z')) key -= ('a' - 'A');
142
 
166
 
143
  /* is there a match? */
167
  /* is there a match? */
144
  for (res = 0; c[res] != 0; res++) if (c[res] == key) return(res);
168
  for (res = 0; c[res] != 0; res++) if (c[res] == key) return(res);
145
 
169
 
146
  goto AGAIN;
170
  goto AGAIN;
147
}
171
}
148
 
172
 
149
 
173
 
150
/* converts a path to its canonic representation, returns 0 on success
174
/* converts a path to its canonic representation, returns 0 on success
151
 * or DOS err on failure (invalid drive) */
175
 * or DOS err on failure (invalid drive) */
152
unsigned short file_truename(const char *src, char *dst) {
176
unsigned short file_truename(const char *src, char *dst) {
153
  unsigned short res = 0;
177
  unsigned short res = 0;
154
  _asm {
178
  _asm {
155
    push es
179
    push es
156
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
180
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
157
    push ds
181
    push ds
158
    pop es
182
    pop es
159
    mov si, src
183
    mov si, src
160
    mov di, dst
184
    mov di, dst
161
    int 0x21
185
    int 0x21
162
    jnc DONE
186
    jnc DONE
163
    mov [res], ax
187
    mov [res], ax
164
    DONE:
188
    DONE:
165
    pop es
189
    pop es
166
  }
190
  }
167
  return(res);
191
  return(res);
168
}
192
}
169
 
193
 
170
 
194
 
171
/* returns DOS attributes of file, or -1 on error */
195
/* returns DOS attributes of file, or -1 on error */
172
int file_getattr(const char *fname) {
196
int file_getattr(const char *fname) {
173
  int res = -1;
197
  int res = -1;
174
  _asm {
198
  _asm {
175
    mov ax, 0x4300  /* query file attributes, fname at DS:DX */
199
    mov ax, 0x4300  /* query file attributes, fname at DS:DX */
176
    mov dx, fname
200
    mov dx, fname
177
    int 0x21        /* CX=attributes if CF=0, otherwise AX=errno */
201
    int 0x21        /* CX=attributes if CF=0, otherwise AX=errno */
178
    jc DONE
202
    jc DONE
179
    mov [res], cx
203
    mov [res], cx
180
    DONE:
204
    DONE:
181
  }
205
  }
182
  return(res);
206
  return(res);
183
}
207
}
184
 
208
 
185
 
209
 
186
/* returns screen's width (in columns) */
210
/* returns screen's width (in columns) */
187
unsigned short screen_getwidth(void) {
211
unsigned short screen_getwidth(void) {
188
  /* BIOS 0040:004A = word containing screen width in text columns */
212
  /* BIOS 0040:004A = word containing screen width in text columns */
189
  unsigned short far *scrw = MK_FP(0x40, 0x4a);
213
  unsigned short far *scrw = MK_FP(0x40, 0x4a);
190
  return(*scrw);
214
  return(*scrw);
191
}
215
}
192
 
216
 
193
 
217
 
194
/* returns screen's height (in rows) */
218
/* returns screen's height (in rows) */
195
unsigned short screen_getheight(void) {
219
unsigned short screen_getheight(void) {
196
  /* BIOS 0040:0084 = byte containing maximum valid row value (EGA ONLY) */
220
  /* BIOS 0040:0084 = byte containing maximum valid row value (EGA ONLY) */
197
  unsigned char far *scrh = MK_FP(0x40, 0x84);
221
  unsigned char far *scrh = MK_FP(0x40, 0x84);
198
  if (*scrh == 0) return(25);  /* pre-EGA adapter */
222
  if (*scrh == 0) return(25);  /* pre-EGA adapter */
199
  return(*scrh + 1);
223
  return(*scrh + 1);
200
}
224
}
201
 
225
 
202
 
226
 
203
/* displays the "Press any key to continue" msg and waits for a keypress */
227
/* displays the "Press any key to continue" msg and waits for a keypress */
204
void press_any_key(void) {
228
void press_any_key(void) {
205
  output("Press any key to continue...");
229
  output("Press any key to continue...");
206
  _asm {
230
  _asm {
207
    mov ah, 0x08  /* no echo console input */
231
    mov ah, 0x08  /* no echo console input */
208
    int 0x21      /* pressed key in AL now (0 for extended keys) */
232
    int 0x21      /* pressed key in AL now (0 for extended keys) */
209
    test al, al
233
    test al, al
210
    jnz DONE
234
    jnz DONE
211
    int 0x21      /* executed ah=8 again to read the rest of extended key */
235
    int 0x21      /* executed ah=8 again to read the rest of extended key */
212
    DONE:
236
    DONE:
213
    /* output CR/LF */
237
    /* output CR/LF */
214
    mov ah, 0x02
238
    mov ah, 0x02
215
    mov dl, 0x0D
239
    mov dl, 0x0D
216
    int 0x21
240
    int 0x21
217
    mov dl, 0x0A
241
    mov dl, 0x0A
218
    int 0x21
242
    int 0x21
219
  }
243
  }
220
}
244
}
221
 
245
 
222
 
246
 
223
/* validate a drive (A=0, B=1, etc). returns 1 if valid, 0 otherwise */
247
/* validate a drive (A=0, B=1, etc). returns 1 if valid, 0 otherwise */
224
int isdrivevalid(unsigned char drv) {
248
int isdrivevalid(unsigned char drv) {
225
  _asm {
249
  _asm {
226
    mov ah, 0x19  /* query default (current) disk */
250
    mov ah, 0x19  /* query default (current) disk */
227
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
251
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
228
    mov ch, al    /* save current drive to ch */
252
    mov ch, al    /* save current drive to ch */
229
    /* try setting up the drive as current */
253
    /* try setting up the drive as current */
230
    mov ah, 0x0E   /* select default drive */
254
    mov ah, 0x0E   /* select default drive */
231
    mov dl, [drv]  /* 0=A, 1=B, etc */
255
    mov dl, [drv]  /* 0=A, 1=B, etc */
232
    int 0x21
256
    int 0x21
233
    /* this call does not set CF on error, I must check cur drive to look for success */
257
    /* this call does not set CF on error, I must check cur drive to look for success */
234
    mov ah, 0x19  /* query default (current) disk */
258
    mov ah, 0x19  /* query default (current) disk */
235
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
259
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
236
    mov [drv], 1  /* preset result as success */
260
    mov [drv], 1  /* preset result as success */
237
    cmp al, dl    /* is eq? */
261
    cmp al, dl    /* is eq? */
238
    je DONE
262
    je DONE
239
    mov [drv], 0  /* fail */
263
    mov [drv], 0  /* fail */
240
    jmp FAILED
264
    jmp FAILED
241
    DONE:
265
    DONE:
242
    /* set current drive back to what it was initially */
266
    /* set current drive back to what it was initially */
243
    mov ah, 0x0E
267
    mov ah, 0x0E
244
    mov dl, ch
268
    mov dl, ch
245
    int 0x21
269
    int 0x21
246
    FAILED:
270
    FAILED:
247
  }
271
  }
248
  return(drv);
272
  return(drv);
249
}
273
}
250
 
274
 
251
 
275
 
252
/* converts a 8+3 filename into 11-bytes FCB format (MYFILE  EXT) */
276
/* converts a 8+3 filename into 11-bytes FCB format (MYFILE  EXT) */
253
void file_fname2fcb(char *dst, const char *src) {
277
void file_fname2fcb(char *dst, const char *src) {
254
  unsigned short i;
278
  unsigned short i;
255
 
279
 
256
  /* fill dst with 11 spaces and a NULL terminator */
280
  /* fill dst with 11 spaces and a NULL terminator */
257
  for (i = 0; i < 11; i++) dst[i] = ' ';
281
  for (i = 0; i < 11; i++) dst[i] = ' ';
258
  dst[11] = 0;
282
  dst[11] = 0;
259
 
283
 
260
  /* copy fname until dot (.) or 8 characters */
284
  /* copy fname until dot (.) or 8 characters */
261
  for (i = 0; i < 8; i++) {
285
  for (i = 0; i < 8; i++) {
262
    if ((src[i] == '.') || (src[i] == 0)) break;
286
    if ((src[i] == '.') || (src[i] == 0)) break;
263
    dst[i] = src[i];
287
    dst[i] = src[i];
264
  }
288
  }
265
 
289
 
266
  /* advance src until extension or end of string */
290
  /* advance src until extension or end of string */
267
  src += i;
291
  src += i;
268
  for (;;) {
292
  for (;;) {
269
    if (*src == '.') {
293
    if (*src == '.') {
270
      src++; /* next character is extension */
294
      src++; /* next character is extension */
271
      break;
295
      break;
272
    }
296
    }
273
    if (*src == 0) break;
297
    if (*src == 0) break;
274
  }
298
  }
275
 
299
 
276
  /* copy extension to dst (3 chars max) */
300
  /* copy extension to dst (3 chars max) */
277
  dst += 8;
301
  dst += 8;
278
  for (i = 0; i < 3; i++) {
302
  for (i = 0; i < 3; i++) {
279
    if (src[i] == 0) break;
303
    if (src[i] == 0) break;
280
    dst[i] = src[i];
304
    dst[i] = src[i];
281
  }
305
  }
282
}
306
}
283
 
307
 
284
 
308
 
285
/* converts a 11-bytes FCB filename (MYFILE  EXT) into 8+3 format (MYFILE.EXT) */
309
/* converts a 11-bytes FCB filename (MYFILE  EXT) into 8+3 format (MYFILE.EXT) */
286
void file_fcb2fname(char *dst, const char *src) {
310
void file_fcb2fname(char *dst, const char *src) {
287
  unsigned short i, end = 0;
311
  unsigned short i, end = 0;
288
 
312
 
289
  for (i = 0; i < 8; i++) {
313
  for (i = 0; i < 8; i++) {
290
    dst[i] = src[i];
314
    dst[i] = src[i];
291
    if (dst[i] != ' ') end = i + 1;
315
    if (dst[i] != ' ') end = i + 1;
292
  }
316
  }
293
 
317
 
294
  /* is there an extension? */
318
  /* is there an extension? */
295
  if (src[8] == ' ') {
319
  if (src[8] == ' ') {
296
    dst[end] = 0;
320
    dst[end] = 0;
297
  } else { /* found extension: copy it until first space */
321
  } else { /* found extension: copy it until first space */
298
    dst[end++] = '.';
322
    dst[end++] = '.';
299
    for (i = 8; i < 11; i++) {
323
    for (i = 8; i < 11; i++) {
300
      if (src[i] == ' ') break;
324
      if (src[i] == ' ') break;
301
      dst[end++] = src[i];
325
      dst[end++] = src[i];
302
    }
326
    }
303
    dst[end] = 0;
327
    dst[end] = 0;
304
  }
328
  }
305
}
329
}
306
 
330
 
307
 
331
 
308
/* converts an ASCIIZ string into an unsigned short. returns 0 on success. */
332
/* converts an ASCIIZ string into an unsigned short. returns 0 on success. */
309
int atouns(unsigned short *r, const char *s) {
333
int atouns(unsigned short *r, const char *s) {
310
  int err = 0;
334
  int err = 0;
311
 
335
 
312
  _asm {
336
  _asm {
313
    mov si, s
337
    mov si, s
314
    xor ax, ax  /* general purpose register */
338
    xor ax, ax  /* general purpose register */
315
    xor cx, cx  /* contains the result */
339
    xor cx, cx  /* contains the result */
316
    mov bx, 10  /* used as a multiplicative step */
340
    mov bx, 10  /* used as a multiplicative step */
317
 
341
 
318
    NEXTBYTE:
342
    NEXTBYTE:
319
    xchg cx, ax /* move result into cx temporarily */
343
    xchg cx, ax /* move result into cx temporarily */
320
    lodsb  /* AL = DS:[SI++] */
344
    lodsb  /* AL = DS:[SI++] */
321
    /* is AL 0? if so we're done */
345
    /* is AL 0? if so we're done */
322
    test al, al
346
    test al, al
323
    jz DONE
347
    jz DONE
324
    /* validate that AL is in range '0'-'9' */
348
    /* validate that AL is in range '0'-'9' */
325
    sub al, '0'
349
    sub al, '0'
326
    jc FAIL   /* neg result */
350
    jc FAIL   /* neg result */
327
    cmp al, 9
351
    cmp al, 9
328
    jg FAIL
352
    jg FAIL
329
    /* restore result into AX (CX contains the new digit) */
353
    /* restore result into AX (CX contains the new digit) */
330
    xchg cx, ax
354
    xchg cx, ax
331
    /* multiply result by 10 and add cl */
355
    /* multiply result by 10 and add cl */
332
    mul bx    /* DX AX = AX * BX(10) */
356
    mul bx    /* DX AX = AX * BX(10) */
333
    jc FAIL   /* overflow */
357
    jc FAIL   /* overflow */
334
    add ax, cx
358
    add ax, cx
335
    /* if CF then overflow occured (overflow part lands in DX) */
359
    /* if CF then overflow occured (overflow part lands in DX) */
336
    jnc NEXTBYTE
360
    jnc NEXTBYTE
337
 
361
 
338
    FAIL:
362
    FAIL:
339
    inc [err]
363
    inc [err]
340
 
364
 
341
    DONE: /* save result (CX) into indirect memory address r */
365
    DONE: /* save result (CX) into indirect memory address r */
342
    mov bx, [r]
366
    mov bx, [r]
343
    mov [bx], cx
367
    mov [bx], cx
344
  }
368
  }
345
  return(err);
369
  return(err);
346
}
370
}
347
 
371
 
348
 
372
 
349
/* appends a backslash if path is a directory
373
/* appends a backslash if path is a directory
350
 * returns the (possibly updated) length of path */
374
 * returns the (possibly updated) length of path */
351
unsigned short path_appendbkslash_if_dir(char *path) {
375
unsigned short path_appendbkslash_if_dir(char *path) {
352
  unsigned short len;
376
  unsigned short len;
353
  int attr;
377
  int attr;
354
  for (len = 0; path[len] != 0; len++);
378
  for (len = 0; path[len] != 0; len++);
355
  if (len == 0) return(0);
379
  if (len == 0) return(0);
356
  if (path[len - 1] == '\\') return(len);
380
  if (path[len - 1] == '\\') return(len);
357
  /* */
381
  /* */
358
  attr = file_getattr(path);
382
  attr = file_getattr(path);
359
  if ((attr > 0) && (attr & DOS_ATTR_DIR)) {
383
  if ((attr > 0) && (attr & DOS_ATTR_DIR)) {
360
    path[len++] = '\\';
384
    path[len++] = '\\';
361
    path[len] = 0;
385
    path[len] = 0;
362
  }
386
  }
363
  return(len);
387
  return(len);
364
}
388
}
365
 
389
 
366
 
390
 
367
/* get current path drive d (A=1, B=2, etc - 0 is "current drive")
391
/* get current path drive d (A=1, B=2, etc - 0 is "current drive")
368
 * returns 0 on success, doserr otherwise */
392
 * returns 0 on success, doserr otherwise */
369
unsigned short curpathfordrv(char *buff, unsigned char d) {
393
unsigned short curpathfordrv(char *buff, unsigned char d) {
370
  unsigned short r = 0;
394
  unsigned short r = 0;
371
 
395
 
372
  _asm {
396
  _asm {
373
    /* is d == 0? then I need to resolve current drive */
397
    /* is d == 0? then I need to resolve current drive */
374
    cmp byte ptr [d], 0
398
    cmp byte ptr [d], 0
375
    jne GETCWD
399
    jne GETCWD
376
    /* resolve cur drive */
400
    /* resolve cur drive */
377
    mov ah, 0x19  /* get current default drive */
401
    mov ah, 0x19  /* get current default drive */
378
    int 0x21      /* al = drive (00h = A:, 01h = B:, etc) */
402
    int 0x21      /* al = drive (00h = A:, 01h = B:, etc) */
379
    inc al        /* convert to 1=A, 2=B, etc */
403
    inc al        /* convert to 1=A, 2=B, etc */
380
    mov [d], al
404
    mov [d], al
381
 
405
 
382
    GETCWD:
406
    GETCWD:
383
    /* prepend buff with drive:\ */
407
    /* prepend buff with drive:\ */
384
    mov si, buff
408
    mov si, buff
385
    mov dl, [d]
409
    mov dl, [d]
386
    mov [si], dl
410
    mov [si], dl
387
    add byte ptr [si], 'A' - 1
411
    add byte ptr [si], 'A' - 1
388
    inc si
412
    inc si
389
    mov [si], ':'
413
    mov [si], ':'
390
    inc si
414
    inc si
391
    mov [si], '\\'
415
    mov [si], '\\'
392
    inc si
416
    inc si
393
 
417
 
394
    mov ah, 0x47      /* get current directory of drv DL into DS:SI */
418
    mov ah, 0x47      /* get current directory of drv DL into DS:SI */
395
    int 0x21
419
    int 0x21
396
    jnc DONE
420
    jnc DONE
397
    mov [r], ax       /* copy result from ax */
421
    mov [r], ax       /* copy result from ax */
398
 
422
 
399
    DONE:
423
    DONE:
400
  }
424
  }
401
 
425
 
402
  return(r);
426
  return(r);
403
}
427
}
404
 
428
 
405
 
429
 
406
/* fills a nls_patterns struct with current NLS patterns, returns 0 on success, DOS errcode otherwise */
430
/* fills a nls_patterns struct with current NLS patterns, returns 0 on success, DOS errcode otherwise */
407
unsigned short nls_getpatterns(struct nls_patterns *p) {
431
unsigned short nls_getpatterns(struct nls_patterns *p) {
408
  unsigned short r = 0;
432
  unsigned short r = 0;
409
 
433
 
410
  _asm {
434
  _asm {
411
    mov ax, 0x3800  /* DOS 2+ -- Get Country Info for current country */
435
    mov ax, 0x3800  /* DOS 2+ -- Get Country Info for current country */
412
    mov dx, p       /* DS:DX points to the CountryInfoRec buffer */
436
    mov dx, p       /* DS:DX points to the CountryInfoRec buffer */
413
    int 0x21
437
    int 0x21
414
    jnc DONE
438
    jnc DONE
415
    mov [r], ax     /* copy DOS err code to r */
439
    mov [r], ax     /* copy DOS err code to r */
416
    DONE:
440
    DONE:
417
  }
441
  }
418
 
442
 
419
  return(r);
443
  return(r);
420
}
444
}
421
 
445
 
422
 
446
 
423
/* computes a formatted date based on NLS patterns found in p
447
/* computes a formatted date based on NLS patterns found in p
424
 * returns length of result */
448
 * returns length of result */
425
unsigned short nls_format_date(char *s, unsigned short yr, unsigned char mo, unsigned char dy, const struct nls_patterns *p) {
449
unsigned short nls_format_date(char *s, unsigned short yr, unsigned char mo, unsigned char dy, const struct nls_patterns *p) {
426
  unsigned short items[3];
450
  unsigned short items[3];
427
  /* preset date/month/year in proper order depending on date format */
451
  /* preset date/month/year in proper order depending on date format */
428
  switch (p->dateformat) {
452
  switch (p->dateformat) {
429
    case 0:  /* USA style: m d y */
453
    case 0:  /* USA style: m d y */
430
      items[0] = mo;
454
      items[0] = mo;
431
      items[1] = dy;
455
      items[1] = dy;
432
      items[2] = yr;
456
      items[2] = yr;
433
      break;
457
      break;
434
    case 1:  /* EU style: d m y */
458
    case 1:  /* EU style: d m y */
435
      items[0] = dy;
459
      items[0] = dy;
436
      items[1] = mo;
460
      items[1] = mo;
437
      items[2] = yr;
461
      items[2] = yr;
438
      break;
462
      break;
439
    case 2:  /* Japan-style: y m d */
463
    case 2:  /* Japan-style: y m d */
440
    default:
464
    default:
441
      items[0] = yr;
465
      items[0] = yr;
442
      items[1] = mo;
466
      items[1] = mo;
443
      items[2] = dy;
467
      items[2] = dy;
444
      break;
468
      break;
445
  }
469
  }
446
  /* compute the string */
470
  /* compute the string */
447
  return(sprintf(s, "%02u%s%02u%s%02u", items[0], p->datesep, items[1], p->datesep, items[2]));
471
  return(sprintf(s, "%02u%s%02u%s%02u", items[0], p->datesep, items[1], p->datesep, items[2]));
448
}
472
}
449
 
473
 
450
 
474
 
451
/* computes a formatted time based on NLS patterns found in p
475
/* computes a formatted time based on NLS patterns found in p
452
 * returns length of result */
476
 * returns length of result */
453
unsigned short nls_format_time(char *s, unsigned char ho, unsigned char mn, const struct nls_patterns *p) {
477
unsigned short nls_format_time(char *s, unsigned char ho, unsigned char mn, const struct nls_patterns *p) {
454
  char ampm[2] = {0, 0};
478
  char ampm[2] = {0, 0};
455
  const char *fmt = "%02u%s%02u%s";
479
  const char *fmt = "%02u%s%02u%s";
456
  if (p->timefmt == 0) {
480
  if (p->timefmt == 0) {
457
    if (ho == 12) {
481
    if (ho == 12) {
458
      ampm[0] = 'p';
482
      ampm[0] = 'p';
459
    } else if (ho > 12) {
483
    } else if (ho > 12) {
460
      ho -= 12;
484
      ho -= 12;
461
      ampm[0] = 'p';
485
      ampm[0] = 'p';
462
    } else { /* ho < 12 */
486
    } else { /* ho < 12 */
463
      if (ho == 0) ho = 12;
487
      if (ho == 0) ho = 12;
464
      ampm[0] = 'a';
488
      ampm[0] = 'a';
465
    }
489
    }
466
    fmt = "%2u%s%02u%s";
490
    fmt = "%2u%s%02u%s";
467
  }
491
  }
468
  return(sprintf(s, fmt, ho, p->timesep, mn, ampm));
492
  return(sprintf(s, fmt, ho, p->timesep, mn, ampm));
469
}
493
}
470
 
494
 
471
 
495
 
472
/* computes a formatted integer number based on NLS patterns found in p
496
/* computes a formatted integer number based on NLS patterns found in p
473
 * returns length of result */
497
 * returns length of result */
474
unsigned short nls_format_number(char *s, long num, const struct nls_patterns *p) {
498
unsigned short nls_format_number(char *s, long num, const struct nls_patterns *p) {
475
  unsigned short sl = 0, res, i;
499
  unsigned short sl = 0, res, i;
476
  unsigned char thcount = 0;
500
  unsigned char thcount = 0;
477
 
501
 
478
  /* handle negative values */
502
  /* handle negative values */
479
  if (num < 0) {
503
  if (num < 0) {
480
    s[sl++] = '-';
504
    s[sl++] = '-';
481
    num = 0 - num;
505
    num = 0 - num;
482
  }
506
  }
483
 
507
 
484
  /* write the absolute value now */
508
  /* write the absolute value now */
485
  do {
509
  do {
486
    if ((thcount == 3) && (p->thousep[0] != 0)) {
510
    if ((thcount == 3) && (p->thousep[0] != 0)) {
487
      s[sl++] = p->thousep[0];
511
      s[sl++] = p->thousep[0];
488
      thcount = 0;
512
      thcount = 0;
489
    }
513
    }
490
    s[sl++] = '0' + num % 10;
514
    s[sl++] = '0' + num % 10;
491
    num /= 10;
515
    num /= 10;
492
    thcount++;
516
    thcount++;
493
  } while (num > 0);
517
  } while (num > 0);
494
 
518
 
495
  /* terminate the string and remember its size */
519
  /* terminate the string and remember its size */
496
  s[sl] = 0;
520
  s[sl] = 0;
497
  res = sl;
521
  res = sl;
498
 
522
 
499
  /* reverse the string now (it has been build in reverse) */
523
  /* reverse the string now (it has been build in reverse) */
500
  if (s[0] == '-') {
524
  if (s[0] == '-') {
501
    sl--;
525
    sl--;
502
    s++;
526
    s++;
503
  }
527
  }
504
  for (i = 0; i <= (sl / 2); i++) {
528
  for (i = 0; i <= (sl / 2); i++) {
505
    thcount = s[i];
529
    thcount = s[i];
506
    s[i] = s[sl - (i + 1)];
530
    s[i] = s[sl - (i + 1)];
507
    s[sl - (i + 1)] = thcount;
531
    s[sl - (i + 1)] = thcount;
508
  }
532
  }
509
 
533
 
510
  return(res);
534
  return(res);
511
}
535
}
512
 
536