Subversion Repositories SvarDOS

Rev

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

Rev Author Line No. Line
421 mateuszvis 1
/* This file is part of the SvarCOM project and is published under the terms
2
 * of the MIT license.
3
 *
1629 mateusz.vi 4
 * Copyright (C) 2021-2024 Mateusz Viste
421 mateuszvis 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
 
352 mateuszvis 25
/*
26
 * a variety of helper functions
27
 */
28
 
396 mateuszvis 29
#include <i86.h>    /* MK_FP() */
30
 
968 mateusz.vi 31
#include "svarlang.lib\svarlang.h"
32
 
437 mateuszvis 33
#include "env.h"
1881 mateusz.vi 34
#include "rmodinit.h"
437 mateuszvis 35
 
352 mateuszvis 36
#include "helpers.h"
37
 
420 mateuszvis 38
 
965 mateusz.vi 39
 
1823 mateusz.vi 40
void dos_get_date(unsigned short *y, unsigned char *m, unsigned char *d) {
41
  /* get cur date */
42
  _asm {
43
    mov ah, 0x2a  /* DOS 1+ -- Query DOS Date */
44
    int 0x21      /* CX=year DH=month DL=day */
45
    mov bx, y
46
    mov [bx], cx
47
    mov bx, m
48
    mov [bx], dh
49
    mov bx, d
50
    mov [bx], dl
51
  }
52
}
53
 
54
 
55
void dos_get_time(unsigned char *h, unsigned char *m, unsigned char *s) {
56
  _asm {
57
    mov ah, 0x2c  /* DOS 1+ -- Query DOS Time */
58
    int 0x21      /* CH=hour CL=minutes DH=seconds DL=1/100sec */
59
    mov bx, h
60
    mov [bx], ch
61
    mov bx, m
62
    mov [bx], cl
63
    mov bx, s
64
    mov [bx], dh
65
  }
66
}
67
 
68
 
2213 mateusz.vi 69
/* like strlen() */
70
unsigned short sv_strlen(const char *s) {
71
  unsigned short len = 0;
72
  while (*s != 0) {
73
    s++;
74
    len++;
75
  }
76
  return(len);
77
}
78
 
79
 
530 mateuszvis 80
/* case-insensitive comparison of strings, compares up to maxlen characters.
81
 * returns non-zero on equality. */
82
int imatchlim(const char *s1, const char *s2, unsigned short maxlen) {
83
  while (maxlen--) {
352 mateuszvis 84
    char c1, c2;
85
    c1 = *s1;
86
    c2 = *s2;
87
    if ((c1 >= 'a') && (c1 <= 'z')) c1 -= ('a' - 'A');
88
    if ((c2 >= 'a') && (c2 <= 'z')) c2 -= ('a' - 'A');
89
    /* */
90
    if (c1 != c2) return(0);
530 mateuszvis 91
    if (c1 == 0) break;
352 mateuszvis 92
    s1++;
93
    s2++;
94
  }
530 mateuszvis 95
  return(1);
352 mateuszvis 96
}
97
 
98
 
99
/* returns zero if s1 starts with s2 */
100
int strstartswith(const char *s1, const char *s2) {
101
  while (*s2 != 0) {
102
    if (*s1 != *s2) return(-1);
103
    s1++;
104
    s2++;
105
  }
106
  return(0);
107
}
369 mateuszvis 108
 
109
 
538 mateuszvis 110
/* outputs a NULL-terminated string to handle (1=stdout 2=stderr) */
111
void output_internal(const char *s, unsigned char nl, unsigned char handle) {
112
  const static unsigned char *crlf = "\r\n";
369 mateuszvis 113
  _asm {
445 mateuszvis 114
    push ds
115
    pop es         /* make sure es=ds (scasb uses es) */
116
    /* get length of s into CX */
117
    mov ax, 0x4000 /* ah=DOS "write to file" and AL=0 for NULL matching */
118
    mov dx, s      /* set dx to string (required for later) */
119
    mov di, dx     /* set di to string (for NULL matching) */
120
    mov cx, 0xffff /* preset cx to 65535 (-1) */
121
    cld            /* clear DF so scasb increments DI */
122
    repne scasb    /* cmp al, es:[di], inc di, dec cx until match found */
123
    /* CX contains (65535 - strlen(s)) now */
124
    not cx         /* reverse all bits so I get (strlen(s) + 1) */
125
    dec cx         /* this is CX length */
126
    jz WRITEDONE   /* do nothing for empty strings */
127
 
128
    /* output by writing to stdout */
129
    /* mov ah, 0x40 */  /* DOS 2+ -- write to file via handle */
538 mateuszvis 130
    xor bh, bh
131
    mov bl, handle /* set handle (1=stdout 2=stderr) */
445 mateuszvis 132
    /* mov cx, xxx */ /* write CX bytes */
133
    /* mov dx, s   */ /* DS:DX is the source of bytes to "write" */
369 mateuszvis 134
    int 0x21
445 mateuszvis 135
    WRITEDONE:
136
 
137
    /* print out a CR/LF trailer if nl set */
538 mateuszvis 138
    test byte ptr [nl], 0xff
369 mateuszvis 139
    jz FINITO
538 mateuszvis 140
    /* bx still contains handle */
141
    mov ah, 0x40 /* "write to file" */
142
    mov cx, 2
445 mateuszvis 143
    mov dx, crlf
369 mateuszvis 144
    int 0x21
145
    FINITO:
146
  }
147
}
388 mateuszvis 148
 
