Subversion Repositories SvarDOS

Rev

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