Subversion Repositories SvarDOS

Rev

Rev 593 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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