149
 
542 mateuszvis 150
void nls_output_internal(unsigned short id, unsigned char nl, unsigned char handle) {
538 mateuszvis 151
  const char *NOTFOUND = "NLS_STRING_NOT_FOUND";
968 mateusz.vi 152
  const char *ptr = svarlang_strid(id);
985 mateusz.vi 153
  if ((ptr == NULL) || (ptr[0]) == 0) ptr = NOTFOUND;
542 mateuszvis 154
  output_internal(ptr, nl, handle);
538 mateuszvis 155
}
156
 
157
 
959 mateusz.vi 158
/* output DOS error e to stdout, if stdout is redirected then *additionally*
159
 * also to stderr */
538 mateuszvis 160
void nls_outputnl_doserr(unsigned short e) {
161
  static char errstr[16];
162
  const char *ptr = NULL;
959 mateusz.vi 163
  unsigned char redirflag = 0;
538 mateuszvis 164
  /* find string in nls block */
968 mateusz.vi 165
  if (e < 0xff) ptr = svarlang_strid(0xff00 | e);
538 mateuszvis 166
  /* if not found, use a fallback */
1046 mateusz.vi 167
  if ((ptr == NULL) || (ptr[0] == 0)) {
2220 mateusz.vi 168
    sv_strcpy(errstr, "DOS ERR ");
169
    ustoa(errstr + sv_strlen(errstr), e, 0, '0');
538 mateuszvis 170
    ptr = errstr;
171
  }
959 mateusz.vi 172
 
173
  /* display to stdout */
174
  output_internal(ptr, 1, hSTDOUT);
175
 
176
  /* is stdout redirected? */
177
  _asm {
178
    push bx
179
    push dx
180
 
181
    mov ax, 0x4400   /* query device flags */
182
    mov bx, 1        /* stdout */
183
    int 0x21
184
    /* CF set on error and AX filled with DOS error,
185
     * returns flags in DX on succes:
186
     *  bit 7 reset if handle points to a file, set if handle points to a device  */
187
    jc FAIL
188
    mov redirflag, dl
189
    and redirflag, 128
190
 
191
    FAIL:
192
    pop dx
193
    pop bx
194
  }
195
 
196
  if (redirflag == 0) output_internal(ptr, 1, hSTDERR);
538 mateuszvis 197
}
198
 
199
 
388 mateuszvis 200
/* find first matching files using a FindFirst DOS call
201
 * returns 0 on success or a DOS err code on failure */
202
unsigned short findfirst(struct DTA *dta, const char *pattern, unsigned short attr) {
203
  unsigned short res = 0;
204
  _asm {
205
    /* set DTA location */
206
    mov ah, 0x1a
207
    mov dx, dta
208
    int 0x21
209
    /* */
210
    mov ah, 0x4e    /* FindFirst */
211
    mov dx, pattern
212
    mov cx, attr
213
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
214
    jnc DONE
215
    mov [res], ax
216
    DONE:
217
  }
218
  return(res);
219
}
220
 
221
 
222
/* find next matching, ie. continues an action intiated by findfirst() */
223
unsigned short findnext(struct DTA *dta) {
224
  unsigned short res = 0;
225
  _asm {
2199 mateusz.vi 226
    /* set DTA location */
227
    mov ah, 0x1a
388 mateuszvis 228
    mov dx, dta
2199 mateusz.vi 229
    int 0x21
230
    /* FindNext */
231
    mov ah, 0x4f
388 mateuszvis 232
    int 0x21        /* CF set on error + err code in AX, DTA filled with FileInfoRec on success */
233
    jnc DONE
234
    mov [res], ax
235
    DONE:
236
  }
237
  return(res);
238
}
392 mateuszvis 239
 
240
 
1997 mateusz.vi 241
static unsigned char _dos_getkey_noecho(void);
242
#pragma aux _dos_getkey_noecho = \
243
"mov ax, 0x0c08" /* clear input buffer and execute getchar (INT 21h,AH=8) */  \
244
"int 0x21"                                                                    \
245
"test al, al"    /* if AL == 0 then this is an extended character */          \
246
"jnz GOTCHAR"                                                                 \
247
"mov ah, 0x08"   /* read again to flush extended char from input buffer */    \
248
"int 0x21"                                                                    \
249
"xor al, al"     /* all extended chars are ignored */                         \
250
"GOTCHAR:"       /* received key is in AL now */                              \
251
modify [ah]                                                                   \
252
value [al]
253
 
254
 
392 mateuszvis 255
/* print s string and wait for a single key press from stdin. accepts only
256
 * key presses defined in the c ASCIIZ string. returns offset of pressed key
1997 mateusz.vi 257
 * in string. keys in c MUST BE UPPERCASE! ENTER chooses the FIRST choice */
