Subversion Repositories SvarDOS

Rev

Rev 593 | Rev 606 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 593 Rev 603
1
/*
1
/*
2
 * Locales configuration for SvarDOS
2
 * Locales configuration for SvarDOS
3
 *
3
 *
4
 * Copyright (C) Mateusz Viste 2015-2022
4
 * Copyright (C) Mateusz Viste 2015-2022
5
 *
5
 *
6
 * MIT license
6
 * MIT license
7
 *
7
 *
8
 * Permission is hereby granted, free of charge, to any person obtaining a copy
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
9
 * of this software and associated documentation files (the "Software"), to
10
 * deal in the Software without restriction, including without limitation the
10
 * deal in the Software without restriction, including without limitation the
11
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
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
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:
13
 * furnished to do so, subject to the following conditions:
14
 *
14
 *
15
 * The above copyright notice and this permission notice shall be included in
15
 * The above copyright notice and this permission notice shall be included in
16
 * all copies or substantial portions of the Software.
16
 * all copies or substantial portions of the Software.
17
 *
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
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,
19
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
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
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
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
23
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
 * IN THE SOFTWARE.
24
 * IN THE SOFTWARE.
25
 */
25
 */
26
 
26
 
27
#include <stdio.h>
27
#include <stdio.h>
28
#include <stdlib.h> /* atoi() */
28
#include <stdlib.h> /* atoi() */
29
#include <string.h> /* strchr */
29
#include <string.h> /* strchr */
30
 
30
 
-
 
31
#include "svarlang.lib/svarlang.h"
-
 
32
 
31
#include "country.h"
33
#include "country.h"
32
 
34
 
33
#define PVER "20220202"
35
#define PVER "20220203"
34
#define PDATE "2015-2022"
36
#define PDATE "2015-2022"
35
 
37
 
36
 
38
 
-
 
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,
-
 
75
  NLS_ERR_INVPARAM      = 0x0903
-
 
76
};
-
 
77
 
-
 
78
 
-
 
79
static void nls_puts(enum NLS_STRINGS id) {
-
 
80
  puts(svarlang_strid(id));
-
 
81
}
-
 
82
 
-
 
83
 
-
 
84
static void nls_put(enum NLS_STRINGS id) {
-
 
85
  printf("%s", svarlang_strid(id));
-
 
86
}
-
 
87
 
-
 
88
 
-
 
89
static void crlf(void) {
-
 
90
  puts("");
-
 
91
}
-
 
92
 
-
 
93
 
37
static void about(void) {
94
static void about(void) {
38
  puts("localcfg ver " PVER " - locales configuration for DOS\n"
95
  printf("localcfg ");
-
 
96
  nls_put(NLS_HLP_VER);
39
       "Copyright (C) " PDATE " Mateusz Viste\n"
97
  puts(" " PVER ", (C) " PDATE " Mateusz Viste");
40
       "\n"
98
  crlf();
41
       "localcfg creates or edits a custom COUNTRY.SYS-like file with your preferences.\n"
99
  nls_puts(NLS_HLP_DESC);
42
       "\n"
100
  crlf();
43
       "usage: localcfg [COUNTRY.SYS] [options]\n"
101
  nls_puts(NLS_HLP_USAGE);
44
       "\n"
102
  crlf();
45
       "options allow to configure country locales to your likening, as follows:\n"
103
  nls_puts(NLS_HLP_OPTIONS);
-
 
104
  crlf();
46
       "  /country:XX indicates your country code is XX (1 for USA, 33 for France, etc)\n"
105
  nls_puts(NLS_HLP_COUNTRY);
47
       "  /cp:XXX     adapts country data for codepage XXX (example: '437')\n"
106
  nls_puts(NLS_HLP_CP);
48
       "  /decim:X    reconfigures the decimal symbol to be 'X'");
107
  nls_puts(NLS_HLP_DECIM);
49
  puts("  /thous:X    reconfigures the thousands symbol to be 'X'\n"
108
  nls_puts(NLS_HLP_THOUS);
50
       "  /datesep:X  sets the date separator to 'X' (for example '/')\n"
109
  nls_puts(NLS_HLP_DATESEP);
51
       "  /datefmt:X  sets the date format, can be: MDY, DMY or YMD\n"
110
  nls_puts(NLS_HLP_DATEFMT);
52
       "  /timesep:X  sets the time separator to 'X' (for example ':')\n"
111
  nls_puts(NLS_HLP_TIMESEP);
53
       "  /timefmt:X  sets the time format: 0=12h with AM/PM or 1=24h\n"
112
  nls_puts(NLS_HLP_TIMEFMT);
54
       "  /curr:XXX   sets the currency to XXX (a string of 1 to 4 characters)\n"
113
  nls_puts(NLS_HLP_CURR);
55
       "  /currpos:X  sets the currency symbol position to X, where X is either");
114
  nls_puts(NLS_HLP_CURRPOS0);
56
  puts("              0=currency precedes the value, 1=currency follows the value and\n"
115
  nls_puts(NLS_HLP_CURRPOS1);
57
       "              2=currency replaces the decimal sign\n"
116
  nls_puts(NLS_HLP_CURRPOS2);
58
       "  /currspc:X  space between the currency and the value (0=no, 1=yes)\n"
117
  nls_puts(NLS_HLP_CURRSPC);
59
       "  /currprec:X currency's precision (number of decimal digits, 0..9)\n"
118
  nls_puts(NLS_HLP_CURRPREC);
60
       "  /yesno:XY   sets the 'Yes/No' letter to XY (default: YN)\n"
119
  nls_puts(NLS_HLP_YESNO);
61
       "\n"
120
  crlf();
62
       "If COUNTRY.SYS location is not provided, then localcfg tries loading it\n"
-
 
63
       "from %DOSDIR%\\CFG\\COUNTRY.SYS\n"
121
  nls_puts(NLS_HLP_INFOLOC1);
64
      );
122
  nls_puts(NLS_HLP_INFOLOC2);
65
}
123
}
66
 
