Subversion Repositories SvarDOS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
585 mateuszvis 1
/*
588 mateuszvis 2
 * Locales configuration for SvarDOS
585 mateuszvis 3
 *
1191 mateusz.vi 4
 * Copyright (C) Mateusz Viste 2015-2023
588 mateuszvis 5
 *
592 mateuszvis 6
 * MIT license
585 mateuszvis 7
 *
592 mateuszvis 8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
9
 * of this software and associated documentation files (the "Software"), to
10
 * deal in the Software without restriction, including without limitation the
11
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12
 * sell copies of the Software, and to permit persons to whom the Software is
13
 * furnished to do so, subject to the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
 * IN THE SOFTWARE.
585 mateuszvis 25
 */
26
 
27
#include <stdio.h>
28
#include <stdlib.h> /* atoi() */
29
#include <string.h> /* strchr */
30
 
1191 mateusz.vi 31
#include "svarlang.h"
603 mateuszvis 32
 
585 mateuszvis 33
#include "country.h"
34
 
1191 mateusz.vi 35
#define PVER "20230208"
36
#define PDATE "2015-2023"
585 mateuszvis 37
 
38
 
603 mateuszvis 39
enum NLS_STRINGS {
40
  NLS_HLP_VER           = 0x0000,
41
  NLS_HLP_DESC          = 0x0001,
42
  NLS_HLP_USAGE         = 0x0002,
43
  NLS_HLP_OPTIONS       = 0x0003,
44
  NLS_HLP_COUNTRY       = 0x000A,
45
  NLS_HLP_CP            = 0x000B,
46
  NLS_HLP_DECIM         = 0x000C,
47
  NLS_HLP_THOUS         = 0x000D,
48
  NLS_HLP_DATESEP       = 0x000E,
49
  NLS_HLP_DATEFMT       = 0x000F,
50
  NLS_HLP_TIMESEP       = 0x0010,
51
  NLS_HLP_TIMEFMT       = 0x0011,
52
  NLS_HLP_CURR          = 0x0012,
53
  NLS_HLP_CURRPOS0      = 0x0013,
54
  NLS_HLP_CURRPOS1      = 0x0014,
55
  NLS_HLP_CURRPOS2      = 0x0015,
56
  NLS_HLP_CURRSPC       = 0x0016,
57
  NLS_HLP_CURRPREC      = 0x0017,
58
  NLS_HLP_YESNO         = 0x0018,
59
  NLS_HLP_INFOLOC1      = 0x0032,
60
  NLS_HLP_INFOLOC2      = 0x0033,
61
 
62
  NLS_INFO_COUNTRY      = 0x0700,
63
  NLS_INFO_CODEPAGE     = 0x0701,
64
  NLS_INFO_DECSEP       = 0x0702,
65
  NLS_INFO_THOUSEP      = 0x0703,
66
  NLS_INFO_DATEFMT      = 0x0704,
67
  NLS_INFO_TIMEFMT      = 0x0705,
68
  NLS_INFO_YESNO        = 0x0706,
69
  NLS_INFO_CURREXAMPLE  = 0x0707,
70
  NLS_MAKESURE          = 0x0709,
71
 
72
  NLS_ERR_FILEPATHTWICE = 0x0900,
73
  NLS_ERR_BADPATH       = 0x0901,
74
  NLS_ERR_READFAIL      = 0x0902,
611 mateuszvis 75
  NLS_ERR_INVPARAM      = 0x0903,
76
  NLS_ERR_INVFORMAT     = 0x0904,
77
  NLS_ERR_NOTLOCALCFG   = 0x0905
603 mateuszvis 78
};
79
 
80
 
606 mateuszvis 81
static void output(const char *s) {
82
  _asm {
83
    /* set cx to strlen(s) */
84
    push ds
85
    pop es
86
    mov di, s
87
    xor al, al
88
    cld
89
    mov cx, 0xff
90
    repne scasb  /* compare ES:DI with AL, inc DI until match */
91
    mov cx, di
92
    sub cx, s
93
    dec cx
94
    /* output via DOS */
95
    mov ah, 0x40  /* write to handle */
96
    mov bx, 1     /* 1=stdout */
97
    mov dx, s
98
    int 0x21
99
  }
603 mateuszvis 100
}
101
 
102
 
606 mateuszvis 103
static void crlf(void) {
104
  output("\r\n");
105
}
106
 