392 mateuszvis 258
unsigned short askchoice(const char *s, const char *c) {
259
  unsigned short res;
1001 mateusz.vi 260
  char cstr[2] = {0,0};
392 mateuszvis 261
  char key = 0;
262
 
263
  output(s);
264
  output(" ");
1001 mateusz.vi 265
  output("(");
266
  for (res = 0; c[res] != 0; res++) {
267
    if (res != 0) output("/");
268
    cstr[0] = c[res];
269
    output(cstr);
270
  }
271
  output(") ");
392 mateuszvis 272
 
1997 mateusz.vi 273
  AGAIN:
274
  key = _dos_getkey_noecho();
275
  if (key == '\r') key = c[0]; /* ENTER is synonym for the first key */
392 mateuszvis 276
 
277
  /* ucase() result */
278
  if ((key >= 'a') && (key <= 'z')) key -= ('a' - 'A');
279
 
280
  /* is there a match? */
1997 mateusz.vi 281
  for (res = 0; c[res] != 0; res++) {
282
    if (c[res] == key) {
283
      cstr[0] = key;
284
      output(cstr);
285
      output("\r\n");
286
      return(res);
287
    }
288
  }
392 mateuszvis 289
 
290
  goto AGAIN;
291
}
292
 
293
 
399 mateuszvis 294
/* converts a path to its canonic representation, returns 0 on success
295
 * or DOS err on failure (invalid drive) */
296
unsigned short file_truename(const char *src, char *dst) {
297
  unsigned short res = 0;
392 mateuszvis 298
  _asm {
399 mateuszvis 299
    push es
392 mateuszvis 300
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
301
    push ds
302
    pop es
303
    mov si, src
304
    mov di, dst
305
    int 0x21
399 mateuszvis 306
    jnc DONE
307
    mov [res], ax
308
    DONE:
309
    pop es
392 mateuszvis 310
  }
399 mateuszvis 311
  return(res);
392 mateuszvis 312
}
313
 
314
 
315
/* returns DOS attributes of file, or -1 on error */
316
int file_getattr(const char *fname) {
317
  int res = -1;
318
  _asm {
319
    mov ax, 0x4300  /* query file attributes, fname at DS:DX */
320
    mov dx, fname
321
    int 0x21        /* CX=attributes if CF=0, otherwise AX=errno */
322
    jc DONE
323
    mov [res], cx
324
    DONE:
325
  }
326
  return(res);
327
}
396 mateuszvis 328
 
329
 
330
/* returns screen's width (in columns) */
331
unsigned short screen_getwidth(void) {
332
  /* BIOS 0040:004A = word containing screen width in text columns */
333
  unsigned short far *scrw = MK_FP(0x40, 0x4a);
334
  return(*scrw);
335
}
336
 
337
 
338
/* returns screen's height (in rows) */
339
unsigned short screen_getheight(void) {
340
  /* BIOS 0040:0084 = byte containing maximum valid row value (EGA ONLY) */
341
  unsigned char far *scrh = MK_FP(0x40, 0x84);
342
  if (*scrh == 0) return(25);  /* pre-EGA adapter */
343
  return(*scrh + 1);
344
}
345
 
346
 
347
/* displays the "Press any key to continue" msg and waits for a keypress */
348
void press_any_key(void) {
437 mateuszvis 349
  nls_output(15, 1); /* Press any key to continue... */
396 mateuszvis 350
  _asm {
351
    mov ah, 0x08  /* no echo console input */
352
    int 0x21      /* pressed key in AL now (0 for extended keys) */
353
    test al, al
354
    jnz DONE
355
    int 0x21      /* executed ah=8 again to read the rest of extended key */
356
    DONE:
357
    /* output CR/LF */
358
    mov ah, 0x02
359
    mov dl, 0x0D
360
    int 0x21
361
    mov dl, 0x0A
362
    int 0x21
363
  }
364
}
399 mateuszvis 365
 
366
 
367
/* validate a drive (A=0, B=1, etc). returns 1 if valid, 0 otherwise */
368
int isdrivevalid(unsigned char drv) {
369
  _asm {
370
    mov ah, 0x19  /* query default (current) disk */
371
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
372
    mov ch, al    /* save current drive to ch */
373
    /* try setting up the drive as current */
374
    mov ah, 0x0E   /* select default drive */
375
    mov dl, [drv]  /* 0=A, 1=B, etc */
376
    int 0x21
377
    /* this call does not set CF on error, I must check cur drive to look for success */
378
    mov ah, 0x19  /* query default (current) disk */
379
    int 0x21      /* drive in AL (0=A, 1=B, etc) */
380
    mov [drv], 1  /* preset result as success */
381
    cmp al, dl    /* is eq? */
382
    je DONE
383
    mov [drv], 0  /* fail */
384
    jmp FAILED
385
    DONE:
386
    /* set current drive back to what it was initially */
387
    mov ah, 0x0E
388
    mov dl, ch
389
    int 0x21
390
    FAILED:
391
  }
392
  return(drv);
393
}
406 mateuszvis 394
 
395
 
396
/* converts a 8+3 filename into 11-bytes FCB format (MYFILE  EXT) */
397
void file_fname2fcb(char *dst, const char *src) {
398
  unsigned short i;
399
 
400
  /* fill dst with 11 spaces and a NULL terminator */
420 mateuszvis 401
  for (i = 0; i < 11; i++) dst[i] = ' ';
402
  dst[11] = 0;
406 mateuszvis 403
 
404
  /* copy fname until dot (.) or 8 characters */
405
  for (i = 0; i < 8; i++) {
406
    if ((src[i] == '.') || (src[i] == 0)) break;
407
    dst[i] = src[i];
408
  }
409
 
410
  /* advance src until extension or end of string */
411
  src += i;
412
  for (;;) {
413
    if (*src == '.') {
414
      src++; /* next character is extension */
415
      break;
416
    }
417
    if (*src == 0) break;
418
  }
419
 
420
  /* copy extension to dst (3 chars max) */
421
  dst += 8;
422
  for (i = 0; i < 3; i++) {
423
    if (src[i] == 0) break;
424
    dst[i] = src[i];
425
  }
426
}
427
 
