Subversion Repositories SvarDOS

Rev

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