Subversion Repositories SvarDOS

Rev

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