428
 
429
/* converts a 11-bytes FCB filename (MYFILE  EXT) into 8+3 format (MYFILE.EXT) */
430
void file_fcb2fname(char *dst, const char *src) {
431
  unsigned short i, end = 0;
432
 
433
  for (i = 0; i < 8; i++) {
434
    dst[i] = src[i];
435
    if (dst[i] != ' ') end = i + 1;
436
  }
437
 
438
  /* is there an extension? */
439
  if (src[8] == ' ') {
440
    dst[end] = 0;
441
  } else { /* found extension: copy it until first space */
442
    dst[end++] = '.';
443
    for (i = 8; i < 11; i++) {
444
      if (src[i] == ' ') break;
445
      dst[end++] = src[i];
446
    }
447
    dst[end] = 0;
448
  }
449
}
410 mateuszvis 450
 
451
 
2220 mateusz.vi 452
/* convert an unsigned short to ASCIZ, output set to fixlen chars if fixlen
453
 * is non zero (prefixed with prefixchar if value too small, else truncated)
454
 * returns length of produced string */
455
unsigned short ustoa(char *dst, unsigned short n, unsigned char fixlen, char prefixchar) {
456
  unsigned short r;
457
  unsigned char i;
458
  unsigned char nonzerocharat = 5;
459
 
460
  for (i = 4; i != 0xff; i--) {
461
    r = n % 10;
462
    n /= 10;
463
    dst[i] = '0' + r;
464
    if (r != 0) nonzerocharat = i;
465
  }
466
 
467
  /* change prefix to prefixchar (eg. "00120" -> "  120") */
468
  for (i = 0; i < 4; i++) {
469
    if (dst[i] != '0') break;
470
    dst[i] = prefixchar;
471
  }
472
 
473
  /* apply fixlen, if set */
474
  if ((fixlen > 0) && (fixlen < 6)) {
475
    nonzerocharat = 5 - fixlen;
476
  }
477
 
478
  if (nonzerocharat != 0) {
479
    memcpy_ltr(dst, dst + nonzerocharat, 5 - nonzerocharat);
480
  }
481
 
482
  dst[5 - nonzerocharat] = 0;
483
 
484
  return(5 - nonzerocharat);
485
}
486
 
487
 
430 mateuszvis 488
/* converts an ASCIIZ string into an unsigned short. returns 0 on success.
489
 * on error, result will contain all valid digits that were read until
490
 * error occurred (0 on overflow or if parsing failed immediately) */
426 mateuszvis 491
int atous(unsigned short *r, const char *s) {
410 mateuszvis 492
  int err = 0;
493
 
494
  _asm {
495
    mov si, s
496
    xor ax, ax  /* general purpose register */
497
    xor cx, cx  /* contains the result */
498
    mov bx, 10  /* used as a multiplicative step */
499
 
500
    NEXTBYTE:
501
    xchg cx, ax /* move result into cx temporarily */
502
    lodsb  /* AL = DS:[SI++] */
503
    /* is AL 0? if so we're done */
504
    test al, al
505
    jz DONE
506
    /* validate that AL is in range '0'-'9' */
507
    sub al, '0'
430 mateuszvis 508
    jc FAIL   /* invalid character detected */
410 mateuszvis 509
    cmp al, 9
430 mateuszvis 510
    jg FAIL   /* invalid character detected */
410 mateuszvis 511
    /* restore result into AX (CX contains the new digit) */
512
    xchg cx, ax
513
    /* multiply result by 10 and add cl */
514
    mul bx    /* DX AX = AX * BX(10) */
430 mateuszvis 515
    jc OVERFLOW  /* overflow */
410 mateuszvis 516
    add ax, cx
430 mateuszvis 517
    /* if CF is set then overflow occurred (overflow part lands in DX) */
410 mateuszvis 518
    jnc NEXTBYTE
519
 
430 mateuszvis 520
    OVERFLOW:
521
    xor cx, cx  /* make sure result is zeroed in case overflow occured */
522
 
410 mateuszvis 523
    FAIL:
524
    inc [err]
525
 
526
    DONE: /* save result (CX) into indirect memory address r */
527
    mov bx, [r]
528
    mov [bx], cx
529
  }
530
  return(err);
531
}
415 mateuszvis 532
 
533
 
534
/* appends a backslash if path is a directory
535
 * returns the (possibly updated) length of path */
536
unsigned short path_appendbkslash_if_dir(char *path) {
537
  unsigned short len;
538
  int attr;
539
  for (len = 0; path[len] != 0; len++);
540
  if (len == 0) return(0);
541
  if (path[len - 1] == '\\') return(len);
542
  /* */
543
  attr = file_getattr(path);
544
  if ((attr > 0) && (attr & DOS_ATTR_DIR)) {
545
    path[len++] = '\\';
546
    path[len] = 0;
547
  }
548
  return(len);
549
}
416 mateuszvis 550
 
