Subversion Repositories SvarDOS

Rev

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