Subversion Repositories SvarDOS

Rev

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