551
 
552
/* get current path drive d (A=1, B=2, etc - 0 is "current drive")
553
 * returns 0 on success, doserr otherwise */
554
unsigned short curpathfordrv(char *buff, unsigned char d) {
555
  unsigned short r = 0;
556
 
557
  _asm {
558
    /* is d == 0? then I need to resolve current drive */
559
    cmp byte ptr [d], 0
560
    jne GETCWD
561
    /* resolve cur drive */
562
    mov ah, 0x19  /* get current default drive */
563
    int 0x21      /* al = drive (00h = A:, 01h = B:, etc) */
564
    inc al        /* convert to 1=A, 2=B, etc */
565
    mov [d], al
566
 
567
    GETCWD:
568
    /* prepend buff with drive:\ */
569
    mov si, buff
570
    mov dl, [d]
571
    mov [si], dl
572
    add byte ptr [si], 'A' - 1
573
    inc si
574
    mov [si], ':'
575
    inc si
576
    mov [si], '\\'
577
    inc si
578
 
579
    mov ah, 0x47      /* get current directory of drv DL into DS:SI */
580
    int 0x21
581
    jnc DONE
582
    mov [r], ax       /* copy result from ax */
583
 
584
    DONE:
585
  }
586
 
587
  return(r);
588
}
420 mateuszvis 589
 
590
 
2213 mateusz.vi 591
/* like strcpy() but returns the string's length */
592
unsigned short sv_strcpy(char *dst, const char *s) {
593
  unsigned short len = 0;
594
  for (;;) {
595
    *dst = *s;
596
    if (*s == 0) return(len);
597
    dst++;
598
    s++;
599
    len++;
600
  }
601
}
602
 
603
 
604
/* like sv_strcpy() but operates on far pointers */
605
unsigned short sv_strcpy_far(char far *dst, const char far *s) {
606
  unsigned short len = 0;
607
  for (;;) {
608
    *dst = *s;
609
    if (*s == 0) return(len);
610
    dst++;
611
    s++;
612
    len++;
613
  }
614
}
615
 
616
 
617
/* like strcat() */
618
void sv_strcat(char *dst, const char *s) {
619
  /* advance dst to end of string */
620
  while (*dst != 0) dst++;
621
  /* append string */
622
  sv_strcpy(dst, s);
623
}
624
 
625
 
626
/* like strcat() but operates on far pointers */
627
void sv_strcat_far(char far *dst, const char far *s) {
628
  /* advance dst to end of string */
629
  while (*dst != 0) dst++;
630
  /* append string */
631
  sv_strcpy_far(dst, s);
632
}
633
 
634
 
420 mateuszvis 635
/* fills a nls_patterns struct with current NLS patterns, returns 0 on success, DOS errcode otherwise */
636
unsigned short nls_getpatterns(struct nls_patterns *p) {
637
  unsigned short r = 0;
638
 
639
  _asm {
640
    mov ax, 0x3800  /* DOS 2+ -- Get Country Info for current country */
641
    mov dx, p       /* DS:DX points to the CountryInfoRec buffer */
642
    int 0x21
643
    jnc DONE
644
    mov [r], ax     /* copy DOS err code to r */
645
    DONE:
646
  }
647
 
648
  return(r);
649
}
650
 
651
 
652
/* computes a formatted date based on NLS patterns found in p
653
 * returns length of result */
654
unsigned short nls_format_date(char *s, unsigned short yr, unsigned char mo, unsigned char dy, const struct nls_patterns *p) {
655
  unsigned short items[3];
2220 mateusz.vi 656
  unsigned short slen;
657
 
420 mateuszvis 658
  /* preset date/month/year in proper order depending on date format */
659
  switch (p->dateformat) {
660
    case 0:  /* USA style: m d y */
661
      items[0] = mo;
662
      items[1] = dy;
663
      items[2] = yr;
664
      break;
665
    case 1:  /* EU style: d m y */
666
      items[0] = dy;
667
      items[1] = mo;
668
      items[2] = yr;
669
      break;
670
    case 2:  /* Japan-style: y m d */
671
    default:
672
      items[0] = yr;
673
      items[1] = mo;
674
      items[2] = dy;
675
      break;
676
  }
677
  /* compute the string */
2220 mateusz.vi 678
 
679
  slen = ustoa(s, items[0], 2, '0');
680
  slen += sv_strcpy(s + slen, p->datesep);
681
  slen += ustoa(s + slen, items[1], 2, '0');
682
  slen += sv_strcpy(s + slen, p->datesep);
683
  slen += ustoa(s + slen, items[2], 2, '0');
684
 
685
  return(slen);
420 mateuszvis 686
}
687
 
688
 
426 mateuszvis 689
/* computes a formatted time based on NLS patterns found in p, sc are ignored if set 0xff
420 mateuszvis 690
 * returns length of result */
