Subversion Repositories SvarDOS

Rev

Rev 591 | Rev 593 | 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"
39
       "Copyright (C) Mateusz Viste " PDATE "\n"
585 mateuszvis 40
       "\n"
41
       "localcfg creates a custom COUNTRY.SYS-like file matching your preferences.\n"
42
       "\n"
43
       "usage: localcfg myprefs.sys [options]\n"
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"
61
      );
62
}
63
 
64
 
65
static char *datestring(struct country *c) {
66
  static char result[16];
591 mateuszvis 67
  switch (c->CTYINFO.datefmt) {
585 mateuszvis 68
    case COUNTRY_DATE_MDY:
591 mateuszvis 69
      sprintf(result, "12%c31%c1990", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
585 mateuszvis 70
      break;
71
    case COUNTRY_DATE_DMY:
591 mateuszvis 72
      sprintf(result, "31%c12%c1990", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
585 mateuszvis 73
      break;
74
    case COUNTRY_DATE_YMD:
75
    default:
591 mateuszvis 76
      sprintf(result, "1990%c12%c31", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
585 mateuszvis 77
      break;
78
  }
79
  return(result);
80
}
81
 
82
 
83
static char *timestring(struct country *c) {
84
  static char result[16];
591 mateuszvis 85
  if (c->CTYINFO.timefmt == COUNTRY_TIME12) {
86
    sprintf(result, "11%c59%c59 PM", c->CTYINFO.timesep[0], c->CTYINFO.timesep[0]);
585 mateuszvis 87
  } else {
591 mateuszvis 88
    sprintf(result, "23%c59%c59", c->CTYINFO.timesep[0], c->CTYINFO.timesep[0]);
585 mateuszvis 89
  }
90
  return(result);
91
}
92
 
93
 
94
static char *currencystring(struct country *c) {
95
  static char result[16];
96
  char decimalpart[16];
97
  char space[2] = {0, 0};
98
  char decsym[8];
99
  char cursym[8];
100
  decimalpart[0] = '1';
101
  decimalpart[1] = '2';
102
  decimalpart[2] = '3';
103
  decimalpart[3] = '4';
104
  decimalpart[4] = '5';
105
  decimalpart[5] = '6';
106
  decimalpart[6] = '7';
107
  decimalpart[7] = '8';
108
  decimalpart[8] = '9';
109
  decimalpart[9] = 0;
110
  /* prepare the decimal string first */
591 mateuszvis 111
  if (c->CTYINFO.currprec < 9) {
112
    decimalpart[c->CTYINFO.currprec] = 0;
585 mateuszvis 113
  }
114
  /* prepare the currency space string */
591 mateuszvis 115
  if (c->CTYINFO.currspace != 0) {
585 mateuszvis 116
    space[0] = ' ';
117
  }
118
  /* prepare the currency and decimal symbols */
591 mateuszvis 119
  if (c->CTYINFO.currdecsym != 0) { /* currency replaces the decimal point */
120
    sprintf(decsym, "%s", c->CTYINFO.currsym);
585 mateuszvis 121
    cursym[0] = 0;
122
  } else {
591 mateuszvis 123
    sprintf(decsym, "%c", c->CTYINFO.decimal[0]);
124
    sprintf(cursym, "%s", c->CTYINFO.currsym);
585 mateuszvis 125
  }
591 mateuszvis 126
  if (c->CTYINFO.currprec == 0) decsym[0] = 0;
585 mateuszvis 127
  /* compute the final string */
591 mateuszvis 128
  if (c->CTYINFO.currpos == 0) { /* currency precedes value */
585 mateuszvis 129
    sprintf(result, "%s%s99%s%s", cursym, space, decsym, decimalpart);
130
  } else { /* currency follows value or replaces decimal symbol */
131
    sprintf(result, "99%s%s%s%s", decsym, decimalpart, space, cursym);
132
  }
133
  return(result);
134
}
135
 
136
 
137
/* checks if str starts with prefix. returns 0 if so, non-zero otherwise. */
138
static int stringstartswith(char *str, char *prefix) {
139
  for (;;) {
140
    /* end of prefix means success */
141
    if (*prefix == 0) return(0);
142
    /* otherwise there is no match */
143
    if (*str != *prefix) return(-1);
144
    /* if match good so far, look at next char */
145
    str += 1;
146
    prefix += 1;
147
  }
148
}
149
 
150
 
151
/* processes an argument. returns 0 on success, non-zero otherwise. */
152
static int processarg(char *arg, struct country *c) {
153
  char *value;
154
  int intvalue;
155
  /* an option must start with a '/' */
156
  if (arg[0] != '/') return(-1);
157
  arg += 1; /* skip the slash */
158
  /* find where the value starts */
159
  value = strchr(arg, ':');
160
  /* if no value present, fail */
161
  if (value == NULL) return(-2);
162
  value += 1;
163
  if (*value == 0) return(-3);
164
  /* interpret the option now */
165
  if (stringstartswith(arg, "country:") == 0) {
166
    intvalue = atoi(value);
167
    if ((intvalue > 0) && (intvalue < 1000)) {
591 mateuszvis 168
      c->CTYINFO.id = intvalue;
585 mateuszvis 169
      return(0);
170
    }
171
  } else if (stringstartswith(arg, "cp:") == 0) {
172
    intvalue = atoi(value);
173
    if ((intvalue > 0) && (intvalue < 1000)) {
591 mateuszvis 174
      c->CTYINFO.codepage = intvalue;
585 mateuszvis 175
      return(0);
176
    }
177
  } else if (stringstartswith(arg, "decim:") == 0) {
178
    if (value[1] == 0) { /* value must be exactly one character */
591 mateuszvis 179
      c->CTYINFO.decimal[0] = *value;
585 mateuszvis 180
      return(0);
181
    }
182
  } else if (stringstartswith(arg, "thous:") == 0) {
183
    if (value[1] == 0) { /* value must be exactly one character */
591 mateuszvis 184
      c->CTYINFO.thousands[0] = *value;
585 mateuszvis 185
      return(0);
186
    }
187
  } else if (stringstartswith(arg, "datesep:") == 0) {
188
    if (value[1] == 0) { /* value must be exactly one character */
591 mateuszvis 189
      c->CTYINFO.datesep[0] = *value;
585 mateuszvis 190
      return(0);
191
    }
192
  } else if (stringstartswith(arg, "timesep:") == 0) {
193
    if (value[1] == 0) { /* value must be exactly one character */
591 mateuszvis 194
      c->CTYINFO.timesep[0] = *value;
585 mateuszvis 195
      return(0);
196
    }
197
  } else if (stringstartswith(arg, "datefmt:") == 0) {
198
    if (strcmp(value, "MDY") == 0) {
591 mateuszvis 199
      c->CTYINFO.datefmt = COUNTRY_DATE_MDY;
585 mateuszvis 200
      return(0);
201
    } else if (strcmp(value, "DMY") == 0) {
591 mateuszvis 202
      c->CTYINFO.datefmt = COUNTRY_DATE_DMY;
585 mateuszvis 203
      return(0);
204
    } else if (strcmp(value, "YMD") == 0) {
591 mateuszvis 205
      c->CTYINFO.datefmt = COUNTRY_DATE_YMD;
585 mateuszvis 206
      return(0);
207
    }
208
  } else if (stringstartswith(arg, "timefmt:") == 0) {
209
    if (value[1] == 0) {
210
      if ((value[0] >= '0') && (value[0] <= '1')) {
591 mateuszvis 211
        c->CTYINFO.timefmt = value[0] - '0';
585 mateuszvis 212
        return(0);
213
      }
214
    }
215
  } else if (stringstartswith(arg, "curr:") == 0) {
216
    if (strlen(value) <= 4) {
591 mateuszvis 217
      strcpy(c->CTYINFO.currsym, value);
585 mateuszvis 218
      return(0);
219
    }
220
  } else if (stringstartswith(arg, "currpos:") == 0) {
221
    if (value[1] == 0) {
222
      if (value[0] == '0') {
591 mateuszvis 223
        c->CTYINFO.currpos = 0;
585 mateuszvis 224
        return(0);
225
      } else if (value[0] == '1') {
591 mateuszvis 226
        c->CTYINFO.currpos = 1;
585 mateuszvis 227
        return(0);
228
      } else if (value[0] == '2') {
591 mateuszvis 229
        c->CTYINFO.currpos = 0;
230
        c->CTYINFO.currdecsym = 1;
585 mateuszvis 231
        return(0);
232
      }
233
    }
234
  } else if (stringstartswith(arg, "currspc:") == 0) {
235
    if (value[1] == 0) {
236
      if ((value[0] >= '0') && (value[0] <= '1')) {
591 mateuszvis 237
        c->CTYINFO.currspace = value[0] - '0';
585 mateuszvis 238
        return(0);
239
      }
240
    }
241
  } else if (stringstartswith(arg, "currprec:") == 0) {
242
    if (value[1] == 0) {
243
      if ((value[0] >= '0') && (value[0] <= '9')) {
591 mateuszvis 244
        c->CTYINFO.currprec = value[0] - '0';
585 mateuszvis 245
        return(0);
246
      }
247
    }
248
  } else if (stringstartswith(arg, "yesno:") == 0) {
249
    /* string must be exactly 2 characters long */
250
    if ((value[0] != 0) && (value[1] != 0) && (value[2] == 0)) {
591 mateuszvis 251
      c->YESNO.yes[0] = value[0];
252
      c->YESNO.no[0] = value[1];
585 mateuszvis 253
      return(0);
254
    }
255
  }
256
  /* if I'm here, something went wrong */
257
  return(-4);
258
}
259
 
260
 
588 mateuszvis 261
/* converts a path to its canonic representation, returns 0 on success
262
 * or DOS err on failure (invalid drive) */
263
static unsigned short file_truename(const char *dst, char *src) {
264
  unsigned short res = 0;
265
  _asm {
266
    push es
267
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
268
    push ds
269
    pop es
270
    mov si, src
271
    mov di, dst
272
    int 0x21
273
    jnc DONE
274
    mov [res], ax
275
    DONE:
276
    pop es
277
  }
278
  return(res);
279
}
280
 
281
 
585 mateuszvis 282
int main(int argc, char **argv) {
283
  struct country cntdata;
284
  int changedflag;
285
  int x;
588 mateuszvis 286
  static char fname[130];
585 mateuszvis 287
 
288
  if ((argc < 2) || (argv[1][0] == '/')) {
289
    about();
290
    return(1);
291
  }
292
 
588 mateuszvis 293
  if (file_truename(fname, argv[1]) != 0) {
294
    puts("ERROR: bad file path");
295
    return(1);
296
  }
585 mateuszvis 297
 
298
  x = country_read(&cntdata, fname);
299
  if (x != 0) {
300
    printf("ERROR: failed to read the preference file [%d]\n", x);
301
    return(2);
302
  }
303
 
304
  changedflag = argc - 2;
305
 
306
  /* process command line arguments */
307
  while (--argc > 1) {
308
    if (processarg(argv[argc], &cntdata) != 0) {
309
      about();
310
      return(3);
311
    }
312
  }
313
 
591 mateuszvis 314
  printf("Country intl code.....: %03d\n", cntdata.CTYINFO.id);
315
  printf("Codepage..............: %d\n", cntdata.CTYINFO.codepage);
316
  printf("Decimal separator.....: %c\n", cntdata.CTYINFO.decimal[0]);
317
  printf("Thousands separator...: %c\n", cntdata.CTYINFO.thousands[0]);
585 mateuszvis 318
  printf("Date format...........: %s\n", datestring(&cntdata));
319
  printf("Time format...........: %s\n", timestring(&cntdata));
591 mateuszvis 320
  printf("Yes/No letters........: %c/%c\n", cntdata.YESNO.yes[0], cntdata.YESNO.no[0]);
585 mateuszvis 321
  printf("Currency example......: %s\n", currencystring(&cntdata));
322
 
323
  printf("\n"
588 mateuszvis 324
         "Please make sure your CONFIG.SYS contains a COUNTRY directive that points to\n"
325
         "your custom preferences file:\n"
591 mateuszvis 326
         "COUNTRY=%03d,%03d,%s\n\n", cntdata.CTYINFO.id, cntdata.CTYINFO.codepage, fname);
585 mateuszvis 327
 
328
  /* if anything changed, write the new file */
329
  if (changedflag != 0) country_write(fname, &cntdata);
330
 
331
  return(0);
332
}