Subversion Repositories SvarDOS

Rev

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