426 mateuszvis 691
unsigned short nls_format_time(char *s, unsigned char ho, unsigned char mn, unsigned char sc, const struct nls_patterns *p) {
692
  char ampm = 0;
693
  unsigned short res;
694
 
420 mateuszvis 695
  if (p->timefmt == 0) {
696
    if (ho == 12) {
426 mateuszvis 697
      ampm = 'p';
420 mateuszvis 698
    } else if (ho > 12) {
699
      ho -= 12;
426 mateuszvis 700
      ampm = 'p';
420 mateuszvis 701
    } else { /* ho < 12 */
702
      if (ho == 0) ho = 12;
426 mateuszvis 703
      ampm = 'a';
420 mateuszvis 704
    }
2220 mateusz.vi 705
    res = ustoa(s, ho, 2, ' '); /* %2u */
426 mateuszvis 706
  } else {
2220 mateusz.vi 707
    res = ustoa(s, ho, 2, '0'); /* %02u */
420 mateuszvis 708
  }
426 mateuszvis 709
 
710
  /* append separator and minutes */
2220 mateusz.vi 711
  res += sv_strcpy(s + res, p->timesep);
712
  res += ustoa(s + res, mn, 2, '0'); /* %02u */
426 mateuszvis 713
 
714
  /* if seconds provided, append them, too */
2220 mateusz.vi 715
  if (sc != 0xff) {
716
    res += sv_strcpy(s + res, p->timesep);
717
    res += ustoa(s + res, sc, 2, '0'); /* %02u */
718
  }
426 mateuszvis 719
 
2220 mateusz.vi 720
  /* finally append the AM/PM char */
426 mateuszvis 721
  if (ampm != 0) s[res++] = ampm;
722
  s[res] = 0;
723
 
724
  return(res);
420 mateuszvis 725
}
726
 
727
 
728
/* computes a formatted integer number based on NLS patterns found in p
729
 * returns length of result */
423 mateuszvis 730
unsigned short nls_format_number(char *s, unsigned long num, const struct nls_patterns *p) {
731
  unsigned short sl = 0, i;
420 mateuszvis 732
  unsigned char thcount = 0;
733
 
423 mateuszvis 734
  /* write the value (reverse) with thousand separators (if any defined) */
420 mateuszvis 735
  do {
736
    if ((thcount == 3) && (p->thousep[0] != 0)) {
737
      s[sl++] = p->thousep[0];
738
      thcount = 0;
739
    }
740
    s[sl++] = '0' + num % 10;
741
    num /= 10;
742
    thcount++;
743
  } while (num > 0);
744
 
423 mateuszvis 745
  /* terminate the string */
420 mateuszvis 746
  s[sl] = 0;
747
 
423 mateuszvis 748
  /* reverse the string now (has been built in reverse) */
749
  for (i = sl / 2 + (sl & 1); i < sl; i++) {
420 mateuszvis 750
    thcount = s[i];
423 mateuszvis 751
    s[i] = s[sl - (i + 1)];   /* abc'de  if i=3 then ' <-> c */
420 mateuszvis 752
    s[sl - (i + 1)] = thcount;
753
  }
754
 
423 mateuszvis 755
  return(sl);
420 mateuszvis 756
}
437 mateuszvis 757
 
758
 
1137 mateusz.vi 759
/* capitalize an ASCIZ string following country-dependent rules */
760
void nls_strtoup(char *buff) {
761
  unsigned short errcode = 0;
762
  /* requires DOS 4+ */
763
  _asm {
764
    push ax
765
    push dx
766
 
767
    mov ax, 0x6522 /* country-dependent capitalize string (DOS 4+) */
768
    mov dx, buff   /* DS:DX -> string to capitalize */
769
    int 0x21
770
    jnc DONE
771
 
772
    mov errcode, ax /* set errcode on failure */
773
    DONE:
774
 
775
    pop dx
776
    pop ax
777
  }
778
 
2213 mateusz.vi 779
  /* do a naive upcase if DOS has no NLS support */
780
  if (errcode != 0) {
781
    while (*buff) {
782
      if (*buff < 128) { /* apply only to 7bit (low ascii) values */
783
        *buff &= 0xDF;
784
      }
785
      buff++;
786
    }
787
  }
1137 mateusz.vi 788
}
789
 
790
 
