Subversion Repositories SvarDOS

Rev

Rev 592 | Rev 603 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
585 mateuszvis 1
/*
588 mateuszvis 2
 * Locales configuration for SvarDOS
585 mateuszvis 3
 *
588 mateuszvis 4
 * Copyright (C) Mateusz Viste 2015-2022
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
 
31
#include "country.h"
32
 
591 mateuszvis 33
#define PVER "20220202"
588 mateuszvis 34
#define PDATE "2015-2022"
585 mateuszvis 35
 
36
 
37
static void about(void) {
591 mateuszvis 38
  puts("localcfg ver " PVER " - locales configuration for DOS\n"
593 mateuszvis 39
       "Copyright (C) " PDATE " Mateusz Viste\n"
585 mateuszvis 40
       "\n"
593 mateuszvis 41
       "localcfg creates or edits a custom COUNTRY.SYS-like file with your preferences.\n"
585 mateuszvis 42
       "\n"
593 mateuszvis 43
       "usage: localcfg [COUNTRY.SYS] [options]\n"
585 mateuszvis 44
       "\n"
45
       "options allow to configure country locales to your likening, as follows:\n"
46
       "  /country:XX indicates your country code is XX (1 for USA, 33 for France, etc)\n"
47
       "  /cp:XXX     adapts country data for codepage XXX (example: '437')\n"
48
       "  /decim:X    reconfigures the decimal symbol to be 'X'");
49
  puts("  /thous:X    reconfigures the thousands symbol to be 'X'\n"
50
       "  /datesep:X  sets the date separator to 'X' (for example '/')\n"
51
       "  /datefmt:X  sets the date format, can be: MDY, DMY or YMD\n"
52
       "  /timesep:X  sets the time separator to 'X' (for example ':')\n"
53
       "  /timefmt:X  sets the time format: 0=12h with AM/PM or 1=24h\n"
54
       "  /curr:XXX   sets the currency to XXX (a string of 1 to 4 characters)\n"
55
       "  /currpos:X  sets the currency symbol position to X, where X is either");
56
  puts("              0=currency precedes the value, 1=currency follows the value and\n"
57
       "              2=currency replaces the decimal sign\n"
58
       "  /currspc:X  space between the currency and the value (0=no, 1=yes)\n"
59
       "  /currprec:X currency's precision (number of decimal digits, 0..9)\n"
60
       "  /yesno:XY   sets the 'Yes/No' letter to XY (default: YN)\n"
593 mateuszvis 61
       "\n"
62
       "If COUNTRY.SYS location is not provided, then localcfg tries loading it\n"
63
       "from %DOSDIR%\\CFG\\COUNTRY.SYS\n"
585 mateuszvis 64
      );
65
}
66
 
67
 
68
static char *datestring(struct country *c) {
69
  static char result[16];
591 mateuszvis 70
  switch (c->CTYINFO.datefmt) {
585 mateuszvis 71
    case COUNTRY_DATE_MDY:
591 mateuszvis 72
      sprintf(result, "12%c31%c1990", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
585 mateuszvis 73
      break;
74
    case COUNTRY_DATE_DMY:
591 mateuszvis 75
      sprintf(result, "31%c12%c1990", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
585 mateuszvis 76
      break;
77
    case COUNTRY_DATE_YMD:
78
    default:
591 mateuszvis 79
      sprintf(result, "1990%c12%c31", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
585 mateuszvis 80
      break;
81
  }
82
  return(result);
83
}
84
 
85
 
86
static char *timestring(struct country *c) {
87
  static char result[16];
591 mateuszvis 88
  if (c->CTYINFO.timefmt == COUNTRY_TIME12) {
89
    sprintf(result, "11%c59%c59 PM", c->CTYINFO.timesep[0], c->CTYINFO.timesep[0]);
585 mateuszvis 90
  } else {
591 mateuszvis 91
    sprintf(result, "23%c59%c59", c->CTYINFO.timesep[0], c->CTYINFO.timesep[0]);
585 mateuszvis 92
  }
93
  return(result);
94
}
95
 
96
 
97
static char *currencystring(struct country *c) {
98
  static char result[16];
99
  char decimalpart[16];
100
  char space[2] = {0, 0};
101
  char decsym[8];
102
  char cursym[8];
103
  decimalpart[0] = '1';
104
  decimalpart[1] = '2';
105
  decimalpart[2] = '3';
106
  decimalpart[3] = '4';
107
  decimalpart[4] = '5';
108
  decimalpart[5] = '6';
109
  decimalpart[6] = '7';
110
  decimalpart[7] = '8';
111
  decimalpart[8] = '9';
112
  decimalpart[9] = 0;
113
  /* prepare the decimal string first */