107
 
108
static void outputnl(const char *s) {
109
  output(s);
110
  crlf();
111
}
112
 
113
 
603 mateuszvis 114
static void nls_put(enum NLS_STRINGS id) {
606 mateuszvis 115
  output(svarlang_strid(id));
603 mateuszvis 116
}
117
 
118
 
606 mateuszvis 119
static void nls_puts(enum NLS_STRINGS id) {
120
  nls_put(id);
121
  crlf();
603 mateuszvis 122
}
123
 
124
 
585 mateuszvis 125
static void about(void) {
606 mateuszvis 126
  output("localcfg ");
603 mateuszvis 127
  nls_put(NLS_HLP_VER);
606 mateuszvis 128
  outputnl(" " PVER ", (C) " PDATE " Mateusz Viste");
603 mateuszvis 129
  nls_puts(NLS_HLP_DESC);
130
  crlf();
131
  nls_puts(NLS_HLP_USAGE);
132
  crlf();
133
  nls_puts(NLS_HLP_OPTIONS);
134
  crlf();
135
  nls_puts(NLS_HLP_COUNTRY);
136
  nls_puts(NLS_HLP_CP);
137
  nls_puts(NLS_HLP_DECIM);
138
  nls_puts(NLS_HLP_THOUS);
139
  nls_puts(NLS_HLP_DATESEP);
140
  nls_puts(NLS_HLP_DATEFMT);
141
  nls_puts(NLS_HLP_TIMESEP);
142
  nls_puts(NLS_HLP_TIMEFMT);
143
  nls_puts(NLS_HLP_CURR);
144
  nls_puts(NLS_HLP_CURRPOS0);
145
  nls_puts(NLS_HLP_CURRPOS1);
146
  nls_puts(NLS_HLP_CURRPOS2);
147
  nls_puts(NLS_HLP_CURRSPC);
148
  nls_puts(NLS_HLP_CURRPREC);
149
  nls_puts(NLS_HLP_YESNO);
150
  crlf();
151
  nls_puts(NLS_HLP_INFOLOC1);
152
  nls_puts(NLS_HLP_INFOLOC2);
585 mateuszvis 153
}
154
 
155
 
