Subversion Repositories SvarDOS

Rev

Rev 416 | Rev 421 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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