124
 
67
 
125
 
68
static char *datestring(struct country *c) {
126
static char *datestring(struct country *c) {
69
  static char result[16];
127
  static char result[16];
70
  switch (c->CTYINFO.datefmt) {
128
  switch (c->CTYINFO.datefmt) {
71
    case COUNTRY_DATE_MDY:
129
    case COUNTRY_DATE_MDY:
72
      sprintf(result, "12%c31%c1990", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
130
      sprintf(result, "12%c31%c1990", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
73
      break;
131
      break;
74
    case COUNTRY_DATE_DMY:
132
    case COUNTRY_DATE_DMY:
75
      sprintf(result, "31%c12%c1990", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
133
      sprintf(result, "31%c12%c1990", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
76
      break;
134
      break;
77
    case COUNTRY_DATE_YMD:
135
    case COUNTRY_DATE_YMD:
78
    default:
136
    default:
79
      sprintf(result, "1990%c12%c31", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
137
      sprintf(result, "1990%c12%c31", c->CTYINFO.datesep[0], c->CTYINFO.datesep[0]);
80
      break;
138
      break;
81
  }
139
  }
82
  return(result);
140
  return(result);
83
}
141
}
84
 
142
 
85
 
143
 
86
static char *timestring(struct country *c) {
144
static char *timestring(struct country *c) {
87
  static char result[16];
145
  static char result[16];
88
  if (c->CTYINFO.timefmt == COUNTRY_TIME12) {
146
  if (c->CTYINFO.timefmt == COUNTRY_TIME12) {
89
    sprintf(result, "11%c59%c59 PM", c->CTYINFO.timesep[0], c->CTYINFO.timesep[0]);
147
    sprintf(result, "11%c59%c59 PM", c->CTYINFO.timesep[0], c->CTYINFO.timesep[0]);
90
  } else {
148
  } else {
91
    sprintf(result, "23%c59%c59", c->CTYINFO.timesep[0], c->CTYINFO.timesep[0]);
149
    sprintf(result, "23%c59%c59", c->CTYINFO.timesep[0], c->CTYINFO.timesep[0]);
92
  }
150
  }
93
  return(result);
151
  return(result);
94
}
152
}
95
 
153
 
96
 
154
 
97
static char *currencystring(struct country *c) {
155
static char *currencystring(struct country *c) {
98
  static char result[16];
156
  static char result[16];
99
  char decimalpart[16];
157
  char decimalpart[16];
100
  char space[2] = {0, 0};
158
  char space[2] = {0, 0};
101
  char decsym[8];
159
  char decsym[8];
102
  char cursym[8];
160
  char cursym[8];
103
  decimalpart[0] = '1';
161
  decimalpart[0] = '1';
104
  decimalpart[1] = '2';
162
  decimalpart[1] = '2';
105
  decimalpart[2] = '3';
163
  decimalpart[2] = '3';
106
  decimalpart[3] = '4';
164
  decimalpart[3] = '4';
107
  decimalpart[4] = '5';
165
  decimalpart[4] = '5';
108
  decimalpart[5] = '6';
166
  decimalpart[5] = '6';
109
  decimalpart[6] = '7';
167
  decimalpart[6] = '7';
110
  decimalpart[7] = '8';
168
  decimalpart[7] = '8';
111
  decimalpart[8] = '9';
169
  decimalpart[8] = '9';
112
  decimalpart[9] = 0;
170
  decimalpart[9] = 0;
113
  /* prepare the decimal string first */
171
  /* prepare the decimal string first */
114
  if (c->CTYINFO.currprec < 9) {
172
  if (c->CTYINFO.currprec < 9) {
115
    decimalpart[c->CTYINFO.currprec] = 0;
173
    decimalpart[c->CTYINFO.currprec] = 0;
116
  }
174
  }
117
  /* prepare the currency space string */
175
  /* prepare the currency space string */
118
  if (c->CTYINFO.currspace != 0) {
176
  if (c->CTYINFO.currspace != 0) {
119
    space[0] = ' ';
177
    space[0] = ' ';
120
  }
178
  }
121
  /* prepare the currency and decimal symbols */
179
  /* prepare the currency and decimal symbols */
122
  if (c->CTYINFO.currdecsym != 0) { /* currency replaces the decimal point */
180
  if (c->CTYINFO.currdecsym != 0) { /* currency replaces the decimal point */
123
    sprintf(decsym, "%s", c->CTYINFO.currsym);
181
    sprintf(decsym, "%s", c->CTYINFO.currsym);
124
    cursym[0] = 0;
182
    cursym[0] = 0;
125
  } else {
183
  } else {
126
    sprintf(decsym, "%c", c->CTYINFO.decimal[0]);
184
    sprintf(decsym, "%c", c->CTYINFO.decimal[0]);
127
    sprintf(cursym, "%s", c->CTYINFO.currsym);
185
    sprintf(cursym, "%s", c->CTYINFO.currsym);
128
  }
186
  }
129
  if (c->CTYINFO.currprec == 0) decsym[0] = 0;
187
  if (c->CTYINFO.currprec == 0) decsym[0] = 0;
130
  /* compute the final string */
188
  /* compute the final string */
131
  if (c->CTYINFO.currpos == 0) { /* currency precedes value */
189
  if (c->CTYINFO.currpos == 0) { /* currency precedes value */
132
    sprintf(result, "%s%s99%s%s", cursym, space, decsym, decimalpart);
190
    sprintf(result, "%s%s99%s%s", cursym, space, decsym, decimalpart);
133
  } else { /* currency follows value or replaces decimal symbol */
191
  } else { /* currency follows value or replaces decimal symbol */
134
    sprintf(result, "99%s%s%s%s", decsym, decimalpart, space, cursym);
192
    sprintf(result, "99%s%s%s%s", decsym, decimalpart, space, cursym);
135
  }
193
  }
136
  return(result);
194
  return(result);
137
}
195
}
138
 
196
 
139
 
197
 
140
/* checks if str starts with prefix. returns 0 if so, non-zero otherwise. */
198
/* checks if str starts with prefix. returns 0 if so, non-zero otherwise. */
141
static int stringstartswith(char *str, char *prefix) {
199
static int stringstartswith(char *str, char *prefix) {
142
  for (;;) {
200
  for (;;) {
143
    /* end of prefix means success */
201
    /* end of prefix means success */
144
    if (*prefix == 0) return(0);
202
    if (*prefix == 0) return(0);
145
    /* otherwise there is no match */
203
    /* otherwise there is no match */
146
    if (*str != *prefix) return(-1);
204
    if (*str != *prefix) return(-1);
147
    /* if match good so far, look at next char */
205
    /* if match good so far, look at next char */
148
    str += 1;
206
    str += 1;
149
    prefix += 1;
207
    prefix += 1;
150
  }
208
  }
151
}
209
}
152
 
210
 
153
 
211
 
154
/* processes an argument. returns 0 on success, non-zero otherwise. */
212
/* processes an argument. returns 0 on success, non-zero otherwise. */
155
static int processarg(char *arg, struct country *c) {
213
static int processarg(char *arg, struct country *c) {
156
  char *value;
214
  char *value;
157
  int intvalue;
215
  int intvalue;
158
  /* an option must start with a '/' */
216
  /* an option must start with a '/' */
159
  if (arg[0] != '/') return(-1);
217
  if (arg[0] != '/') return(-1);
160
  arg += 1; /* skip the slash */
218
  arg += 1; /* skip the slash */
161
  /* find where the value starts */
219
  /* find where the value starts */
162
  value = strchr(arg, ':');
220
  value = strchr(arg, ':');
163
  /* if no value present, fail */
221
  /* if no value present, fail */
164
  if (value == NULL) return(-2);
222
  if (value == NULL) return(-2);
165
  value += 1;
223
  value += 1;
166
  if (*value == 0) return(-3);
224
  if (*value == 0) return(-3);
167
  /* interpret the option now */
225
  /* interpret the option now */
168
  if (stringstartswith(arg, "country:") == 0) {
226
  if (stringstartswith(arg, "country:") == 0) {
169
    intvalue = atoi(value);
227
    intvalue = atoi(value);
170
    if ((intvalue > 0) && (intvalue < 1000)) {
228
    if ((intvalue > 0) && (intvalue < 1000)) {
171
      c->CTYINFO.id = intvalue;
229
      c->CTYINFO.id = intvalue;
172
      return(0);
230
      return(0);
173
    }
231
    }
174
  } else if (stringstartswith(arg, "cp:") == 0) {
232
  } else if (stringstartswith(arg, "cp:") == 0) {
175
    intvalue = atoi(value);
233
    intvalue = atoi(value);
176
    if ((intvalue > 0) && (intvalue < 1000)) {
234
    if ((intvalue > 0) && (intvalue < 1000)) {
177
      c->CTYINFO.codepage = intvalue;
235
      c->CTYINFO.codepage = intvalue;
178
      return(0);
236
      return(0);
179
    }
237
    }
180
  } else if (stringstartswith(arg, "decim:") == 0) {
238
  } else if (stringstartswith(arg, "decim:") == 0) {
181
    if (value[1] == 0) { /* value must be exactly one character */
239
    if (value[1] == 0) { /* value must be exactly one character */
182
      c->CTYINFO.decimal[0] = *value;
240
      c->CTYINFO.decimal[0] = *value;
183
      return(0);
241
      return(0);
184
    }
242
    }
185
  } else if (stringstartswith(arg, "thous:") == 0) {
243
  } else if (stringstartswith(arg, "thous:") == 0) {
186
    if (value[1] == 0) { /* value must be exactly one character */
244
    if (value[1] == 0) { /* value must be exactly one character */
187
      c->CTYINFO.thousands[0] = *value;
245
      c->CTYINFO.thousands[0] = *value;
188
      return(0);
246
      return(0);
189
    }
247
    }
190
  } else if (stringstartswith(arg, "datesep:") == 0) {
248
  } else if (stringstartswith(arg, "datesep:") == 0) {
191
    if (value[1] == 0) { /* value must be exactly one character */
249
    if (value[1] == 0) { /* value must be exactly one character */
192
      c->CTYINFO.datesep[0] = *value;
250
      c->CTYINFO.datesep[0] = *value;
193
      return(0);
251
      return(0);
194
    }
252
    }
195
  } else if (stringstartswith(arg, "timesep:") == 0) {
253
  } else if (stringstartswith(arg, "timesep:") == 0) {
196
    if (value[1] == 0) { /* value must be exactly one character */
254
    if (value[1] == 0) { /* value must be exactly one character */
197
      c->CTYINFO.timesep[0] = *value;
255
      c->CTYINFO.timesep[0] = *value;
198
      return(0);
256
      return(0);
199
    }
257
    }
200
  } else if (stringstartswith(arg, "datefmt:") == 0) {
258
  } else if (stringstartswith(arg, "datefmt:") == 0) {
201
    if (strcmp(value, "MDY") == 0) {
259
    if (strcmp(value, "MDY") == 0) {
202
      c->CTYINFO.datefmt = COUNTRY_DATE_MDY;
260
      c->CTYINFO.datefmt = COUNTRY_DATE_MDY;
203
      return(0);
261
      return(0);
204
    } else if (strcmp(value, "DMY") == 0) {
262
    } else if (strcmp(value, "DMY") == 0) {
205
      c->CTYINFO.datefmt = COUNTRY_DATE_DMY;
263
      c->CTYINFO.datefmt = COUNTRY_DATE_DMY;
206
      return(0);
264
      return(0);
207
    } else if (strcmp(value, "YMD") == 0) {
265
    } else if (strcmp(value, "YMD") == 0) {
208
      c->CTYINFO.datefmt = COUNTRY_DATE_YMD;
266
      c->CTYINFO.datefmt = COUNTRY_DATE_YMD;
209
      return(0);
267
      return(0);
210
    }
268
    }
211
  } else if (stringstartswith(arg, "timefmt:") == 0) {
269
  } else if (stringstartswith(arg, "timefmt:") == 0) {
212
    if (value[1] == 0) {
270
    if (value[1] == 0) {
213
      if ((value[0] >= '0') && (value[0] <= '1')) {
271
      if ((value[0] >= '0') && (value[0] <= '1')) {
214
        c->CTYINFO.timefmt = value[0] - '0';
272
        c->CTYINFO.timefmt = value[0] - '0';
215
        return(0);
273
        return(0);
216
      }
274
      }
217
    }
275
    }
218
  } else if (stringstartswith(arg, "curr:") == 0) {
276
  } else if (stringstartswith(arg, "curr:") == 0) {
219
    if (strlen(value) <= 4) {
277
    if (strlen(value) <= 4) {
220
      strcpy(c->CTYINFO.currsym, value);
278
      strcpy(c->CTYINFO.currsym, value);
221
      return(0);
279
      return(0);
222
    }
280
    }
223
  } else if (stringstartswith(arg, "currpos:") == 0) {
281
  } else if (stringstartswith(arg, "currpos:") == 0) {
224
    if (value[1] == 0) {
282
    if (value[1] == 0) {
225
      if (value[0] == '0') {
283
      if (value[0] == '0') {
226
        c->CTYINFO.currpos = 0;
284
        c->CTYINFO.currpos = 0;
227
        return(0);
285
        return(0);
228
      } else if (value[0] == '1') {
286
      } else if (value[0] == '1') {
229
        c->CTYINFO.currpos = 1;
287
        c->CTYINFO.currpos = 1;
230
        return(0);
288
        return(0);
231
      } else if (value[0] == '2') {
289
      } else if (value[0] == '2') {
232
        c->CTYINFO.currpos = 0;
290
        c->CTYINFO.currpos = 0;
233
        c->CTYINFO.currdecsym = 1;
291
        c->CTYINFO.currdecsym = 1;
234
        return(0);
292
        return(0);
235
      }
293
      }
236
    }
294
    }
237
  } else if (stringstartswith(arg, "currspc:") == 0) {
295
  } else if (stringstartswith(arg, "currspc:") == 0) {
238
    if (value[1] == 0) {
296
    if (value[1] == 0) {
239
      if ((value[0] >= '0') && (value[0] <= '1')) {
297
      if ((value[0] >= '0') && (value[0] <= '1')) {
240
        c->CTYINFO.currspace = value[0] - '0';
298
        c->CTYINFO.currspace = value[0] - '0';
241
        return(0);
299
        return(0);
242
      }
300
      }
243
    }
301
    }
244
  } else if (stringstartswith(arg, "currprec:") == 0) {
302
  } else if (stringstartswith(arg, "currprec:") == 0) {
245
    if (value[1] == 0) {
303
    if (value[1] == 0) {
246
      if ((value[0] >= '0') && (value[0] <= '9')) {
304
      if ((value[0] >= '0') && (value[0] <= '9')) {
247
        c->CTYINFO.currprec = value[0] - '0';
305
        c->CTYINFO.currprec = value[0] - '0';
248
        return(0);
306
        return(0);
249
      }
307
      }
250
    }
308
    }
251
  } else if (stringstartswith(arg, "yesno:") == 0) {
309
  } else if (stringstartswith(arg, "yesno:") == 0) {
252
    /* string must be exactly 2 characters long */
310
    /* string must be exactly 2 characters long */
253
    if ((value[0] != 0) && (value[1] != 0) && (value[2] == 0)) {
311
    if ((value[0] != 0) && (value[1] != 0) && (value[2] == 0)) {
254
      c->YESNO.yes[0] = value[0];
312
      c->YESNO.yes[0] = value[0];
255
      c->YESNO.no[0] = value[1];
313
      c->YESNO.no[0] = value[1];
256
      return(0);
314
      return(0);
257
    }
315
    }
258
  }
316
  }
259
  /* if I'm here, something went wrong */
317
  /* if I'm here, something went wrong */
260
  return(-4);
318
  return(-4);
261
}
319
}
262
 
320
 
263
 
321
 
264
/* converts a path to its canonic representation, returns 0 on success
322
/* converts a path to its canonic representation, returns 0 on success
265
 * or DOS err on failure (invalid drive) */
323
 * or DOS err on failure (invalid drive) */
266
static unsigned short file_truename(const char *dst, char *src) {
324
static unsigned short file_truename(const char *dst, char *src) {
267
  unsigned short res = 0;
325
  unsigned short res = 0;
268
  _asm {
326
  _asm {
269
    push es
327
    push es
270
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
328
    mov ah, 0x60  /* query truename, DS:SI=src, ES:DI=dst */
271
    push ds
329
    push ds
272
    pop es
330
    pop es
273
    mov si, src
331
    mov si, src
274
    mov di, dst
332
    mov di, dst
275
    int 0x21
333
    int 0x21
276
    jnc DONE
334
    jnc DONE
277
    mov [res], ax
335
    mov [res], ax
278
    DONE:
336
    DONE:
279
    pop es
337
    pop es
280
  }
338
  }
281
  return(res);
339
  return(res);
282
}
340
}
283
 
341
 
284
 
342
 
285
static void default_country_path(char *s) {
343
static void default_country_path(char *s) {
286
  char *dosdir = getenv("DOSDIR");
344
  char *dosdir = getenv("DOSDIR");
287
  size_t dosdirlen;
345
  size_t dosdirlen;
288
  s[0] = 0;
346
  s[0] = 0;
289
  if (dosdir == NULL) return;
347
  if (dosdir == NULL) return;
290
  dosdirlen = strlen(dosdir);
348
  dosdirlen = strlen(dosdir);
291
  if (dosdirlen == 0) return;
349
  if (dosdirlen == 0) return;
292
  /* drop trailing backslash if present */
350
  /* drop trailing backslash if present */
293
  if (dosdir[dosdirlen - 1] == '\\') dosdirlen--;
351
  if (dosdir[dosdirlen - 1] == '\\') dosdirlen--;
294
  /* copy dosdir to s and append the rest of the path */
352
  /* copy dosdir to s and append the rest of the path */
295
  memcpy(s, dosdir, dosdirlen);
353
  memcpy(s, dosdir, dosdirlen);
296
  strcpy(s + dosdirlen, "\\CFG\\COUNTRY.SYS");
354
  strcpy(s + dosdirlen, "\\CFG\\COUNTRY.SYS");
297
}
355
}
298
 
356
 
299
 
357
 
300
int main(int argc, char **argv) {
358
int main(int argc, char **argv) {
301
  struct country cntdata;
359
  struct country cntdata;
302
  int changedflag;
360
  int changedflag;
303
  int x;
361
  int x;
304
  static char fname[130];
362
  static char fname[130];
305
 
363
 
-
 
364
  svarlang_autoload("localcfg");
-
 
365
 
306
  /* scan argv looking for the path to country.sys */
366
  /* scan argv looking for the path to country.sys */
307
  for (x = 1; x < argc; x++) {
367
  for (x = 1; x < argc; x++) {
308
    if (argv[x][0] != '/') {
368
    if (argv[x][0] != '/') {
309
      if (fname[0] != 0) {
369
      if (fname[0] != 0) {
310
        puts("ERROR: file path can be provided only once");
370
        nls_puts(NLS_ERR_FILEPATHTWICE);
311
        return(1);
371
        return(1);
312
      }
372
      }
313
      /* */
373
      /* */
314
      if (file_truename(fname, argv[x]) != 0) {
374
      if (file_truename(fname, argv[x]) != 0) {
315
        puts("ERROR: bad file path");
375
        nls_puts(NLS_ERR_BADPATH);
316
        return(1);
376
        return(1);
317
      }
377
      }
318
    } else if (strcmp(argv[x], "/?") == 0) { /* is it /? */
378
    } else if (strcmp(argv[x], "/?") == 0) { /* is it /? */
319
      about();
379
      about();
320
      return(1);
380
      return(1);
321
    }
381
    }
322
  }
382
  }
323
 
383
 
324
  /* if no file path provided, look into %DOSDIR%\CFG\COUNTRY.SYS */
384
  /* if no file path provided, look into %DOSDIR%\CFG\COUNTRY.SYS */
325
  if (fname[0] == 0) default_country_path(fname);
385
  if (fname[0] == 0) default_country_path(fname);
326
 
386
 
327
  x = country_read(&cntdata, fname);
387
  x = country_read(&cntdata, fname);
328
  if (x != 0) {
388
  if (x != 0) {
329
    printf("ERROR: failed to read the preference file [%d]\n", x);
389
    nls_puts(NLS_ERR_READFAIL);
330
    return(2);
390
    return(2);
331
  }
391
  }
332
 
392
 
333
  changedflag = 0;
393
  changedflag = 0;
334
 
394
 
335
  /* process command line arguments */
395
  /* process command line arguments */
336
  for (x = 1; x < argc; x++) {
396
  for (x = 1; x < argc; x++) {
337
    if (argv[x][0] != '/') continue; /* skip country.sys filename (processed earlier) */
397
    if (argv[x][0] != '/') continue; /* skip country.sys filename (processed earlier) */
338
    changedflag++;
398
    changedflag++;
339
    if (processarg(argv[x], &cntdata) != 0) {
399
    if (processarg(argv[x], &cntdata) != 0) {
340
      puts("ERROR: invalid parameter syntax");
400
      nls_puts(NLS_ERR_INVPARAM);
341
      return(3);
401
      return(3);
342
    }
402
    }
343
  }
403
  }
344
 
404
 
-
 
405
  nls_put(NLS_INFO_COUNTRY);
345
  printf("Country intl code.....: %03d\n", cntdata.CTYINFO.id);
406
  printf(" %03d\r\n", cntdata.CTYINFO.id);
-
 
407
  nls_put(NLS_INFO_CODEPAGE);
346
  printf("Codepage..............: %d\n", cntdata.CTYINFO.codepage);
408
  printf(" %d\r\n", cntdata.CTYINFO.codepage);
-
 
409
  nls_put(NLS_INFO_DECSEP);
347
  printf("Decimal separator.....: %c\n", cntdata.CTYINFO.decimal[0]);
410
  printf(" %c\r\n", cntdata.CTYINFO.decimal[0]);
-
 
411
  nls_put(NLS_INFO_THOUSEP);
348
  printf("Thousands separator...: %c\n", cntdata.CTYINFO.thousands[0]);
412
  printf(" %c\r\n", cntdata.CTYINFO.thousands[0]);
-
 
413
  nls_put(NLS_INFO_DATEFMT);
349
  printf("Date format...........: %s\n", datestring(&cntdata));
414
  printf(" %s\r\n", datestring(&cntdata));
-
 
415
  nls_put(NLS_INFO_TIMEFMT);
350
  printf("Time format...........: %s\n", timestring(&cntdata));
416
  printf(" %s\r\n", timestring(&cntdata));
-
 
417
  nls_put(NLS_INFO_YESNO);
351
  printf("Yes/No letters........: %c/%c\n", cntdata.YESNO.yes[0], cntdata.YESNO.no[0]);
418
  printf(" %c/%c\r\n", cntdata.YESNO.yes[0], cntdata.YESNO.no[0]);
-
 
419
  nls_put(NLS_INFO_CURREXAMPLE);
352
  printf("Currency example......: %s\n", currencystring(&cntdata));
420
  printf(" %s\r\n", currencystring(&cntdata));
353
 
421
 
354
  printf("\n"
422
  crlf();
355
         "Make sure that your CONFIG.SYS contains this directive:\n"
423
  nls_puts(NLS_MAKESURE);
356
         "COUNTRY=%03d,%03d,%s\n\n", cntdata.CTYINFO.id, cntdata.CTYINFO.codepage, fname);
424
  printf("COUNTRY=%03d,%03d,%s", cntdata.CTYINFO.id, cntdata.CTYINFO.codepage, fname);
-
 
425
  crlf();
357
 
426
 
358
  /* if anything changed, write the new file */
427
  /* if anything changed, write the new file */
359
  if (changedflag != 0) country_write(fname, &cntdata);
428
  if (changedflag != 0) country_write(fname, &cntdata);
360
 
429
 
361
  return(0);
430
  return(0);
362
}
431
}
363
 
432