610 mateuszvis 156
static char *datestring(char *result, struct country *c) {
591 mateuszvis 157
  switch (c->CTYINFO.datefmt) {
585 mateuszvis 158
    case COUNTRY_DATE_MDY:
591 mateuszvis 159
      sprintf(result, "12%c31%c1990", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
585 mateuszvis 160
      break;
161
    case COUNTRY_DATE_DMY:
591 mateuszvis 162
      sprintf(result, "31%c12%c1990", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
585 mateuszvis 163
      break;
164
    case COUNTRY_DATE_YMD:
165
    default:
591 mateuszvis 166
      sprintf(result, "1990%c12%c31", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
585 mateuszvis 167
      break;
168
  }
169
  return(result);
170
}
171
 
172
 
610 mateuszvis 173
static char *timestring(char *result, struct country *c) {
591 mateuszvis 174
  if (c->CTYINFO.timefmt == COUNTRY_TIME12) {
175
    sprintf(result, "11%c59%c59 PM", c->CTYINFO.timesep[0], c->CTYINFO.timesep[0]);
585 mateuszvis 176
  } else {
591 mateuszvis 177
    sprintf(result, "23%c59%c59", c->CTYINFO.timesep[0], c->CTYINFO.timesep[0]);
585 mateuszvis 178
  }
179
  return(result);
180
}
181
 
182
 
610 mateuszvis 183
static char *currencystring(char *result, struct country *c) {
585 mateuszvis 184
  char decimalpart[16];
185
  char space[2] = {0, 0};
186
  char decsym[8];
187
  char cursym[8];
188
  decimalpart[0] = '1';
189
  decimalpart[1] = '2';
190
  decimalpart[2] = '3';
191
  decimalpart[3] = '4';
192
  decimalpart[4] = '5';
193
  decimalpart[5] = '6';
194
  decimalpart[6] = '7';
195
  decimalpart[7] = '8';
196
  decimalpart[8] = '9';
197
  decimalpart[9] = 0;
198
  /* prepare the decimal string first */
591 mateuszvis 199
  if (c->CTYINFO.currprec < 9) {
200
    decimalpart[c->CTYINFO.currprec] = 0;
585 mateuszvis 201
  }
202
  /* prepare the currency space string */
591 mateuszvis 203
  if (c->CTYINFO.currspace != 0) {
585 mateuszvis 204
    space[0] = ' ';
205
  }
206
  /* prepare the currency and decimal symbols */
591 mateuszvis 207
  if (c->CTYINFO.currdecsym != 0) { /* currency replaces the decimal point */
208
    sprintf(decsym, "%s", c->CTYINFO.currsym);
585 mateuszvis 209
    cursym[0] = 0;
210
  } else {
591 mateuszvis 211
    sprintf(decsym, "%c", c->CTYINFO.decimal[0]);
212
    sprintf(cursym, "%s", c->CTYINFO.currsym);
585 mateuszvis 213
  }
591 mateuszvis 214
  if (c->CTYINFO.currprec == 0) decsym[0] = 0;
585 mateuszvis 215
  /* compute the final string */
591 mateuszvis 216
  if (c->CTYINFO.currpos == 0) { /* currency precedes value */
585 mateuszvis 217
    sprintf(result, "%s%s99%s%s", cursym, space, decsym, decimalpart);
218
  } else { /* currency follows value or replaces decimal symbol */
219
    sprintf(result, "99%s%s%s%s", decsym, decimalpart, space, cursym);
220
  }
221
  return(result);
222
}
223
 
224
 
225
/* checks if str starts with prefix. returns 0 if so, non-zero otherwise. */
226
static int stringstartswith(char *str, char *prefix) {
227
  for (;;) {
228
    /* end of prefix means success */
229
    if (*prefix == 0) return(0);
230
    /* otherwise there is no match */
231
    if (*str != *prefix) return(-1);
232
    /* if match good so far, look at next char */
233
    str += 1;
234
    prefix += 1;
235
  }
236
}
237
 
238
 
239
/* processes an argument. returns 0 on success, non-zero otherwise. */
240
static int processarg(char *arg, struct country *c) {
241
  char *value;
242
  int intvalue;
243
  /* an option must start with a '/' */
244
  if (arg[0] != '/') return(-1);
245
  arg += 1; /* skip the slash */
246
  /* find where the value starts */
247
  value = strchr(arg, ':');
248
  /* if no value present, fail */
249
  if (value == NULL) return(-2);
250
  value += 1;
251
  if (*value == 0) return(-3);
252
  /* interpret the option now */
253
  if (stringstartswith(arg, "country:") == 0) {
254
    intvalue = atoi(value);
255
    if ((intvalue > 0) && (intvalue < 1000)) {
591 mateuszvis 256
      c->CTYINFO.id = intvalue;
585 mateuszvis 257
      return(0);
258
    }
259
  } else if (stringstartswith(arg, "cp:") == 0) {
260
    intvalue = atoi(value);
261
    if ((intvalue > 0) && (intvalue < 1000)) {
591 mateuszvis 262
      c->CTYINFO.codepage = intvalue;
585 mateuszvis 263
      return(0);
264
    }
265
  } else if (stringstartswith(arg, "decim:") == 0) {
266
    if (value[1] == 0) { /* value must be exactly one character */
591 mateuszvis 267
      c->CTYINFO.decimal[0] = *value;
585 mateuszvis 268
      return(0);
269
    }
270
  } else if (stringstartswith(arg, "thous:") == 0) {
271
    if (value[1] == 0) { /* value must be exactly one character */
591 mateuszvis 272
      c->CTYINFO.thousands[0] = *value;
585 mateuszvis 273
      return(0);
274
    }
275
  } else if (stringstartswith(arg, "datesep:") == 0) {
276
    if (value[1] == 0) { /* value must be exactly one character */
591 mateuszvis 277
      c->CTYINFO.datesep[0] = *value;
585 mateuszvis 278
      return(0);
279
    }
280
  } else if (stringstartswith(arg, "timesep:") == 0) {
281
    if (value[1] == 0) { /* value must be exactly one character */
591 mateuszvis 282
      c->CTYINFO.timesep[0] = *value;
585 mateuszvis 283
      return(0);
284
    }
285
  } else if (stringstartswith(arg, "datefmt:") == 0) {
286
    if (strcmp(value, "MDY") == 0) {
591 mateuszvis 287
      c->CTYINFO.datefmt = COUNTRY_DATE_MDY;
585 mateuszvis 288
      return(0);
289
    } else if (strcmp(value, "DMY") == 0) {
591 mateuszvis 290
      c->CTYINFO.datefmt = COUNTRY_DATE_DMY;
585 mateuszvis 291
      return(0);
292
    } else if (strcmp(value, "YMD") == 0) {
591 mateuszvis 293
      c->CTYINFO.datefmt = COUNTRY_DATE_YMD;
585 mateuszvis 294
      return(0);
295
    }
296
  } else if (stringstartswith(arg, "timefmt:") == 0) {
297
    if (value[1] == 0) {
298
      if ((value[0] >= '0') && (value[0] <= '1')) {
591 mateuszvis 299
        c->CTYINFO.timefmt = value[0] - '0';
585 mateuszvis 300
        return(0);
301
      }
302
    }
303
  } else if (stringstartswith(arg, "curr:") == 0) {
304
    if (strlen(value) <= 4) {
591 mateuszvis 305
      strcpy(c->CTYINFO.currsym, value);
585 mateuszvis 306
      return(0);
307
    }
308
  } else if (stringstartswith(arg, "currpos:") == 0) {
309
    if (value[1] == 0) {
310
      if (value[0] == '0') {
591 mateuszvis 311
        c->CTYINFO.currpos = 0;
585 mateuszvis 312
        return(0);
313
      } else if (value[0] == '1') {
591 mateuszvis 314
        c->CTYINFO.currpos = 1;
585 mateuszvis 315
        return(0);
316
      } else if (value[0] == '2') {
591 mateuszvis 317
        c->CTYINFO.currpos = 0;
318
        c->CTYINFO.currdecsym = 1;
585 mateuszvis 319
        return(0);
320
      }
321
    }
322
  } else if (stringstartswith(arg, "currspc:") == 0) {
323
    if (value[1] == 0) {
324
      if ((value[0] >= '0') && (value[0] <= '1')) {
591 mateuszvis 325
        c->CTYINFO.currspace = value[0] - '0';
585 mateuszvis 326
        return(0);
327
      }
328
    }
329
  } else if (stringstartswith(arg, "currprec:") == 0) {
330
    if (value[1] == 0) {
331
      if ((value[0] >= '0') && (value[0] <= '9')) {
591 mateuszvis 332
        c->CTYINFO.currprec = value[0] - '0';
585 mateuszvis 333
        return(0);
334
      }
335
    }
336
  } else if (stringstartswith(arg, "yesno:") == 0) {
337
    /* string must be exactly 2 characters long */
338
    if ((value[0] != 0) && (value[1] != 0) && (value[2] == 0)) {
591 mateuszvis 339
      c->YESNO.yes[0] = value[0];
340
      c->YESNO.no[0] = value[1];
585 mateuszvis 341
      return(0);
342
    }
343
  }
344
  /* if I'm here, something went wrong */
345
  return(-4);
346
}
347
 
348
 
588 mateuszvis 349
/* converts a path to its canonic representation, returns 0 on success
350
 * or DOS err on failure (invalid drive) */
351
static unsigned short file_truename(const char *dst, char *src) {
352
  unsigned short res = 0;
353
  _asm {
354
    push es
355
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
356
    push ds
357
    pop es
358
    mov si, src
359
    mov di, dst
360
    int 0x21
361
    jnc DONE
362
    mov [res], ax
363
    DONE:
364
    pop es
365
  }
366
  return(res);
367
}
368
 
369
 
593 mateuszvis 370
static void default_country_path(char *s) {
371
  char *dosdir = getenv("DOSDIR");
372
  size_t dosdirlen;
373
  s[0] = 0;
374
  if (dosdir == NULL) return;
375
  dosdirlen = strlen(dosdir);
376
  if (dosdirlen == 0) return;
377
  /* drop trailing backslash if present */
378
  if (dosdir[dosdirlen - 1] == '\\') dosdirlen--;
379
  /* copy dosdir to s and append the rest of the path */
380
  memcpy(s, dosdir, dosdirlen);
381
  strcpy(s + dosdirlen, "\\CFG\\COUNTRY.SYS");
382
}
383
 
384
 
585 mateuszvis 385
int main(int argc, char **argv) {
386
  struct country cntdata;
387
  int changedflag;
388
  int x;
610 mateuszvis 389
  static char fname[130];
390
  static char buff[64];
585 mateuszvis 391
 
603 mateuszvis 392
  svarlang_autoload("localcfg");
393
 
593 mateuszvis 394
  /* scan argv looking for the path to country.sys */
395
  for (x = 1; x < argc; x++) {
396
    if (argv[x][0] != '/') {
397
      if (fname[0] != 0) {
603 mateuszvis 398
        nls_puts(NLS_ERR_FILEPATHTWICE);
593 mateuszvis 399
        return(1);
400
      }
401
      /* */
402
      if (file_truename(fname, argv[x]) != 0) {
603 mateuszvis 403
        nls_puts(NLS_ERR_BADPATH);
593 mateuszvis 404
        return(1);
405
      }
406
    } else if (strcmp(argv[x], "/?") == 0) { /* is it /? */
407
      about();
408
      return(1);
409
    }
585 mateuszvis 410
  }
411
 
593 mateuszvis 412
  /* if no file path provided, look into %DOSDIR%\CFG\COUNTRY.SYS */
413
  if (fname[0] == 0) default_country_path(fname);
585 mateuszvis 414
 
415
  x = country_read(&cntdata, fname);
416
  if (x != 0) {
611 mateuszvis 417
    if (x == COUNTRY_ERR_INV_FORMAT) {
418
      nls_puts(NLS_ERR_INVFORMAT);
419
    } else if (x == COUNTRY_ERR_NOT_LOCALCFG) {
420
      nls_puts(NLS_ERR_NOTLOCALCFG);
421
    } else {
422
      nls_puts(NLS_ERR_READFAIL);
423
    }
585 mateuszvis 424
    return(2);
425
  }
426
 
593 mateuszvis 427
  changedflag = 0;
585 mateuszvis 428
 
429
  /* process command line arguments */
593 mateuszvis 430
  for (x = 1; x < argc; x++) {
431
    if (argv[x][0] != '/') continue; /* skip country.sys filename (processed earlier) */
432
    changedflag++;
433
    if (processarg(argv[x], &cntdata) != 0) {
603 mateuszvis 434
      nls_puts(NLS_ERR_INVPARAM);
585 mateuszvis 435
      return(3);
436
    }
437
  }
438
 
603 mateuszvis 439
  nls_put(NLS_INFO_COUNTRY);
606 mateuszvis 440
  sprintf(buff, " %03d", cntdata.CTYINFO.id);
441
  outputnl(buff);
603 mateuszvis 442
  nls_put(NLS_INFO_CODEPAGE);
606 mateuszvis 443
  sprintf(buff, " %d", cntdata.CTYINFO.codepage);
444
  outputnl(buff);
603 mateuszvis 445
  nls_put(NLS_INFO_DECSEP);
606 mateuszvis 446
  sprintf(buff, " %c", cntdata.CTYINFO.decimal[0]);
447
  outputnl(buff);
603 mateuszvis 448
  nls_put(NLS_INFO_THOUSEP);
606 mateuszvis 449
  sprintf(buff, " %c", cntdata.CTYINFO.thousands[0]);
450
  outputnl(buff);
603 mateuszvis 451
  nls_put(NLS_INFO_DATEFMT);
610 mateuszvis 452
  output(" ");
453
  outputnl(datestring(buff, &cntdata));
603 mateuszvis 454
  nls_put(NLS_INFO_TIMEFMT);
610 mateuszvis 455
  output(" ");
456
  outputnl(timestring(buff, &cntdata));
603 mateuszvis 457
  nls_put(NLS_INFO_YESNO);
606 mateuszvis 458
  sprintf(buff, " %c/%c", cntdata.YESNO.yes[0], cntdata.YESNO.no[0]);
459
  outputnl(buff);
603 mateuszvis 460
  nls_put(NLS_INFO_CURREXAMPLE);
610 mateuszvis 461
  output(" ");
462
  outputnl(currencystring(buff, &cntdata));
585 mateuszvis 463
 
603 mateuszvis 464
  crlf();
465
  nls_puts(NLS_MAKESURE);
610 mateuszvis 466
  sprintf(buff, "COUNTRY=%03d,%03d,", cntdata.CTYINFO.id, cntdata.CTYINFO.codepage);
467
  output(buff);
468
  outputnl(fname);
585 mateuszvis 469
 
470
  /* if anything changed, write the new file */
471
  if (changedflag != 0) country_write(fname, &cntdata);
472
 
473
  return(0);
474
}