1881 mateusz.vi 791
/* reload nls ressources from svarcom.lng into svarlang_mem and rmod */
792
void nls_langreload(char *buff, unsigned short rmodseg) {
1629 mateusz.vi 793
  const char far *dosdir;
968 mateusz.vi 794
  const char far *lang;
965 mateusz.vi 795
  static unsigned short lastlang;
1629 mateusz.vi 796
  unsigned short dosdirlen;
1881 mateusz.vi 797
  unsigned short rmodenvseg = *(unsigned short far *)MK_FP(rmodseg, RMOD_OFFSET_ENVSEG);
798
  unsigned char far *rmodcritmsg = MK_FP(rmodseg, RMOD_OFFSET_CRITMSG);
799
  int i;
437 mateuszvis 800
 
801
  /* look up the LANG env variable, upcase it and copy to lang */
1881 mateusz.vi 802
  lang = env_lookup_val(rmodenvseg, "LANG");
968 mateusz.vi 803
  if ((lang == NULL) || (lang[0] == 0)) return;
2213 mateusz.vi 804
  memcpy_ltr_far(buff, lang, 2);
437 mateuszvis 805
  buff[2] = 0;
806
 
807
  /* check if there is need to reload at all */
2213 mateusz.vi 808
  if (lastlang == *((unsigned short *)buff)) return;
437 mateuszvis 809
 
968 mateusz.vi 810
  buff[4] = 0;
1881 mateusz.vi 811
  dosdir = env_lookup_val(rmodenvseg, "DOSDIR");
1629 mateusz.vi 812
  if (dosdir == NULL) return;
437 mateuszvis 813
 
2213 mateusz.vi 814
  sv_strcpy_far(buff + 4, dosdir);
815
  dosdirlen = sv_strlen(buff + 4);
1629 mateusz.vi 816
  if (buff[4 + dosdirlen - 1] == '\\') dosdirlen--;
2213 mateusz.vi 817
  memcpy_ltr(buff + 4 + dosdirlen, "\\SVARCOM.LNG", 13);
437 mateuszvis 818
 
1629 mateusz.vi 819
  /* try loading %DOSDIR%\SVARCOM.LNG */
820
  if (svarlang_load(buff + 4, buff) != 0) {
1881 mateusz.vi 821
    /* failed! try %DOSDIR%\BIN\SVARCOM.LNG */
2213 mateusz.vi 822
    memcpy_ltr(buff + 4 + dosdirlen, "\\BIN\\SVARCOM.LNG", 17);
1629 mateusz.vi 823
    if (svarlang_load(buff + 4, buff) != 0) return;
824
  }
825
 
2213 mateusz.vi 826
  memcpy_ltr_far(&lastlang, lang, 2);
1881 mateusz.vi 827
 
828
  /* update RMOD's critical handler with new strings */
2161 mateusz.vi 829
  for (i = 0; i < 9; i++) {
1881 mateusz.vi 830
    int len;
2213 mateusz.vi 831
    len = sv_strlen(svarlang_str(3, i));
1881 mateusz.vi 832
    if (len > 15) len = 15;
2213 mateusz.vi 833
    memcpy_ltr_far(rmodcritmsg + (i * 16), svarlang_str(3, i), len);
834
    memcpy_ltr_far(rmodcritmsg + (i * 16) + len, "$", 1);
1881 mateusz.vi 835
  }
836
  /* The ARIF string is special: always 4 bytes long and no $ terminator */
2213 mateusz.vi 837
  memcpy_ltr_far(rmodcritmsg + (9 * 16), svarlang_str(3,9), 4);
437 mateuszvis 838
}
571 mateuszvis 839
 
840
 
841
/* locates executable fname in path and fill res with result. returns 0 on success,
842
 * -1 on failed match and -2 on failed match + "don't even try with other paths"
843
 * extptr is filled with a ptr to the extension in fname (NULL if no extension) */
844
int lookup_cmd(char *res, const char *fname, const char *path, const char **extptr) {
845
  unsigned short lastbslash = 0;
846
  unsigned short i, len;
847
  unsigned char explicitpath = 0;
1072 mateusz.vi 848
  const char *exec_ext[] = {"COM", "EXE", "BAT", NULL};
571 mateuszvis 849
 
850
  /* does the original fname has an explicit path prefix or explicit ext? */
851
  *extptr = NULL;
852
  for (i = 0; fname[i] != 0; i++) {
853
    switch (fname[i]) {
854
      case ':':
855
      case '\\':
856
        explicitpath = 1;
857
        *extptr = NULL; /* extension is the last dot AFTER all path delimiters */
858
        break;
859
      case '.':
860
        *extptr = fname + i + 1;
861
        break;
862
    }
863
  }
864
 
1072 mateusz.vi 865
  /* if explicit ext found, make sure it is executable */
866
  if (*extptr != NULL) {
867
    for (i = 0; exec_ext[i] != NULL; i++) if (imatch(*extptr, exec_ext[i])) break;
868
    if (exec_ext[i] == NULL) return(-2); /* bad extension - don't try running it ever */
869
  }
870
 
571 mateuszvis 871
  /* normalize filename */
872
  if (file_truename(fname, res) != 0) return(-2);
873
 
874
  /* printf("truename: %s\r\n", res); */
875
 
1072 mateusz.vi 876
  /* figure out where the command starts */
571 mateuszvis 877
  for (len = 0; res[len] != 0; len++) {
878
    switch (res[len]) {
879
      case '?':   /* abort on any wildcard character */
880
      case '*':
881
        return(-2);
882
      case '\\':
883
        lastbslash = len;
884
        break;
885
    }
886
  }
887
 
888
  /* printf("lastbslash=%u\r\n", lastbslash); */
889
 
890
  /* if no path prefix was found in fname (no colon or backslash) AND we have
891
   * a path arg, then assemble path+filename */
892
  if ((!explicitpath) && (path != NULL) && (path[0] != 0)) {
2213 mateusz.vi 893
    i = sv_strlen(path);
571 mateuszvis 894
    if (path[i - 1] != '\\') i++; /* add a byte for inserting a bkslash after path */
895
    /* move the filename at the place where path will end */
2213 mateusz.vi 896
    memcpy_rtl(res + i, res + lastbslash + 1, len - lastbslash);
571 mateuszvis 897
    /* copy path in front of the filename and make sure there is a bkslash sep */
2213 mateusz.vi 898
    memcpy_ltr(res, path, i);
571 mateuszvis 899
    res[i - 1] = '\\';
900
  }
901
 
902
  /* if no extension was initially provided, try matching COM, EXE, BAT */
903
  if (*extptr == NULL) {
1072 mateusz.vi 904
    int attr;
2213 mateusz.vi 905
    len = sv_strlen(res);
1072 mateusz.vi 906
    res[len++] = '.';
907
    for (i = 0; exec_ext[i] != NULL; i++) {
2213 mateusz.vi 908
      sv_strcpy(res + len, exec_ext[i]);
571 mateuszvis 909
      /* printf("? '%s'\r\n", res); */
1072 mateusz.vi 910
      *extptr = exec_ext[i];
911
      attr = file_getattr(res);
912
      if (attr < 0) continue; /* file not found */
913
      if (attr & DOS_ATTR_DIR) continue; /* this is a directory */
914
      if (attr & DOS_ATTR_VOL) continue; /* this is a volume */
915
      return(0);
571 mateuszvis 916
    }
917
  } else { /* try finding it as-is */
918
    /* printf("? '%s'\r\n", res); */
1072 mateusz.vi 919
    int attr = file_getattr(res);
920
    if ((attr >= 0) &&  /* file exists */
921
        ((attr & DOS_ATTR_DIR) == 0) && /* is not a directory */
922
        ((attr & DOS_ATTR_VOL) == 0)) { /* is not a volume */
923
      return(0);
924
    }
571 mateuszvis 925
  }
926
 
927
  /* not found */
928
  if (explicitpath) return(-2); /* don't bother trying other paths, the caller had its own path preset anyway */
929
  return(-1);
930
}
931
 
