Subversion Repositories SvarDOS

Rev

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