591 mateuszvis 114
  if (c->CTYINFO.currprec < 9) {
115
    decimalpart[c->CTYINFO.currprec] = 0;
585 mateuszvis 116
  }
117
  /* prepare the currency space string */
591 mateuszvis 118
  if (c->CTYINFO.currspace != 0) {
585 mateuszvis 119
    space[0] = ' ';
120
  }
121
  /* prepare the currency and decimal symbols */
591 mateuszvis 122
  if (c->CTYINFO.currdecsym != 0) { /* currency replaces the decimal point */
123
    sprintf(decsym, "%s", c->CTYINFO.currsym);
585 mateuszvis 124
    cursym[0] = 0;
125
  } else {
591 mateuszvis 126
    sprintf(decsym, "%c", c->CTYINFO.decimal[0]);
127
    sprintf(cursym, "%s", c->CTYINFO.currsym);
585 mateuszvis 128
  }
591 mateuszvis 129
  if (c->CTYINFO.currprec == 0) decsym[0] = 0;
585 mateuszvis 130
  /* compute the final string */
591 mateuszvis 131
  if (c->CTYINFO.currpos == 0) { /* currency precedes value */
585 mateuszvis 132
    sprintf(result, "%s%s99%s%s", cursym, space, decsym, decimalpart);
133
  } else { /* currency follows value or replaces decimal symbol */
134
    sprintf(result, "99%s%s%s%s", decsym, decimalpart, space, cursym);
135
  }
136
  return(result);
137
}
138
 
139
 
140
/* checks if str starts with prefix. returns 0 if so, non-zero otherwise. */
141
static int stringstartswith(char *str, char *prefix) {
142
  for (;;) {
143
    /* end of prefix means success */
144
    if (*prefix == 0) return(0);
145
    /* otherwise there is no match */
146
    if (*str != *prefix) return(-1);
147
    /* if match good so far, look at next char */
148
    str += 1;
149
    prefix += 1;
150
  }
151
}
152
 
153
 
