Subversion Repositories SvarDOS

Rev

Rev 2213 | Rev 2220 | 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() */
420 mateuszvis 30
#include <stdio.h>  /* sprintf() */
396 mateuszvis 31
 
968 mateusz.vi 32
#include "svarlang.lib\svarlang.h"
33
 
437 mateuszvis 34
#include "env.h"
1881 mateusz.vi 35
#include "rmodinit.h"
437 mateuszvis 36
 
352 mateuszvis 37
#include "helpers.h"
38
 
420 mateuszvis 39
 
965 mateusz.vi 40
 
1823 mateusz.vi 41
void dos_get_date(unsigned short *y, unsigned char *m, unsigned char *d) {
42
  /* get cur date */
43
  _asm {
44
    mov ah, 0x2a  /* DOS 1+ -- Query DOS Date */
45
    int 0x21      /* CX=year DH=month DL=day */
46
    mov bx, y
47
    mov [bx], cx
48
    mov bx, m
49
    mov [bx], dh
50
    mov bx, d
51
    mov [bx], dl
52
  }
53
}
54
 
55
 
56
void dos_get_time(unsigned char *h, unsigned char *m, unsigned char *s) {
57
  _asm {
58
    mov ah, 0x2c  /* DOS 1+ -- Query DOS Time */
59
    int 0x21      /* CH=hour CL=minutes DH=seconds DL=1/100sec */
60
    mov bx, h
61
    mov [bx], ch
62
    mov bx, m
63
    mov [bx], cl
64
    mov bx, s
65
    mov [bx], dh
66
  }
67
}
68
 
69
 
2213 mateusz.vi 70
/* like strlen() */
71
unsigned short sv_strlen(const char *s) {
72
  unsigned short len = 0;
73
  while (*s != 0) {
74
    s++;
75
    len++;
76
  }
77
  return(len);
78
}
79
 
80
 
530 mateuszvis 81
/* case-insensitive comparison of strings, compares up to maxlen characters.
82
 * returns non-zero on equality. */
83
int imatchlim(const char *s1, const char *s2, unsigned short maxlen) {
84
  while (maxlen--) {
352 mateuszvis 85
    char c1, c2;
86
    c1 = *s1;
87
    c2 = *s2;
88
    if ((c1 >= 'a') && (c1 <= 'z')) c1 -= ('a' - 'A');
89
    if ((c2 >= 'a') && (c2 <= 'z')) c2 -= ('a' - 'A');
90
    /* */
91
    if (c1 != c2) return(0);
530 mateuszvis 92
    if (c1 == 0) break;
352 mateuszvis 93
    s1++;
94
    s2++;
95
  }
530 mateuszvis 96
  return(1);
352 mateuszvis 97
}
98
 
99
 
100
/* returns zero if s1 starts with s2 */
101
int strstartswith(const char *s1, const char *s2) {
102
  while (*s2 != 0) {
103
    if (*s1 != *s2) return(-1);
104
    s1++;
105
    s2++;
106
  }
107
  return(0);
108
}
369 mateuszvis 109
 
110
 
538 mateuszvis 111
/* outputs a NULL-terminated string to handle (1=stdout 2=stderr) */
112
void output_internal(const char *s, unsigned char nl, unsigned char handle) {
113
  const static unsigned char *crlf = "\r\n";
369 mateuszvis 114
  _asm {
445 mateuszvis 115
    push ds
116
    pop es         /* make sure es=ds (scasb uses es) */
117
    /* get length of s into CX */
118
    mov ax, 0x4000 /* ah=DOS "write to file" and AL=0 for NULL matching */
119
    mov dx, s      /* set dx to string (required for later) */
120
    mov di, dx     /* set di to string (for NULL matching) */
121
    mov cx, 0xffff /* preset cx to 65535 (-1) */
122
    cld            /* clear DF so scasb increments DI */
123
    repne scasb    /* cmp al, es:[di], inc di, dec cx until match found */
124
    /* CX contains (65535 - strlen(s)) now */
125
    not cx         /* reverse all bits so I get (strlen(s) + 1) */
126
    dec cx         /* this is CX length */
127
    jz WRITEDONE   /* do nothing for empty strings */
128
 
129
    /* output by writing to stdout */
130
    /* mov ah, 0x40 */  /* DOS 2+ -- write to file via handle */
538 mateuszvis 131
    xor bh, bh
132
    mov bl, handle /* set handle (1=stdout 2=stderr) */
445 mateuszvis 133
    /* mov cx, xxx */ /* write CX bytes */
134
    /* mov dx, s   */ /* DS:DX is the source of bytes to "write" */
369 mateuszvis 135
    int 0x21
445 mateuszvis 136
    WRITEDONE:
137
 
138
    /* print out a CR/LF trailer if nl set */
538 mateuszvis 139
    test byte ptr [nl], 0xff
369 mateuszvis 140
    jz FINITO
538 mateuszvis 141
    /* bx still contains handle */
142
    mov ah, 0x40 /* "write to file" */
143
    mov cx, 2
445 mateuszvis 144
    mov dx, crlf
369 mateuszvis 145
    int 0x21
146
    FINITO:
147
  }
148
}
388 mateuszvis 149
 
