Subversion Repositories SvarDOS

Rev

Rev 586 | Go to most recent revision | Details | Last modification | View Log | RSS feed

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