154
/* processes an argument. returns 0 on success, non-zero otherwise. */
155
static int processarg(char *arg, struct country *c) {
156
  char *value;
157
  int intvalue;
158
  /* an option must start with a '/' */
159
  if (arg[0] != '/') return(-1);
160
  arg += 1; /* skip the slash */
161
  /* find where the value starts */
162
  value = strchr(arg, ':');
163
  /* if no value present, fail */
164
  if (value == NULL) return(-2);
165
  value += 1;
166
  if (*value == 0) return(-3);
167
  /* interpret the option now */
168
  if (stringstartswith(arg, "country:") == 0) {
169
    intvalue = atoi(value);
170
    if ((intvalue > 0) && (intvalue < 1000)) {
591 mateuszvis 171
      c->CTYINFO.id = intvalue;
585 mateuszvis 172
      return(0);
173
    }
174
  } else if (stringstartswith(arg, "cp:") == 0) {
175
    intvalue = atoi(value);
176
    if ((intvalue > 0) && (intvalue < 1000)) {
591 mateuszvis 177
      c->CTYINFO.codepage = intvalue;
585 mateuszvis 178
      return(0);
179
    }
180
  } else if (stringstartswith(arg, "decim:") == 0) {
181
    if (value[1] == 0) { /* value must be exactly one character */
591 mateuszvis 182
      c->CTYINFO.decimal[0] = *value;
585 mateuszvis 183
      return(0);
184
    }
185
  } else if (stringstartswith(arg, "thous:") == 0) {
186
    if (value[1] == 0) { /* value must be exactly one character */
591 mateuszvis 187
      c->CTYINFO.thousands[0] = *value;
585 mateuszvis 188
      return(0);
189
    }
190
  } else if (stringstartswith(arg, "datesep:") == 0) {
191
    if (value[1] == 0) { /* value must be exactly one character */
591 mateuszvis 192
      c->CTYINFO.datesep[0] = *value;
585 mateuszvis 193
      return(0);
194
    }
195
  } else if (stringstartswith(arg, "timesep:") == 0) {
196
    if (value[1] == 0) { /* value must be exactly one character */
591 mateuszvis 197
      c->CTYINFO.timesep[0] = *value;
585 mateuszvis 198
      return(0);
199
    }
200
  } else if (stringstartswith(arg, "datefmt:") == 0) {
201
    if (strcmp(value, "MDY") == 0) {
591 mateuszvis 202
      c->CTYINFO.datefmt = COUNTRY_DATE_MDY;
585 mateuszvis 203
      return(0);
204
    } else if (strcmp(value, "DMY") == 0) {
591 mateuszvis 205
      c->CTYINFO.datefmt = COUNTRY_DATE_DMY;
585 mateuszvis 206
      return(0);
207
    } else if (strcmp(value, "YMD") == 0) {
591 mateuszvis 208
      c->CTYINFO.datefmt = COUNTRY_DATE_YMD;
585 mateuszvis 209
      return(0);
210
    }
211
  } else if (stringstartswith(arg, "timefmt:") == 0) {
212
    if (value[1] == 0) {
213
      if ((value[0] >= '0') && (value[0] <= '1')) {
591 mateuszvis 214
        c->CTYINFO.timefmt = value[0] - '0';
585 mateuszvis 215
        return(0);
216
      }
217
    }
218
  } else if (stringstartswith(arg, "curr:") == 0) {
219
    if (strlen(value) <= 4) {
591 mateuszvis 220
      strcpy(c->CTYINFO.currsym, value);
585 mateuszvis 221
      return(0);
222
    }
223
  } else if (stringstartswith(arg, "currpos:") == 0) {
224
    if (value[1] == 0) {
225
      if (value[0] == '0') {
591 mateuszvis 226
        c->CTYINFO.currpos = 0;
585 mateuszvis 227
        return(0);
228
      } else if (value[0] == '1') {
591 mateuszvis 229
        c->CTYINFO.currpos = 1;
585 mateuszvis 230
        return(0);
231
      } else if (value[0] == '2') {
591 mateuszvis 232
        c->CTYINFO.currpos = 0;
233
        c->CTYINFO.currdecsym = 1;
585 mateuszvis 234
        return(0);
235
      }
236
    }
237
  } else if (stringstartswith(arg, "currspc:") == 0) {
238
    if (value[1] == 0) {
239
      if ((value[0] >= '0') && (value[0] <= '1')) {
591 mateuszvis 240
        c->CTYINFO.currspace = value[0] - '0';
585 mateuszvis 241
        return(0);
242
      }
243
    }
244
  } else if (stringstartswith(arg, "currprec:") == 0) {
245
    if (value[1] == 0) {
246
      if ((value[0] >= '0') && (value[0] <= '9')) {
591 mateuszvis 247
        c->CTYINFO.currprec = value[0] - '0';
585 mateuszvis 248
        return(0);
249
      }
250
    }
251
  } else if (stringstartswith(arg, "yesno:") == 0) {
252
    /* string must be exactly 2 characters long */
253
    if ((value[0] != 0) && (value[1] != 0) && (value[2] == 0)) {
591 mateuszvis 254
      c->YESNO.yes[0] = value[0];
255
      c->YESNO.no[0] = value[1];
585 mateuszvis 256
      return(0);
257
    }
258
  }
259
  /* if I'm here, something went wrong */
260
  return(-4);
261
}
262
 
263
 
588 mateuszvis 264
/* converts a path to its canonic representation, returns 0 on success
265
 * or DOS err on failure (invalid drive) */