150
 
542 mateuszvis 151
void nls_output_internal(unsigned short id, unsigned char nl, unsigned char handle) {
538 mateuszvis 152
  const char *NOTFOUND = "NLS_STRING_NOT_FOUND";
968 mateusz.vi 153
  const char *ptr = svarlang_strid(id);
985 mateusz.vi 154
  if ((ptr == NULL) || (ptr[0]) == 0) ptr = NOTFOUND;
542 mateuszvis 155
  output_internal(ptr, nl, handle);
538 mateuszvis 156
}
157
 
158
 
959 mateusz.vi 159
/* output DOS error e to stdout, if stdout is redirected then *additionally*
160
 * also to stderr */
538 mateuszvis 161
void nls_outputnl_doserr(unsigned short e) {
162
  static char errstr[16];
163
  const char *ptr = NULL;
959 mateusz.vi 164
  unsigned char redirflag = 0;
538 mateuszvis 165
  /* find string in nls block */
968 mateusz.vi 166
  if (e < 0xff) ptr = svarlang_strid(0xff00 | e);
538 mateuszvis 167
  /* if not found, use a fallback */
1046 mateusz.vi 168
  if ((ptr == NULL) || (ptr[0] == 0)) {
538 mateuszvis 169
    sprintf(errstr, "DOS ERR %u", e);
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
 
430 mateuszvis 452
/* converts an ASCIIZ string into an unsigned short. returns 0 on success.
453
 * on error, result will contain all valid digits that were read until
454
 * error occurred (0 on overflow or if parsing failed immediately) */
426 mateuszvis 455
int atous(unsigned short *r, const char *s) {
410 mateuszvis 456
  int err = 0;
457
 
458
  _asm {
459
    mov si, s
460
    xor ax, ax  /* general purpose register */
461
    xor cx, cx  /* contains the result */
462
    mov bx, 10  /* used as a multiplicative step */
463
 
464
    NEXTBYTE:
465
    xchg cx, ax /* move result into cx temporarily */
466
    lodsb  /* AL = DS:[SI++] */
467
    /* is AL 0? if so we're done */
468
    test al, al
469
    jz DONE
470
    /* validate that AL is in range '0'-'9' */
471
    sub al, '0'
430 mateuszvis 472
    jc FAIL   /* invalid character detected */
410 mateuszvis 473
    cmp al, 9
430 mateuszvis 474
    jg FAIL   /* invalid character detected */
410 mateuszvis 475
    /* restore result into AX (CX contains the new digit) */
476
    xchg cx, ax
477
    /* multiply result by 10 and add cl */
478
    mul bx    /* DX AX = AX * BX(10) */
430 mateuszvis 479
    jc OVERFLOW  /* overflow */
410 mateuszvis 480
    add ax, cx
430 mateuszvis 481
    /* if CF is set then overflow occurred (overflow part lands in DX) */
410 mateuszvis 482
    jnc NEXTBYTE
483
 
430 mateuszvis 484
    OVERFLOW:
485
    xor cx, cx  /* make sure result is zeroed in case overflow occured */
486
 
410 mateuszvis 487
    FAIL:
488
    inc [err]
489
 
490
    DONE: /* save result (CX) into indirect memory address r */
491
    mov bx, [r]
492
    mov [bx], cx
493
  }
494
  return(err);
495
}
415 mateuszvis 496
 
497
 
498
/* appends a backslash if path is a directory
499
 * returns the (possibly updated) length of path */
500
unsigned short path_appendbkslash_if_dir(char *path) {
501
  unsigned short len;
502
  int attr;
503
  for (len = 0; path[len] != 0; len++);
504
  if (len == 0) return(0);
505
  if (path[len - 1] == '\\') return(len);
506
  /* */
507
  attr = file_getattr(path);
508
  if ((attr > 0) && (attr & DOS_ATTR_DIR)) {
509
    path[len++] = '\\';
510
    path[len] = 0;
511
  }
512
  return(len);
513
}
416 mateuszvis 514
 
515
 
516
/* get current path drive d (A=1, B=2, etc - 0 is "current drive")
517
 * returns 0 on success, doserr otherwise */
518
unsigned short curpathfordrv(char *buff, unsigned char d) {
519
  unsigned short r = 0;
520
 
521
  _asm {
522
    /* is d == 0? then I need to resolve current drive */
523
    cmp byte ptr [d], 0
524
    jne GETCWD
525
    /* resolve cur drive */
526
    mov ah, 0x19  /* get current default drive */
527
    int 0x21      /* al = drive (00h = A:, 01h = B:, etc) */
528
    inc al        /* convert to 1=A, 2=B, etc */
529
    mov [d], al
530
 
531
    GETCWD:
532
    /* prepend buff with drive:\ */
533
    mov si, buff
534
    mov dl, [d]
535
    mov [si], dl
536
    add byte ptr [si], 'A' - 1
537
    inc si
538
    mov [si], ':'
539
    inc si
540
    mov [si], '\\'
541
    inc si
542
 
543
    mov ah, 0x47      /* get current directory of drv DL into DS:SI */
544
    int 0x21
545
    jnc DONE
546
    mov [r], ax       /* copy result from ax */
547
 
548
    DONE:
549
  }
550
 
551
  return(r);
552
}
420 mateuszvis 553
 
554
 
2213 mateusz.vi 555
/* like strcpy() but returns the string's length */
556
unsigned short sv_strcpy(char *dst, const char *s) {
557
  unsigned short len = 0;
558
  for (;;) {
559
    *dst = *s;
560
    if (*s == 0) return(len);
561
    dst++;
562
    s++;
563
    len++;
564
  }
565
}
566
 
567
 
568
/* like sv_strcpy() but operates on far pointers */
569
unsigned short sv_strcpy_far(char far *dst, const char far *s) {
570
  unsigned short len = 0;
571
  for (;;) {
572
    *dst = *s;
573
    if (*s == 0) return(len);
574
    dst++;
575
    s++;
576
    len++;
577
  }
578
}
579
 
580
 
581
/* like strcat() */
582
void sv_strcat(char *dst, const char *s) {
583
  /* advance dst to end of string */
584
  while (*dst != 0) dst++;
585
  /* append string */
586
  sv_strcpy(dst, s);
587
}
588
 
589
 
590
/* like strcat() but operates on far pointers */
591
void sv_strcat_far(char far *dst, const char far *s) {
592
  /* advance dst to end of string */
593
  while (*dst != 0) dst++;
594
  /* append string */
595
  sv_strcpy_far(dst, s);
596
}
597
 
598
 
420 mateuszvis 599
/* fills a nls_patterns struct with current NLS patterns, returns 0 on success, DOS errcode otherwise */
600
unsigned short nls_getpatterns(struct nls_patterns *p) {
601
  unsigned short r = 0;
602
 
603
  _asm {
604
    mov ax, 0x3800  /* DOS 2+ -- Get Country Info for current country */
605
    mov dx, p       /* DS:DX points to the CountryInfoRec buffer */
606
    int 0x21
607
    jnc DONE
608
    mov [r], ax     /* copy DOS err code to r */
609
    DONE:
610
  }
611
 
612
  return(r);
613
}
614
 
615
 
616
/* computes a formatted date based on NLS patterns found in p
617
 * returns length of result */
618
unsigned short nls_format_date(char *s, unsigned short yr, unsigned char mo, unsigned char dy, const struct nls_patterns *p) {
619
  unsigned short items[3];
620
  /* preset date/month/year in proper order depending on date format */
621
  switch (p->dateformat) {
622
    case 0:  /* USA style: m d y */
623
      items[0] = mo;
624
      items[1] = dy;
625
      items[2] = yr;
626
      break;
627
    case 1:  /* EU style: d m y */
628
      items[0] = dy;
629
      items[1] = mo;
630
      items[2] = yr;
631
      break;
632
    case 2:  /* Japan-style: y m d */
633
    default:
634
      items[0] = yr;
635
      items[1] = mo;
636
      items[2] = dy;
637
      break;
638
  }
639
  /* compute the string */
640
  return(sprintf(s, "%02u%s%02u%s%02u", items[0], p->datesep, items[1], p->datesep, items[2]));
641
}
642
 
643
 
426 mateuszvis 644
/* computes a formatted time based on NLS patterns found in p, sc are ignored if set 0xff
420 mateuszvis 645
 * returns length of result */
426 mateuszvis 646
unsigned short nls_format_time(char *s, unsigned char ho, unsigned char mn, unsigned char sc, const struct nls_patterns *p) {
647
  char ampm = 0;
648
  unsigned short res;
649
 
420 mateuszvis 650
  if (p->timefmt == 0) {
651
    if (ho == 12) {
426 mateuszvis 652
      ampm = 'p';
420 mateuszvis 653
    } else if (ho > 12) {
654
      ho -= 12;
426 mateuszvis 655
      ampm = 'p';
420 mateuszvis 656
    } else { /* ho < 12 */
657
      if (ho == 0) ho = 12;
426 mateuszvis 658
      ampm = 'a';
420 mateuszvis 659
    }
426 mateuszvis 660
    res = sprintf(s, "%2u", ho);
661
  } else {
662
    res = sprintf(s, "%02u", ho);
420 mateuszvis 663
  }
426 mateuszvis 664
 
665
  /* append separator and minutes */
666
  res += sprintf(s + res, "%s%02u", p->timesep, mn);
667
 
668
  /* if seconds provided, append them, too */
669
  if (sc != 0xff) res += sprintf(s + res, "%s%02u", p->timesep, sc);
670
 
671
  /* finally append AM/PM char */
672
  if (ampm != 0) s[res++] = ampm;
673
  s[res] = 0;
674
 
675
  return(res);
420 mateuszvis 676
}
677
 
678
 
679
/* computes a formatted integer number based on NLS patterns found in p
680
 * returns length of result */
423 mateuszvis 681
unsigned short nls_format_number(char *s, unsigned long num, const struct nls_patterns *p) {
682
  unsigned short sl = 0, i;
420 mateuszvis 683
  unsigned char thcount = 0;
684
 
423 mateuszvis 685
  /* write the value (reverse) with thousand separators (if any defined) */
420 mateuszvis 686
  do {
687
    if ((thcount == 3) && (p->thousep[0] != 0)) {
688
      s[sl++] = p->thousep[0];
689
      thcount = 0;
690
    }
691
    s[sl++] = '0' + num % 10;
692
    num /= 10;
693
    thcount++;
694
  } while (num > 0);
695
 
423 mateuszvis 696
  /* terminate the string */
420 mateuszvis 697
  s[sl] = 0;
698
 
423 mateuszvis 699
  /* reverse the string now (has been built in reverse) */
700
  for (i = sl / 2 + (sl & 1); i < sl; i++) {
420 mateuszvis 701
    thcount = s[i];
423 mateuszvis 702
    s[i] = s[sl - (i + 1)];   /* abc'de  if i=3 then ' <-> c */
420 mateuszvis 703
    s[sl - (i + 1)] = thcount;
704
  }
705
 
423 mateuszvis 706
  return(sl);
420 mateuszvis 707
}
437 mateuszvis 708
 
709
 
1137 mateusz.vi 710
/* capitalize an ASCIZ string following country-dependent rules */
711
void nls_strtoup(char *buff) {
712
  unsigned short errcode = 0;
713
  /* requires DOS 4+ */
714
  _asm {
715
    push ax
716
    push dx
717
 
718
    mov ax, 0x6522 /* country-dependent capitalize string (DOS 4+) */
719
    mov dx, buff   /* DS:DX -> string to capitalize */
720
    int 0x21
721
    jnc DONE
722
 
723
    mov errcode, ax /* set errcode on failure */
724
    DONE:
725
 
726
    pop dx
727
    pop ax
728
  }
729
 
2213 mateusz.vi 730
  /* do a naive upcase if DOS has no NLS support */
731
  if (errcode != 0) {
732
    while (*buff) {
733
      if (*buff < 128) { /* apply only to 7bit (low ascii) values */
734
        *buff &= 0xDF;
735
      }
736
      buff++;
737
    }
738
  }
1137 mateusz.vi 739
}
740
 
741
 
1881 mateusz.vi 742
/* reload nls ressources from svarcom.lng into svarlang_mem and rmod */
743
void nls_langreload(char *buff, unsigned short rmodseg) {
1629 mateusz.vi 744
  const char far *dosdir;
968 mateusz.vi 745
  const char far *lang;
965 mateusz.vi 746
  static unsigned short lastlang;
1629 mateusz.vi 747
  unsigned short dosdirlen;
1881 mateusz.vi 748
  unsigned short rmodenvseg = *(unsigned short far *)MK_FP(rmodseg, RMOD_OFFSET_ENVSEG);
749
  unsigned char far *rmodcritmsg = MK_FP(rmodseg, RMOD_OFFSET_CRITMSG);
750
  int i;
437 mateuszvis 751
 
752
  /* look up the LANG env variable, upcase it and copy to lang */
1881 mateusz.vi 753
  lang = env_lookup_val(rmodenvseg, "LANG");
968 mateusz.vi 754
  if ((lang == NULL) || (lang[0] == 0)) return;
2213 mateusz.vi 755
  memcpy_ltr_far(buff, lang, 2);
437 mateuszvis 756
  buff[2] = 0;
757
 
758
  /* check if there is need to reload at all */
2213 mateusz.vi 759
  if (lastlang == *((unsigned short *)buff)) return;
437 mateuszvis 760
 
968 mateusz.vi 761
  buff[4] = 0;
1881 mateusz.vi 762
  dosdir = env_lookup_val(rmodenvseg, "DOSDIR");
1629 mateusz.vi 763
  if (dosdir == NULL) return;
437 mateuszvis 764
 
2213 mateusz.vi 765
  sv_strcpy_far(buff + 4, dosdir);
766
  dosdirlen = sv_strlen(buff + 4);
1629 mateusz.vi 767
  if (buff[4 + dosdirlen - 1] == '\\') dosdirlen--;
2213 mateusz.vi 768
  memcpy_ltr(buff + 4 + dosdirlen, "\\SVARCOM.LNG", 13);
437 mateuszvis 769
 
1629 mateusz.vi 770
  /* try loading %DOSDIR%\SVARCOM.LNG */
771
  if (svarlang_load(buff + 4, buff) != 0) {
1881 mateusz.vi 772
    /* failed! try %DOSDIR%\BIN\SVARCOM.LNG */
2213 mateusz.vi 773
    memcpy_ltr(buff + 4 + dosdirlen, "\\BIN\\SVARCOM.LNG", 17);
1629 mateusz.vi 774
    if (svarlang_load(buff + 4, buff) != 0) return;
775
  }
776
 
2213 mateusz.vi 777
  memcpy_ltr_far(&lastlang, lang, 2);
1881 mateusz.vi 778
 
779
  /* update RMOD's critical handler with new strings */
2161 mateusz.vi 780
  for (i = 0; i < 9; i++) {
1881 mateusz.vi 781
    int len;
2213 mateusz.vi 782
    len = sv_strlen(svarlang_str(3, i));
1881 mateusz.vi 783
    if (len > 15) len = 15;
2213 mateusz.vi 784
    memcpy_ltr_far(rmodcritmsg + (i * 16), svarlang_str(3, i), len);
785
    memcpy_ltr_far(rmodcritmsg + (i * 16) + len, "$", 1);
1881 mateusz.vi 786
  }
787
  /* The ARIF string is special: always 4 bytes long and no $ terminator */
2213 mateusz.vi 788
  memcpy_ltr_far(rmodcritmsg + (9 * 16), svarlang_str(3,9), 4);
437 mateuszvis 789
}
571 mateuszvis 790
 
791
 
792
/* locates executable fname in path and fill res with result. returns 0 on success,
793
 * -1 on failed match and -2 on failed match + "don't even try with other paths"
794
 * extptr is filled with a ptr to the extension in fname (NULL if no extension) */
795
int lookup_cmd(char *res, const char *fname, const char *path, const char **extptr) {
796
  unsigned short lastbslash = 0;
797
  unsigned short i, len;
798
  unsigned char explicitpath = 0;
1072 mateusz.vi 799
  const char *exec_ext[] = {"COM", "EXE", "BAT", NULL};
571 mateuszvis 800
 
801
  /* does the original fname has an explicit path prefix or explicit ext? */
802
  *extptr = NULL;
803
  for (i = 0; fname[i] != 0; i++) {
804
    switch (fname[i]) {
805
      case ':':
806
      case '\\':
807
        explicitpath = 1;
808
        *extptr = NULL; /* extension is the last dot AFTER all path delimiters */
809
        break;
810
      case '.':
811
        *extptr = fname + i + 1;
812
        break;
813
    }
814
  }
815
 
1072 mateusz.vi 816
  /* if explicit ext found, make sure it is executable */
817
  if (*extptr != NULL) {
818
    for (i = 0; exec_ext[i] != NULL; i++) if (imatch(*extptr, exec_ext[i])) break;
819
    if (exec_ext[i] == NULL) return(-2); /* bad extension - don't try running it ever */
820
  }
821
 
571 mateuszvis 822
  /* normalize filename */
823
  if (file_truename(fname, res) != 0) return(-2);
824
 
825
  /* printf("truename: %s\r\n", res); */
826
 
1072 mateusz.vi 827
  /* figure out where the command starts */
571 mateuszvis 828
  for (len = 0; res[len] != 0; len++) {
829
    switch (res[len]) {
830
      case '?':   /* abort on any wildcard character */
831
      case '*':
832
        return(-2);
833
      case '\\':
834
        lastbslash = len;
835
        break;
836
    }
837
  }
838
 
839
  /* printf("lastbslash=%u\r\n", lastbslash); */
840
 
841
  /* if no path prefix was found in fname (no colon or backslash) AND we have
842
   * a path arg, then assemble path+filename */
843
  if ((!explicitpath) && (path != NULL) && (path[0] != 0)) {
2213 mateusz.vi 844
    i = sv_strlen(path);
571 mateuszvis 845
    if (path[i - 1] != '\\') i++; /* add a byte for inserting a bkslash after path */
846
    /* move the filename at the place where path will end */
2213 mateusz.vi 847
    memcpy_rtl(res + i, res + lastbslash + 1, len - lastbslash);
571 mateuszvis 848
    /* copy path in front of the filename and make sure there is a bkslash sep */
2213 mateusz.vi 849
    memcpy_ltr(res, path, i);
571 mateuszvis 850
    res[i - 1] = '\\';
851
  }
852
 
853
  /* if no extension was initially provided, try matching COM, EXE, BAT */
854
  if (*extptr == NULL) {
1072 mateusz.vi 855
    int attr;
2213 mateusz.vi 856
    len = sv_strlen(res);
1072 mateusz.vi 857
    res[len++] = '.';
858
    for (i = 0; exec_ext[i] != NULL; i++) {
2213 mateusz.vi 859
      sv_strcpy(res + len, exec_ext[i]);
571 mateuszvis 860
      /* printf("? '%s'\r\n", res); */
1072 mateusz.vi 861
      *extptr = exec_ext[i];
862
      attr = file_getattr(res);
863
      if (attr < 0) continue; /* file not found */
864
      if (attr & DOS_ATTR_DIR) continue; /* this is a directory */
865
      if (attr & DOS_ATTR_VOL) continue; /* this is a volume */
866
      return(0);
571 mateuszvis 867
    }
868
  } else { /* try finding it as-is */
869
    /* printf("? '%s'\r\n", res); */
1072 mateusz.vi 870
    int attr = file_getattr(res);
871
    if ((attr >= 0) &&  /* file exists */
872
        ((attr & DOS_ATTR_DIR) == 0) && /* is not a directory */
873
        ((attr & DOS_ATTR_VOL) == 0)) { /* is not a volume */
874
      return(0);
875
    }
571 mateuszvis 876
  }
877
 
878
  /* not found */
879
  if (explicitpath) return(-2); /* don't bother trying other paths, the caller had its own path preset anyway */
880
  return(-1);
881
}
882
 
883
 
884
/* fills fname with the path and filename to the linkfile related to the
885
 * executable link "linkname". returns 0 on success. */
886
int link_computefname(char *fname, const char *linkname, unsigned short env_seg) {
1044 mateusz.vi 887
  unsigned short pathlen, doserr = 0;
571 mateuszvis 888
 
889
  /* fetch %DOSDIR% */
890
  pathlen = env_lookup_valcopy(fname, 128, env_seg, "DOSDIR");
1988 mateusz.vi 891
  if (pathlen == 0) return(-1);
571 mateuszvis 892
 
893
  /* prep filename: %DOSDIR%\LINKS\PKG.LNK */
894
  if (fname[pathlen - 1] == '\\') pathlen--;
1043 mateusz.vi 895
  pathlen += sprintf(fname + pathlen, "\\LINKS");
1044 mateusz.vi 896
  /* create \LINKS if not exists */
897
  if (file_getattr(fname) < 0) {
898
    _asm {
899
      push dx
900
      mov ah, 0x39
901
      mov dx, fname
902
      int 0x21
903
      jnc DONE
904
      mov doserr, ax
905
      DONE:
906
      pop dx
907
    }
908
    if (doserr) {
909
      output(fname);
910
      output(" - ");
911
      nls_outputnl(255, doserr);
912
      return(-1);
913
    }
914
  }
915
  /* quit early if dir does not exist (or is not a dir) */
1043 mateusz.vi 916
  if (file_getattr(fname) != DOS_ATTR_DIR) {
917
    output(fname);
918
    output(" - ");
919
    nls_outputnl(255,3); /* path not found */
920
    return(-1);
921
  }
922
  sprintf(fname + pathlen, "\\%s.LNK", linkname);
571 mateuszvis 923
 
924
  return(0);
925
}
2195 mateusz.vi 926
 
927
 
928
/* like memcpy() but guarantees to copy from left to right */
929
void memcpy_ltr(void *d, const void *s, unsigned short len) {
930
  unsigned char const *ss = s;
931
  unsigned char *dd = d;
932
 
933
  while (len--) {
934
    *dd = *ss;
935
    ss++;
936
    dd++;
937
  }
938
}
939
 
2213 mateusz.vi 940
 
941
/* like memcpy_ltr() but operates on far pointers */
942
void memcpy_ltr_far(void far *d, const void far *s, unsigned short len) {
943
  unsigned char const far *ss = s;
944
  unsigned char far *dd = d;
945
 
946
  while (len--) {
947
    *dd = *ss;
948
    ss++;
949
    dd++;
950
  }
951
}
952
 
953
 
2195 mateusz.vi 954
/* like memcpy() but guarantees to copy from right to left */
955
void memcpy_rtl(void *d, const void *s, unsigned short len) {
956
  unsigned char const *ss = s;
957
  unsigned char *dd = d;
958
 
959
  dd += len - 1;
960
  ss += len - 1;
961
  while (len--) {
962
    *dd = *ss;
963
    ss--;
964
    dd--;
965
  }
966
}
2213 mateusz.vi 967
 
968
 
969
/* like bzero(), but accepts far pointers */
970
void sv_bzero(void far *dst, unsigned short len) {
971
  char far *d = dst;
972
  while (len--) {
973
    *d = 0;
974
    d++;
975
  }
976
}
2218 mateusz.vi 977
 
978
 
979
/* like memset() */
980
void sv_memset(void *dst, unsigned char c, unsigned short len) {
981
  unsigned char *d = dst;
982
  while (len--) {
983
    *d = c;
984
    d++;
985
  }
986
}