932
 
933
/* fills fname with the path and filename to the linkfile related to the
934
 * executable link "linkname". returns 0 on success. */
935
int link_computefname(char *fname, const char *linkname, unsigned short env_seg) {
1044 mateusz.vi 936
  unsigned short pathlen, doserr = 0;
571 mateuszvis 937
 
938
  /* fetch %DOSDIR% */
939
  pathlen = env_lookup_valcopy(fname, 128, env_seg, "DOSDIR");
1988 mateusz.vi 940
  if (pathlen == 0) return(-1);
571 mateuszvis 941
 
942
  /* prep filename: %DOSDIR%\LINKS\PKG.LNK */
943
  if (fname[pathlen - 1] == '\\') pathlen--;
2220 mateusz.vi 944
  pathlen += sv_strcpy(fname + pathlen, "\\LINKS");
1044 mateusz.vi 945
  /* create \LINKS if not exists */
946
  if (file_getattr(fname) < 0) {
947
    _asm {
948
      push dx
949
      mov ah, 0x39
950
      mov dx, fname
951
      int 0x21
952
      jnc DONE
953
      mov doserr, ax
954
      DONE:
955
      pop dx
956
    }
957
    if (doserr) {
958
      output(fname);
959
      output(" - ");
960
      nls_outputnl(255, doserr);
961
      return(-1);
962
    }
963
  }
964
  /* quit early if dir does not exist (or is not a dir) */
1043 mateusz.vi 965
  if (file_getattr(fname) != DOS_ATTR_DIR) {
966
    output(fname);
967
    output(" - ");
968
    nls_outputnl(255,3); /* path not found */
969
    return(-1);
970
  }
2220 mateusz.vi 971
  sv_strcat(fname + pathlen, "\\");
972
  sv_strcat(fname + pathlen, linkname);
973
  sv_strcat(fname + pathlen, ".LNK");
571 mateuszvis 974
 
975
  return(0);
976
}
2195 mateusz.vi 977
 
978
 
979
/* like memcpy() but guarantees to copy from left to right */
980
void memcpy_ltr(void *d, const void *s, unsigned short len) {
981
  unsigned char const *ss = s;
982
  unsigned char *dd = d;
983
 
984
  while (len--) {
985
    *dd = *ss;
986
    ss++;
987
    dd++;
988
  }
989
}
990
 
2213 mateusz.vi 991
 
992
/* like memcpy_ltr() but operates on far pointers */
993
void memcpy_ltr_far(void far *d, const void far *s, unsigned short len) {
994
  unsigned char const far *ss = s;
995
  unsigned char far *dd = d;
996
 
997
  while (len--) {
998
    *dd = *ss;
999
    ss++;
1000
    dd++;
1001
  }
1002
}
1003
 
1004
 
2195 mateusz.vi 1005
/* like memcpy() but guarantees to copy from right to left */
1006
void memcpy_rtl(void *d, const void *s, unsigned short len) {
1007
  unsigned char const *ss = s;
1008
  unsigned char *dd = d;
1009
 
1010
  dd += len - 1;
1011
  ss += len - 1;
1012
  while (len--) {
1013
    *dd = *ss;
1014
    ss--;
1015
    dd--;
1016
  }
1017
}
2213 mateusz.vi 1018
 
1019
 
1020
/* like bzero(), but accepts far pointers */
1021
void sv_bzero(void far *dst, unsigned short len) {
1022
  char far *d = dst;
1023
  while (len--) {
1024
    *d = 0;
1025
    d++;
1026
  }
1027
}
2218 mateusz.vi 1028
 
1029
 
1030
/* like memset() */
1031
void sv_memset(void *dst, unsigned char c, unsigned short len) {
1032
  unsigned char *d = dst;
1033
  while (len--) {
1034
    *d = c;
1035
    d++;
1036
  }
1037
}