266
static unsigned short file_truename(const char *dst, char *src) {
267
  unsigned short res = 0;
268
  _asm {
269
    push es
270
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
271
    push ds
272
    pop es
273
    mov si, src
274
    mov di, dst
275
    int 0x21
276
    jnc DONE
277
    mov [res], ax
278
    DONE:
279
    pop es
280
  }
281
  return(res);
282
}
283
 
284
 
593 mateuszvis 285
static void default_country_path(char *s) {
286
  char *dosdir = getenv("DOSDIR");
287
  size_t dosdirlen;
288
  s[0] = 0;
289
  if (dosdir == NULL) return;
290
  dosdirlen = strlen(dosdir);
291
  if (dosdirlen == 0) return;
292
  /* drop trailing backslash if present */
293
  if (dosdir[dosdirlen - 1] == '\\') dosdirlen--;
294
  /* copy dosdir to s and append the rest of the path */
295
  memcpy(s, dosdir, dosdirlen);
296
  strcpy(s + dosdirlen, "\\CFG\\COUNTRY.SYS");
297
}
298
 
299
 
585 mateuszvis 300
int main(int argc, char **argv) {
301
  struct country cntdata;
302
  int changedflag;
303
  int x;
588 mateuszvis 304
  static char fname[130];
585 mateuszvis 305
 
593 mateuszvis 306
  /* scan argv looking for the path to country.sys */
307
  for (x = 1; x < argc; x++) {
308
    if (argv[x][0] != '/') {
309
      if (fname[0] != 0) {
310
        puts("ERROR: file path can be provided only once");
311
        return(1);
312
      }
313
      /* */
314
      if (file_truename(fname, argv[x]) != 0) {
315
        puts("ERROR: bad file path");
316
        return(1);
317
      }
318
    } else if (strcmp(argv[x], "/?") == 0) { /* is it /? */
319
      about();
320
      return(1);
321
    }
585 mateuszvis 322
  }
323
 
593 mateuszvis 324
  /* if no file path provided, look into %DOSDIR%\CFG\COUNTRY.SYS */
325
  if (fname[0] == 0) default_country_path(fname);
585 mateuszvis 326
 
327
  x = country_read(&cntdata, fname);
328
  if (x != 0) {
329
    printf("ERROR: failed to read the preference file [%d]\n", x);
330
    return(2);
331
  }
332
 
593 mateuszvis 333
  changedflag = 0;
585 mateuszvis 334
 
335
  /* process command line arguments */
593 mateuszvis 336
  for (x = 1; x < argc; x++) {
337
    if (argv[x][0] != '/') continue; /* skip country.sys filename (processed earlier) */
338
    changedflag++;
339
    if (processarg(argv[x], &cntdata) != 0) {
340
      puts("ERROR: invalid parameter syntax");
585 mateuszvis 341
      return(3);
342
    }
343
  }
344
 
591 mateuszvis 345
  printf("Country intl code.....: %03d\n", cntdata.CTYINFO.id);
346
  printf("Codepage..............: %d\n", cntdata.CTYINFO.codepage);
347
  printf("Decimal separator.....: %c\n", cntdata.CTYINFO.decimal[0]);
348
  printf("Thousands separator...: %c\n", cntdata.CTYINFO.thousands[0]);
585 mateuszvis 349
  printf("Date format...........: %s\n", datestring(&cntdata));
350
  printf("Time format...........: %s\n", timestring(&cntdata));
591 mateuszvis 351
  printf("Yes/No letters........: %c/%c\n", cntdata.YESNO.yes[0], cntdata.YESNO.no[0]);
585 mateuszvis 352
  printf("Currency example......: %s\n", currencystring(&cntdata));
353
 
354
  printf("\n"
593 mateuszvis 355
         "Make sure that your CONFIG.SYS contains this directive:\n"
591 mateuszvis 356
         "COUNTRY=%03d,%03d,%s\n\n", cntdata.CTYINFO.id, cntdata.CTYINFO.codepage, fname);
585 mateuszvis 357
 
358
  /* if anything changed, write the new file */
359
  if (changedflag != 0) country_write(fname, &cntdata);
360
 
361
  return(0);
362
}