1 /*
2  * Copyright (c) 2019 Georg Brein. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright notice,
8  *    this list of conditions and the following disclaimer.
9  *
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  * 3. Neither the name of the copyright holder nor the names of its
15  *    contributors may be used to endorse or promote products derived from
16  *    this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 
32 #include <locale.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stddef.h>
36 #include <string.h>
37 #include <wchar.h>
38 #include <wctype.h>
39 #include <limits.h>
40 #include <errno.h>
41 
42 #include <unistd.h>
43 
44 #include "tnylpo.h"
45 
46 
47 /*
48  * global variables containing the configuration information
49  * from the command line resp. from the configuration file
50  */
51 /*
52  * size of the emulated terminal (doesn't change)
53  */
54 int lines = 0;
55 int cols = 0;
56 /*
57  * use terminal emulation (1) or line orientated/batch console (0)
58  */
59 int conf_interactive = (-1);
60 /*
61  * use WordStar (1) or VT52 (0) cursor keys (terminal emulation only)
62  */
63 int altkeys = (-1);
64 /*
65  * reverse the backspace and the delete key (terminal emulation only)
66  */
67 int reverse_bs_del = (-1);
68 /*
69  * seconds to wait before exiting full screen mode (terminal emulation only)
70  */
71 int screen_delay = (-1);
72 /*
73  * CP/M charset in use (0 ... primary, 1 ... secondary);
74  */
75 int charset = 0;
76 /*
77  * primary and secondary character set
78  */
79 wchar_t *conf_charset[256];
80 wchar_t *conf_alt_charset[256];
81 /*
82  * use this character to represent unprintable characters
83  */
84 wchar_t *conf_unprintable = NULL;
85 /*
86  * paths corresponding to the CP/M drives A...P and their read-only flags
87  */
88 char *conf_drives[16];
89 int conf_readonly[16];
90 /*
91  * name of the command file to execute
92  */
93 char *conf_command = NULL;
94 /*
95  * additional command line parameters
96  */
97 int conf_argc = 0;
98 char **conf_argv = NULL;
99 /*
100  * files corresponding to the CP/M character devices LST, PUN, and RDR
101  * and the flags controlling translation to Unix format
102  */
103 char *conf_printer = NULL;
104 int conf_printer_raw = (-1);
105 char *conf_punch = NULL;
106 int conf_punch_raw = (-1);
107 char *conf_reader = NULL;
108 int conf_reader_raw = (-1);
109 /*
110  * path of the log file
111  */
112 char *conf_log = NULL;
113 /*
114  * log level
115  */
116 enum log_level log_level = LL_UNSET;
117 /*
118  * CP/M default drive (0...15 corresponding to A...P)
119  */
120 int default_drive = (-1);
121 /*
122  * flag controlling wether calling BDOS function 19 closes the
123  * corresponding Unix file or not (some programs, e.g. dBase II continue
124  * to use FCBs for file I/O after closing them)
125  */
126 int dont_close = (-1);
127 /*
128  * dump configuration: default is no dump
129  */
130 enum dump conf_dump = 0;
131 /*
132  * emulation delay: insert a pause of delay_nanoseconds every
133  * delay_count instructions (default: no delay)
134  */
135 int delay_count = (-1);
136 int delay_nanoseconds = (-1);
137 /*
138  * save configuration: default is no saving done
139  */
140 const char *conf_save_file = NULL;
141 int conf_save_hex = 0;
142 int conf_save_start = 0;
143 int conf_save_end = 0;
144 /*
145  * use colors in terminal
146  */
147 int conf_color = (-1);
148 int conf_foreground = (-1);
149 int conf_background = (-1);
150 
151 
152 /*
153  * maximal length of a line in the configuration file
154  */
155 #define L_LINE 1024
156 
157 
158 /*
159  * variables for communication between the procedures of the
160  * configuration file parser
161  */
162 /*
163  * file pointer, file name, and current line number of the configuration file
164  */
165 static FILE *cf = NULL;
166 static char *cfn = NULL;
167 static int ln = 0;
168 /*
169  * pointer to the current character in current line of the configuration file
170  */
171 static wchar_t *curr_p = NULL;
172 /*
173  * current token and its attributes
174  */
175 static int token = (-1);
176 static unsigned long token_ul = 0;
177 static wchar_t *token_ident = NULL;
178 static wchar_t *token_string = NULL;
179 /*
180  * character class for blank; Solaris < 10 doesn't support
181  * iswblank(), so we use iswctype() instead; for this, we need the
182  * value of wctype("blank"), which we get once and store in this variable
183  */
184 static wctype_t wctype_blank;
185 
186 
187 /*
188  * built-in character sets: VT52 (ASCII + VT52 graphical character set),
189  * pure ASCII, and ISO-8859-1/Latin 1
190  */
191 enum charset {
192 	CS_NONE = (-1),
193 	CS_VT52 = 0,
194 	CS_ASCII = 1,
195 	CS_LATIN1 = 2,
196 	CS_TNYLPO = 3
197 };
198 
199 
200 /*
201  * Unfortunately, some platforms (though supporting UTF-8) have
202  * problems with some characters, e. g. Solaris < 10, which displays
203  * some symbols and the box-drawing characters in double width,
204  * which is not supported by tnylpo.
205  *
206  * The following #ifs are a hack to keep tnylpo useable on these platforms.
207  */
208 
209 
210 /*
211  * built-in character sets in wchar_t representation (unfortunately,
212  * not all of the VT52 graphical characters are available)
213  */
214 static const wchar_t *default_charset[256][4] = {
215 #if (defined OLD_SOLARIS)
216 /*00*/	{ NULL, NULL, NULL, L"·" },
217 	{ L"·", NULL, NULL, L"·" },
218 	{ L"·", NULL, NULL, L"·" },
219 	{ NULL, NULL, NULL, L"·" },
220 	{ NULL, NULL, NULL, L"·" },
221 	{ NULL, NULL, NULL, L"·" },
222 	{ L"°", NULL, NULL, L"·" },
223 	{ L"±", NULL, NULL, L"·" },
224 	{ L"·", NULL, NULL, L"·" },
225 	{ L"·", NULL, NULL, L"·" },
226 	{ L"÷", NULL, NULL, L"+" },
227 	{ L"·", NULL, NULL, L"+" },
228 	{ NULL, NULL, NULL, L"+" },
229 	{ NULL, NULL, NULL, L"+" },
230 	{ NULL, NULL, NULL, L"+" },
231 	{ NULL, NULL, NULL, L"·" },
232 /*10*/	{ NULL, NULL, NULL, L"·" },
233 	{ NULL, NULL, NULL, L"-" },
234 	{ NULL, NULL, NULL, L"·" },
235 	{ NULL, NULL, NULL, L"·" },
236 	{ L"·", NULL, NULL, L"+" },
237 	{ L"·", NULL, NULL, L"+" },
238 	{ L"·", NULL, NULL, L"+" },
239 	{ L"·", NULL, NULL, L"+" },
240 	{ L"·", NULL, NULL, L"|" },
241 	{ L"·", NULL, NULL, L"·" },
242 	{ L"·", NULL, NULL, L"·" },
243 	{ L"·", NULL, NULL, L"·" },
244 	{ L"·", NULL, NULL, L"·" },
245 	{ L"·", NULL, NULL, L"·" },
246 	{ L"¶", NULL, NULL, L"·" },
247 	{ L" ", NULL, NULL, L"·" },
248 /*20*/	{ L" ", L" ", L" ", L" " },
249 	{ L"!", L"!", L"!", L"!" },
250 	{ L"\"", L"\"", L"\"", L"\"" },
251 	{ L"#", L"#", L"#", L"#" },
252 	{ L"$", L"$", L"$", L"$" },
253 	{ L"%", L"%", L"%", L"%" },
254 	{ L"&", L"&", L"&", L"&" },
255 	{ L"\'", L"\'", L"\'", L"\'" },
256 	{ L"(", L"(", L"(", L"(" },
257 	{ L")", L")", L")", L")" },
258 	{ L"*", L"*", L"*", L"*" },
259 	{ L"+", L"+", L"+", L"+" },
260 	{ L",", L",", L",", L"," },
261 	{ L"-", L"-", L"-", L"-" },
262 	{ L".", L".", L".", L"." },
263 	{ L"/", L"/", L"/", L"/" },
264 /*30*/	{ L"0", L"0", L"0", L"0" },
265 	{ L"1", L"1", L"1", L"1" },
266 	{ L"2", L"2", L"2", L"2" },
267 	{ L"3", L"3", L"3", L"3" },
268 	{ L"4", L"4", L"4", L"4" },
269 	{ L"5", L"5", L"5", L"5" },
270 	{ L"6", L"6", L"6", L"6" },
271 	{ L"7", L"7", L"7", L"7" },
272 	{ L"8", L"8", L"8", L"8" },
273 	{ L"9", L"9", L"9", L"9" },
274 	{ L":", L":", L":", L":" },
275 	{ L";", L";", L";", L";" },
276 	{ L"<", L"<", L"<", L"<" },
277 	{ L"=", L"=", L"=", L"=" },
278 	{ L">", L">", L">", L">" },
279 	{ L"?", L"?", L"?", L"?" },
280 /*40*/	{ L"@", L"@", L"@", L"@" },
281 	{ L"A", L"A", L"A", L"A" },
282 	{ L"B", L"B", L"B", L"B" },
283 	{ L"C", L"C", L"C", L"C" },
284 	{ L"D", L"D", L"D", L"D" },
285 	{ L"E", L"E", L"E", L"E" },
286 	{ L"F", L"F", L"F", L"F" },
287 	{ L"G", L"G", L"G", L"G" },
288 	{ L"H", L"H", L"H", L"H" },
289 	{ L"I", L"I", L"I", L"I" },
290 	{ L"J", L"J", L"J", L"J" },
291 	{ L"K", L"K", L"K", L"K" },
292 	{ L"L", L"L", L"L", L"L" },
293 	{ L"M", L"M", L"M", L"M" },
294 	{ L"N", L"N", L"N", L"N" },
295 	{ L"O", L"O", L"O", L"O" },
296 /*50*/	{ L"P", L"P", L"P", L"P" },
297 	{ L"Q", L"Q", L"Q", L"Q" },
298 	{ L"R", L"R", L"R", L"R" },
299 	{ L"S", L"S", L"S", L"S" },
300 	{ L"T", L"T", L"T", L"T" },
301 	{ L"U", L"U", L"U", L"U" },
302 	{ L"V", L"V", L"V", L"V" },
303 	{ L"W", L"W", L"W", L"W" },
304 	{ L"X", L"X", L"X", L"X" },
305 	{ L"Y", L"Y", L"Y", L"Y" },
306 	{ L"Z", L"Z", L"Z", L"Z" },
307 	{ L"[", L"[", L"[", L"[" },
308 	{ L"\\", L"\\", L"\\", L"\\" },
309 	{ L"]", L"]", L"]", L"]" },
310 	{ L"^", L"^", L"^", L"^" },
311 	{ L"_", L"_", L"_", L"_" },
312 /*60*/	{ L"`", L"`", L"`", L"`" },
313 	{ L"a", L"a", L"a", L"a" },
314 	{ L"b", L"b", L"b", L"b" },
315 	{ L"c", L"c", L"c", L"c" },
316 	{ L"d", L"d", L"d", L"d" },
317 	{ L"e", L"e", L"e", L"e" },
318 	{ L"f", L"f", L"f", L"f" },
319 	{ L"g", L"g", L"g", L"g" },
320 	{ L"h", L"h", L"h", L"h" },
321 	{ L"i", L"i", L"i", L"i" },
322 	{ L"j", L"j", L"j", L"j" },
323 	{ L"k", L"k", L"k", L"k" },
324 	{ L"l", L"l", L"l", L"l" },
325 	{ L"m", L"m", L"m", L"m" },
326 	{ L"n", L"n", L"n", L"n" },
327 	{ L"o", L"o", L"o", L"o" },
328 /*70*/	{ L"p", L"p", L"p", L"p" },
329 	{ L"q", L"q", L"q", L"q" },
330 	{ L"r", L"r", L"r", L"r" },
331 	{ L"s", L"s", L"s", L"s" },
332 	{ L"t", L"t", L"t", L"t" },
333 	{ L"u", L"u", L"u", L"u" },
334 	{ L"v", L"v", L"v", L"v" },
335 	{ L"w", L"w", L"w", L"w" },
336 	{ L"x", L"x", L"x", L"x" },
337 	{ L"y", L"y", L"y", L"y" },
338 	{ L"z", L"z", L"z", L"z" },
339 	{ L"{", L"{", L"{", L"{" },
340 	{ L"|", L"|", L"|", L"|" },
341 	{ L"}", L"}", L"}", L"}" },
342 	{ L"~", L"~", L"~", L"~" },
343 	{ NULL, NULL, NULL, L"·" },
344 /*80*/	{ NULL, NULL, NULL, L"€" },
345 	{ NULL, NULL, NULL, L"·" },
346 	{ NULL, NULL, NULL, L"·" },
347 	{ NULL, NULL, NULL, L"·" },
348 	{ NULL, NULL, NULL, L"·" },
349 	{ NULL, NULL, NULL, L"·" },
350 	{ NULL, NULL, NULL, L"·" },
351 	{ NULL, NULL, NULL, L"·" },
352 	{ NULL, NULL, NULL, L"·" },
353 	{ NULL, NULL, NULL, L"·" },
354 	{ NULL, NULL, NULL, L"Š" },
355 	{ NULL, NULL, NULL, L"·" },
356 	{ NULL, NULL, NULL, L"Œ" },
357 	{ NULL, NULL, NULL, L"·" },
358 	{ NULL, NULL, NULL, L"Ž" },
359 	{ NULL, NULL, NULL, L"·" },
360 /*90*/	{ NULL, NULL, NULL, L"·" },
361 	{ NULL, NULL, NULL, L"·" },
362 	{ NULL, NULL, NULL, L"·" },
363 	{ NULL, NULL, NULL, L"·" },
364 	{ NULL, NULL, NULL, L"·" },
365 	{ NULL, NULL, NULL, L"·" },
366 	{ NULL, NULL, NULL, L"·" },
367 	{ NULL, NULL, NULL, L"·" },
368 	{ NULL, NULL, NULL, L"·" },
369 	{ NULL, NULL, NULL, L"·" },
370 	{ NULL, NULL, NULL, L"š" },
371 	{ NULL, NULL, NULL, L"·" },
372 	{ NULL, NULL, NULL, L"œ" },
373 	{ NULL, NULL, NULL, L"·" },
374 	{ NULL, NULL, NULL, L"ž" },
375 	{ NULL, NULL, NULL, L"Ÿ" },
376 /*a0*/	{ NULL, NULL, L" ", L" " },
377 	{ NULL, NULL, L"¡", L"¡" },
378 	{ NULL, NULL, L"¢", L"¢" },
379 	{ NULL, NULL, L"£", L"£" },
380 	{ NULL, NULL, L"¤", L"¤" },
381 	{ NULL, NULL, L"¥", L"¥" },
382 	{ NULL, NULL, L"¦", L"¦" },
383 	{ NULL, NULL, L"§", L"§" },
384 	{ NULL, NULL, L"¨", L"¨" },
385 	{ NULL, NULL, L"©", L"©" },
386 	{ NULL, NULL, L"ª", L"ª" },
387 	{ NULL, NULL, L"«", L"«" },
388 	{ NULL, NULL, L"¬", L"¬" },
389 	{ NULL, NULL, L"-", L"-" },
390 	{ NULL, NULL, L"®", L"®" },
391 	{ NULL, NULL, L"¯", L"¯" },
392 /*b0*/	{ NULL, NULL, L"°", L"°" },
393 	{ NULL, NULL, L"±", L"±" },
394 	{ NULL, NULL, L"²", L"²" },
395 	{ NULL, NULL, L"³", L"³" },
396 	{ NULL, NULL, L"´", L"´" },
397 	{ NULL, NULL, L"µ", L"µ" },
398 	{ NULL, NULL, L"¶", L"¶" },
399 	{ NULL, NULL, L"·", L"·" },
400 	{ NULL, NULL, L"¸", L"¸" },
401 	{ NULL, NULL, L"¹", L"¹" },
402 	{ NULL, NULL, L"º", L"º" },
403 	{ NULL, NULL, L"»", L"»" },
404 	{ NULL, NULL, L"¼", L"¼" },
405 	{ NULL, NULL, L"½", L"½" },
406 	{ NULL, NULL, L"¾", L"¾" },
407 	{ NULL, NULL, L"¿", L"¿" },
408 /*c0*/	{ NULL, NULL, L"À", L"À" },
409 	{ NULL, NULL, L"Á", L"Á" },
410 	{ NULL, NULL, L"Â", L"Â" },
411 	{ NULL, NULL, L"Ã", L"Ã" },
412 	{ NULL, NULL, L"Ä", L"Ä" },
413 	{ NULL, NULL, L"Å", L"Å" },
414 	{ NULL, NULL, L"Æ", L"Æ" },
415 	{ NULL, NULL, L"Ç", L"Ç" },
416 	{ NULL, NULL, L"È", L"È" },
417 	{ NULL, NULL, L"É", L"É" },
418 	{ NULL, NULL, L"Ê", L"Ê" },
419 	{ NULL, NULL, L"Ë", L"Ë" },
420 	{ NULL, NULL, L"Ì", L"Ì" },
421 	{ NULL, NULL, L"Í", L"Í" },
422 	{ NULL, NULL, L"Î", L"Î" },
423 	{ NULL, NULL, L"Ï", L"Ï" },
424 /*d0*/	{ NULL, NULL, L"Ð", L"Ð" },
425 	{ NULL, NULL, L"Ñ", L"Ñ" },
426 	{ NULL, NULL, L"Ò", L"Ò" },
427 	{ NULL, NULL, L"Ó", L"Ó" },
428 	{ NULL, NULL, L"Ô", L"Ô" },
429 	{ NULL, NULL, L"Õ", L"Õ" },
430 	{ NULL, NULL, L"Ö", L"Ö" },
431 	{ NULL, NULL, L"×", L"×" },
432 	{ NULL, NULL, L"Ø", L"Ø" },
433 	{ NULL, NULL, L"Ù", L"Ù" },
434 	{ NULL, NULL, L"Ú", L"Ú" },
435 	{ NULL, NULL, L"Û", L"Û" },
436 	{ NULL, NULL, L"Ü", L"Ü" },
437 	{ NULL, NULL, L"Ý", L"Ý" },
438 	{ NULL, NULL, L"Þ", L"Þ" },
439 	{ NULL, NULL, L"ß", L"ß" },
440 /*e0*/	{ NULL, NULL, L"à", L"à" },
441 	{ NULL, NULL, L"á", L"á" },
442 	{ NULL, NULL, L"â", L"â" },
443 	{ NULL, NULL, L"ã", L"ã" },
444 	{ NULL, NULL, L"ä", L"ä" },
445 	{ NULL, NULL, L"å", L"å" },
446 	{ NULL, NULL, L"æ", L"æ" },
447 	{ NULL, NULL, L"ç", L"ç" },
448 	{ NULL, NULL, L"è", L"è" },
449 	{ NULL, NULL, L"é", L"é" },
450 	{ NULL, NULL, L"ê", L"ê" },
451 	{ NULL, NULL, L"ë", L"ë" },
452 	{ NULL, NULL, L"ì", L"ì" },
453 	{ NULL, NULL, L"í", L"í" },
454 	{ NULL, NULL, L"î", L"î" },
455 	{ NULL, NULL, L"ï", L"ï" },
456 /*f0*/	{ NULL, NULL, L"ð", L"ð" },
457 	{ NULL, NULL, L"ñ", L"ñ" },
458 	{ NULL, NULL, L"ò", L"ò" },
459 	{ NULL, NULL, L"ó", L"ó" },
460 	{ NULL, NULL, L"ô", L"ô" },
461 	{ NULL, NULL, L"õ", L"õ" },
462 	{ NULL, NULL, L"ö", L"ö" },
463 	{ NULL, NULL, L"÷", L"÷" },
464 	{ NULL, NULL, L"ø", L"ø" },
465 	{ NULL, NULL, L"ù", L"ù" },
466 	{ NULL, NULL, L"ú", L"ú" },
467 	{ NULL, NULL, L"û", L"û" },
468 	{ NULL, NULL, L"ü", L"ü" },
469 	{ NULL, NULL, L"ý", L"ý" },
470 	{ NULL, NULL, L"þ", L"þ" },
471 	{ NULL, NULL, L"ÿ", L"ÿ" }
472 #else
473 /*00*/	{ NULL, NULL, NULL, L"▄" },
474 	{ L"█", NULL, NULL, L"█" },
475 	{ L"⅟", NULL, NULL, L"▐" },
476 	{ NULL, NULL, NULL, L"▖" },
477 	{ NULL, NULL, NULL, L"▗" },
478 	{ NULL, NULL, NULL, L"▘" },
479 	{ L"°", NULL, NULL, L"▝" },
480 	{ L"±", NULL, NULL, L"▌" },
481 	{ L"→", NULL, NULL, L"▀" },
482 	{ L"…", NULL, NULL, L"▞" },
483 	{ L"÷", NULL, NULL, L"┘" },
484 	{ L"↓", NULL, NULL, L"┐" },
485 	{ NULL, NULL, NULL, L"┌" },
486 	{ NULL, NULL, NULL, L"└" },
487 	{ NULL, NULL, NULL, L"┼" },
488 	{ NULL, NULL, NULL, L"▙" },
489 /*10*/	{ NULL, NULL, NULL, L"▛" },
490 	{ NULL, NULL, NULL, L"─" },
491 	{ NULL, NULL, NULL, L"▜" },
492 	{ NULL, NULL, NULL, L"▟" },
493 	{ L"₀", NULL, NULL, L"├" },
494 	{ L"₁", NULL, NULL, L"┤" },
495 	{ L"₂", NULL, NULL, L"┴" },
496 	{ L"₃", NULL, NULL, L"┬" },
497 	{ L"₄", NULL, NULL, L"│" },
498 	{ L"₅", NULL, NULL, L"←" },
499 	{ L"₆", NULL, NULL, L"↑" },
500 	{ L"₇", NULL, NULL, L"→" },
501 	{ L"₈", NULL, NULL, L"↓" },
502 	{ L"₉", NULL, NULL, L"▚" },
503 	{ L"¶", NULL, NULL, L"░" },
504 	{ L" ", NULL, NULL, L"▒" },
505 /*20*/	{ L" ", L" ", L" ", L" " },
506 	{ L"!", L"!", L"!", L"!" },
507 	{ L"\"", L"\"", L"\"", L"\"" },
508 	{ L"#", L"#", L"#", L"#" },
509 	{ L"$", L"$", L"$", L"$" },
510 	{ L"%", L"%", L"%", L"%" },
511 	{ L"&", L"&", L"&", L"&" },
512 	{ L"\'", L"\'", L"\'", L"\'" },
513 	{ L"(", L"(", L"(", L"(" },
514 	{ L")", L")", L")", L")" },
515 	{ L"*", L"*", L"*", L"*" },
516 	{ L"+", L"+", L"+", L"+" },
517 	{ L",", L",", L",", L"," },
518 	{ L"-", L"-", L"-", L"-" },
519 	{ L".", L".", L".", L"." },
520 	{ L"/", L"/", L"/", L"/" },
521 /*30*/	{ L"0", L"0", L"0", L"0" },
522 	{ L"1", L"1", L"1", L"1" },
523 	{ L"2", L"2", L"2", L"2" },
524 	{ L"3", L"3", L"3", L"3" },
525 	{ L"4", L"4", L"4", L"4" },
526 	{ L"5", L"5", L"5", L"5" },
527 	{ L"6", L"6", L"6", L"6" },
528 	{ L"7", L"7", L"7", L"7" },
529 	{ L"8", L"8", L"8", L"8" },
530 	{ L"9", L"9", L"9", L"9" },
531 	{ L":", L":", L":", L":" },
532 	{ L";", L";", L";", L";" },
533 	{ L"<", L"<", L"<", L"<" },
534 	{ L"=", L"=", L"=", L"=" },
535 	{ L">", L">", L">", L">" },
536 	{ L"?", L"?", L"?", L"?" },
537 /*40*/	{ L"@", L"@", L"@", L"@" },
538 	{ L"A", L"A", L"A", L"A" },
539 	{ L"B", L"B", L"B", L"B" },
540 	{ L"C", L"C", L"C", L"C" },
541 	{ L"D", L"D", L"D", L"D" },
542 	{ L"E", L"E", L"E", L"E" },
543 	{ L"F", L"F", L"F", L"F" },
544 	{ L"G", L"G", L"G", L"G" },
545 	{ L"H", L"H", L"H", L"H" },
546 	{ L"I", L"I", L"I", L"I" },
547 	{ L"J", L"J", L"J", L"J" },
548 	{ L"K", L"K", L"K", L"K" },
549 	{ L"L", L"L", L"L", L"L" },
550 	{ L"M", L"M", L"M", L"M" },
551 	{ L"N", L"N", L"N", L"N" },
552 	{ L"O", L"O", L"O", L"O" },
553 /*50*/	{ L"P", L"P", L"P", L"P" },
554 	{ L"Q", L"Q", L"Q", L"Q" },
555 	{ L"R", L"R", L"R", L"R" },
556 	{ L"S", L"S", L"S", L"S" },
557 	{ L"T", L"T", L"T", L"T" },
558 	{ L"U", L"U", L"U", L"U" },
559 	{ L"V", L"V", L"V", L"V" },
560 	{ L"W", L"W", L"W", L"W" },
561 	{ L"X", L"X", L"X", L"X" },
562 	{ L"Y", L"Y", L"Y", L"Y" },
563 	{ L"Z", L"Z", L"Z", L"Z" },
564 	{ L"[", L"[", L"[", L"[" },
565 	{ L"\\", L"\\", L"\\", L"\\" },
566 	{ L"]", L"]", L"]", L"]" },
567 	{ L"^", L"^", L"^", L"^" },
568 	{ L"_", L"_", L"_", L"_" },
569 /*60*/	{ L"`", L"`", L"`", L"`" },
570 	{ L"a", L"a", L"a", L"a" },
571 	{ L"b", L"b", L"b", L"b" },
572 	{ L"c", L"c", L"c", L"c" },
573 	{ L"d", L"d", L"d", L"d" },
574 	{ L"e", L"e", L"e", L"e" },
575 	{ L"f", L"f", L"f", L"f" },
576 	{ L"g", L"g", L"g", L"g" },
577 	{ L"h", L"h", L"h", L"h" },
578 	{ L"i", L"i", L"i", L"i" },
579 	{ L"j", L"j", L"j", L"j" },
580 	{ L"k", L"k", L"k", L"k" },
581 	{ L"l", L"l", L"l", L"l" },
582 	{ L"m", L"m", L"m", L"m" },
583 	{ L"n", L"n", L"n", L"n" },
584 	{ L"o", L"o", L"o", L"o" },
585 /*70*/	{ L"p", L"p", L"p", L"p" },
586 	{ L"q", L"q", L"q", L"q" },
587 	{ L"r", L"r", L"r", L"r" },
588 	{ L"s", L"s", L"s", L"s" },
589 	{ L"t", L"t", L"t", L"t" },
590 	{ L"u", L"u", L"u", L"u" },
591 	{ L"v", L"v", L"v", L"v" },
592 	{ L"w", L"w", L"w", L"w" },
593 	{ L"x", L"x", L"x", L"x" },
594 	{ L"y", L"y", L"y", L"y" },
595 	{ L"z", L"z", L"z", L"z" },
596 	{ L"{", L"{", L"{", L"{" },
597 	{ L"|", L"|", L"|", L"|" },
598 	{ L"}", L"}", L"}", L"}" },
599 	{ L"~", L"~", L"~", L"~" },
600 	{ NULL, NULL, NULL, L"▓" },
601 /*80*/	{ NULL, NULL, NULL, L"€" },
602 	{ NULL, NULL, NULL, L"≠" },
603 	{ NULL, NULL, NULL, L"‚" },
604 	{ NULL, NULL, NULL, L"ƒ" },
605 	{ NULL, NULL, NULL, L"„" },
606 	{ NULL, NULL, NULL, L"…" },
607 	{ NULL, NULL, NULL, L"†" },
608 	{ NULL, NULL, NULL, L"‡" },
609 	{ NULL, NULL, NULL, L"ˆ" },
610 	{ NULL, NULL, NULL, L"‰" },
611 	{ NULL, NULL, NULL, L"Š" },
612 	{ NULL, NULL, NULL, L"‹" },
613 	{ NULL, NULL, NULL, L"Œ" },
614 	{ NULL, NULL, NULL, L"IJ" },
615 	{ NULL, NULL, NULL, L"Ž" },
616 	{ NULL, NULL, NULL, L"≤" },
617 /*90*/	{ NULL, NULL, NULL, L"≥" },
618 	{ NULL, NULL, NULL, L"‘" },
619 	{ NULL, NULL, NULL, L"’" },
620 	{ NULL, NULL, NULL, L"“" },
621 	{ NULL, NULL, NULL, L"”" },
622 	{ NULL, NULL, NULL, L"•" },
623 	{ NULL, NULL, NULL, L"–" },
624 	{ NULL, NULL, NULL, L"—" },
625 	{ NULL, NULL, NULL, L"˜" },
626 	{ NULL, NULL, NULL, L"™" },
627 	{ NULL, NULL, NULL, L"š" },
628 	{ NULL, NULL, NULL, L"›" },
629 	{ NULL, NULL, NULL, L"œ" },
630 	{ NULL, NULL, NULL, L"ij" },
631 	{ NULL, NULL, NULL, L"ž" },
632 	{ NULL, NULL, NULL, L"Ÿ" },
633 /*a0*/	{ NULL, NULL, L" ", L" " },
634 	{ NULL, NULL, L"¡", L"¡" },
635 	{ NULL, NULL, L"¢", L"¢" },
636 	{ NULL, NULL, L"£", L"£" },
637 	{ NULL, NULL, L"¤", L"¤" },
638 	{ NULL, NULL, L"¥", L"¥" },
639 	{ NULL, NULL, L"¦", L"¦" },
640 	{ NULL, NULL, L"§", L"§" },
641 	{ NULL, NULL, L"¨", L"¨" },
642 	{ NULL, NULL, L"©", L"©" },
643 	{ NULL, NULL, L"ª", L"ª" },
644 	{ NULL, NULL, L"«", L"«" },
645 	{ NULL, NULL, L"¬", L"¬" },
646 	{ NULL, NULL, L"–", L"–" },
647 	{ NULL, NULL, L"®", L"®" },
648 	{ NULL, NULL, L"¯", L"¯" },
649 /*b0*/	{ NULL, NULL, L"°", L"°" },
650 	{ NULL, NULL, L"±", L"±" },
651 	{ NULL, NULL, L"²", L"²" },
652 	{ NULL, NULL, L"³", L"³" },
653 	{ NULL, NULL, L"´", L"´" },
654 	{ NULL, NULL, L"µ", L"µ" },
655 	{ NULL, NULL, L"¶", L"¶" },
656 	{ NULL, NULL, L"·", L"·" },
657 	{ NULL, NULL, L"¸", L"¸" },
658 	{ NULL, NULL, L"¹", L"¹" },
659 	{ NULL, NULL, L"º", L"º" },
660 	{ NULL, NULL, L"»", L"»" },
661 	{ NULL, NULL, L"¼", L"¼" },
662 	{ NULL, NULL, L"½", L"½" },
663 	{ NULL, NULL, L"¾", L"¾" },
664 	{ NULL, NULL, L"¿", L"¿" },
665 /*c0*/	{ NULL, NULL, L"À", L"À" },
666 	{ NULL, NULL, L"Á", L"Á" },
667 	{ NULL, NULL, L"Â", L"Â" },
668 	{ NULL, NULL, L"Ã", L"Ã" },
669 	{ NULL, NULL, L"Ä", L"Ä" },
670 	{ NULL, NULL, L"Å", L"Å" },
671 	{ NULL, NULL, L"Æ", L"Æ" },
672 	{ NULL, NULL, L"Ç", L"Ç" },
673 	{ NULL, NULL, L"È", L"È" },
674 	{ NULL, NULL, L"É", L"É" },
675 	{ NULL, NULL, L"Ê", L"Ê" },
676 	{ NULL, NULL, L"Ë", L"Ë" },
677 	{ NULL, NULL, L"Ì", L"Ì" },
678 	{ NULL, NULL, L"Í", L"Í" },
679 	{ NULL, NULL, L"Î", L"Î" },
680 	{ NULL, NULL, L"Ï", L"Ï" },
681 /*d0*/	{ NULL, NULL, L"Ð", L"Ð" },
682 	{ NULL, NULL, L"Ñ", L"Ñ" },
683 	{ NULL, NULL, L"Ò", L"Ò" },
684 	{ NULL, NULL, L"Ó", L"Ó" },
685 	{ NULL, NULL, L"Ô", L"Ô" },
686 	{ NULL, NULL, L"Õ", L"Õ" },
687 	{ NULL, NULL, L"Ö", L"Ö" },
688 	{ NULL, NULL, L"×", L"×" },
689 	{ NULL, NULL, L"Ø", L"Ø" },
690 	{ NULL, NULL, L"Ù", L"Ù" },
691 	{ NULL, NULL, L"Ú", L"Ú" },
692 	{ NULL, NULL, L"Û", L"Û" },
693 	{ NULL, NULL, L"Ü", L"Ü" },
694 	{ NULL, NULL, L"Ý", L"Ý" },
695 	{ NULL, NULL, L"Þ", L"Þ" },
696 	{ NULL, NULL, L"ß", L"ß" },
697 /*e0*/	{ NULL, NULL, L"à", L"à" },
698 	{ NULL, NULL, L"á", L"á" },
699 	{ NULL, NULL, L"â", L"â" },
700 	{ NULL, NULL, L"ã", L"ã" },
701 	{ NULL, NULL, L"ä", L"ä" },
702 	{ NULL, NULL, L"å", L"å" },
703 	{ NULL, NULL, L"æ", L"æ" },
704 	{ NULL, NULL, L"ç", L"ç" },
705 	{ NULL, NULL, L"è", L"è" },
706 	{ NULL, NULL, L"é", L"é" },
707 	{ NULL, NULL, L"ê", L"ê" },
708 	{ NULL, NULL, L"ë", L"ë" },
709 	{ NULL, NULL, L"ì", L"ì" },
710 	{ NULL, NULL, L"í", L"í" },
711 	{ NULL, NULL, L"î", L"î" },
712 	{ NULL, NULL, L"ï", L"ï" },
713 /*f0*/	{ NULL, NULL, L"ð", L"ð" },
714 	{ NULL, NULL, L"ñ", L"ñ" },
715 	{ NULL, NULL, L"ò", L"ò" },
716 	{ NULL, NULL, L"ó", L"ó" },
717 	{ NULL, NULL, L"ô", L"ô" },
718 	{ NULL, NULL, L"õ", L"õ" },
719 	{ NULL, NULL, L"ö", L"ö" },
720 	{ NULL, NULL, L"÷", L"÷" },
721 	{ NULL, NULL, L"ø", L"ø" },
722 	{ NULL, NULL, L"ù", L"ù" },
723 	{ NULL, NULL, L"ú", L"ú" },
724 	{ NULL, NULL, L"û", L"û" },
725 	{ NULL, NULL, L"ü", L"ü" },
726 	{ NULL, NULL, L"ý", L"ý" },
727 	{ NULL, NULL, L"þ", L"þ" },
728 	{ NULL, NULL, L"ÿ", L"ÿ" }
729 #endif
730 };
731 
732 
733 /*
734  * three functions for common syntax errors in the configuration file
735  */
736 static void
pexpected(const char * s)737 pexpected(const char *s) {
738 	perr("%s(%d): %s expected", cfn, ln, s);
739 }
740 
741 
742 static void
pinvalid(const char * s)743 pinvalid(const char *s) {
744 	perr("%s(%d): invalid %s", cfn, ln, s);
745 }
746 
747 
748 static void
predefined(const char * s)749 predefined(const char *s) {
750 	perr("%s(%d): %s redefined", cfn, ln, s);
751 }
752 
753 
754 /*
755  * free the current token and its attributes
756  */
757 static void
free_token(void)758 free_token(void) {
759 	token = (-1);
760 	free(token_string);
761 	token_string = NULL;
762 	free(token_ident);
763 	token_ident = NULL;
764 }
765 
766 
767 /*
768  * read the next token from the configuration file and return it
769  * and ts attributes via static variables
770  */
771 static void
get_token(void)772 get_token(void) {
773 	const wchar_t *start_p;
774 	wchar_t *tp;
775 	size_t lb, ls;
776 	/*
777 	 * free the last token resp. its remains, set token to invalid (-1)
778 	 */
779 	free_token();
780 	/*
781 	 * skip blanks
782 	 */
783 	while (iswctype(*curr_p, wctype_blank)) curr_p++;
784 	/*
785 	 * test for EOL resp. start of a comment
786 	 */
787 	if (*curr_p == L'\0' || *curr_p == L'#' || *curr_p == L';') {
788 		/*
789 		 * 0 is the token for EOL
790 		 */
791 		token = 0;
792 	} else if (iswdigit(*curr_p)) {
793 		/*
794 		 * tokens starting in a decimal digit are numbers;
795 		 * hexadecimal (0x), octal (0), and decimal (1..9)
796 		 * numbers are accepted
797 		 */
798 		errno = 0;
799 		if (*curr_p == L'0') {
800 			if (*(curr_p + 1) == L'x') {
801 				token_ul = wcstoul(curr_p + 2, &tp, 16);
802 			} else {
803 				token_ul = wcstoul(curr_p, &tp, 8);
804 			}
805 		} else {
806 			token_ul = wcstoul(curr_p, &tp, 10);
807 		}
808 		/*
809 		 * check for numeric overflow
810 		 */
811 		if (token_ul == ULONG_MAX && errno == ERANGE) {
812 			perr("%s(%d): integer out of range", cfn, ln);
813 		} else {
814 			/*
815 			 * token is a number
816 			 */
817 			token = '0';
818 			curr_p = tp;
819 		}
820 	} else if (iswalpha(*curr_p)) {
821 		/*
822 		 * identifiers start with an alphabetic character...
823 		 */
824 		start_p = curr_p;
825 		/*
826 		 * and may contain alphabetic and numeric characters
827 		 * resp. the underscore character
828 		 */
829 		while (iswalnum(*curr_p) || *curr_p == L'_') curr_p++;
830 		ls = curr_p - start_p;
831 		token_ident = alloc(sizeof (wchar_t) * (ls + 1));
832 		memcpy(token_ident, start_p, sizeof (wchar_t) * ls);
833 		token_ident[ls] = L'\0';
834 		/*
835 		 * token is an identifier
836 		 */
837 		token = 'i';
838 	} else if (*curr_p == L'=') {
839 		/*
840 		 * token is a equal sign
841 		 */
842 		token = '=';
843 		curr_p++;
844 	} else if (*curr_p == L',') {
845 		/*
846 		 * token is a comma
847 		 */
848 		token = ',';
849 		curr_p++;
850 	} else if (*curr_p == L'\"') {
851 		/*
852 		 * strings are enclosed in double quotes and may contain
853 		 * graphical characters and escapes \", \', and \\
854 		 */
855 		lb = 0;
856 		ls = 0;
857 		curr_p++;
858 		for (;;) {
859 			if (*curr_p == L'\0') {
860 				perr("%s(%d): unterminated string", cfn, ln);
861 				free_token();
862 				break;
863 			}
864 			if (*curr_p == L'"') {
865 				curr_p++;
866 				token_string = resize(token_string,
867 				    (ls + 1) * sizeof (wchar_t));
868 				token_string[ls] = L'\0';
869 				token = 's';
870 				break;
871 			}
872 			if (*curr_p == L'\\') {
873 				curr_p++;
874 				if (*curr_p != L'\"' && *curr_p != L'\\'
875 				    && *curr_p != L'\'') {
876 				    	pinvalid("escape sequence");
877 					free_token();
878 					break;
879 				}
880 			}
881 			if (ls == lb) {
882 				token_string = resize(token_string,
883 				    (lb + 16) * sizeof (wchar_t));
884 				lb += 16;
885 			}
886 			token_string[ls++] = *curr_p++;
887 		}
888 	} else {
889 		pinvalid("token");
890 	}
891 }
892 
893 
894 /*
895  * translate drive letters 'a' ... 'p' to drive numbers 0 ... 15
896  */
897 static int
cpm_drive(wchar_t c)898 cpm_drive(wchar_t c) {
899 	int rc = (-1);
900 	wchar_t *cp;
901 	static const wchar_t drives[16] = L"abcdefghijklmnop";
902 	cp = wcschr(drives, towlower(c));
903 	if (cp) rc = cp - drives;
904 	return rc;
905 }
906 
907 
908 /*
909  * common syntax checks: is the next token a equal sign, a string,
910  * a keyword, or a number?
911  */
912 
913 #define CHECK(NAME, TOKEN, MESSAGE) \
914 static int \
915 check_##NAME(int *rc_p) { \
916 	if (token != TOKEN) { \
917 		pexpected(MESSAGE); \
918 		*rc_p = (-1); \
919 		return 0; \
920 	} \
921 	return 1; \
922 }
923 
924 CHECK(equal, '=', "=")
925 CHECK(string, 's', "string")
926 CHECK(keyword, 'i', "keyword")
927 CHECK(number, '0', "number")
928 
929 
930 /*
931  * string in character definition may contain only a single character
932  */
933 static int
check_char(int * rc_p)934 check_char(int *rc_p) {
935 	if (! check_string(rc_p)) return 0;
936 	if (wcslen(token_string) > 1) {
937 		perr("%s(%d): string may contain only one character", cfn, ln);
938 		*rc_p = (-1);
939 		return 0;
940 	}
941 	return 1;
942 }
943 
944 
945 /*
946  * set all undefined character positions of the primary or
947  * secondary character set from the given built-in character set
948  * (the built-in character sets have gaps as well)
949  */
950 static void
set_charset(enum charset cs,wchar_t * charset[256])951 set_charset(enum charset cs, wchar_t *charset[256]) {
952 	int i;
953 	size_t l;
954 	for (i = 0; i < 256; i++) {
955 		if (charset[i]) continue;
956 		if (! default_charset[i][cs]) continue;
957 		l = (wcslen(default_charset[i][cs]) + 1) * sizeof (wchar_t);
958 		charset[i] = alloc(l);
959 		memcpy(charset[i], default_charset[i][cs], l);
960 	}
961 }
962 
963 
964 /*
965  * parse a screen dimension definition; the dimension may be either "current"
966  * (resulting in (-1) being returned as value) or in the range defined
967  * by the parameters min and max
968  */
969 static int
parse_dim(const char * s,int min,int max,int * lines_p)970 parse_dim(const char *s, int min, int max, int *lines_p) {
971 	int rc = 0;
972 	get_token();
973 	if (! check_equal(&rc)) goto premature_exit;
974 	get_token();
975 	if (token == 'i' && ! wcscmp(token_ident, L"current")) {
976 		*lines_p = (-1);
977 	} else {
978 		if (! check_number(&rc)) goto premature_exit;
979 		if (token_ul < min || token_ul > max) {
980 			perr("%s(%d): %s number out of range (%d..%d)",
981 			    cfn, ln, s, min, max);
982 			rc = (-1);
983 			goto premature_exit;
984 		}
985 		*lines_p = (int) token_ul;
986 	}
987 	get_token();
988 premature_exit:
989 	return rc;
990 }
991 
992 
993 /*
994  * parse a boolean definition: valid values are the
995  * identifiers true or false
996  */
997 static int
parse_boolean(int * value_p)998 parse_boolean(int *value_p) {
999 	int rc = 0;
1000 	get_token();
1001 	if (! check_equal(&rc)) goto premature_exit;
1002 	get_token();
1003 	if (token == 'i' && ! wcscmp(token_ident, L"true")) {
1004 		*value_p = 1;
1005 	} else if (token == 'i' && ! wcscmp(token_ident, L"false")) {
1006 		*value_p = 0;
1007 	} else {
1008 		perr("%s(%d): boolean value expected", cfn, ln);
1009 		rc = (-1);
1010 		goto premature_exit;
1011 	}
1012 	get_token();
1013 premature_exit:
1014 	return rc;
1015 }
1016 
1017 
1018 /*
1019  * convert a wide character string from the configuration file to
1020  * a multi-byte string
1021  */
1022 static int
unix_path(const wchar_t * wcs,char ** pp)1023 unix_path(const wchar_t *wcs, char **pp) {
1024 	int rc = 0;
1025 	size_t l;
1026 	char *cp;
1027 	l = wcslen(wcs) * MB_LEN_MAX + 1;
1028 	cp = alloc(l);
1029 	l = wcstombs(cp, wcs, l);
1030 	if (l == (size_t) (-1)) {
1031 		free(cp);
1032 		cp = NULL;
1033 		rc = (-1);
1034 	} else {
1035 		cp = resize(cp, l + 1);
1036 	}
1037 	*pp = cp;
1038 	return rc;
1039 }
1040 
1041 
1042 /*
1043  * parse the configuration for one of the three CP/M character
1044  * devices LST, PUN, or RDR; all three can have both a path
1045  * and a mode (raw or translated)
1046  */
1047 static int
parse_aux(const char * s,char ** name_p,int * raw_p)1048 parse_aux(const char *s, char **name_p, int *raw_p) {
1049 	int rc = 0;
1050 	get_token();
1051 	if (! check_keyword(&rc)) goto premature_exit;
1052 	if (! wcscmp(token_ident, L"file")) {
1053 		/*
1054 		 * path definition of the corresponding Unix file
1055 		 */
1056 		if (*name_p) {
1057 			perr("%s(%d): %s file redefined", cfn, ln, s);
1058 			rc = (-1);
1059 			goto premature_exit;
1060 		}
1061 		get_token();
1062 		if (! check_equal(&rc)) goto premature_exit;
1063 		get_token();
1064 		if (! check_string(&rc)) goto premature_exit;
1065 		rc = unix_path(token_string, name_p);
1066 		if (rc) {
1067 			pinvalid("file name");
1068 			goto premature_exit;
1069 		}
1070 		get_token();
1071 	} else if (! wcscmp(token_ident, L"mode")) {
1072 		/*
1073 		 * the mode definition is one of the identifiers raw or text
1074 		 */
1075 		if (*raw_p != (-1)) {
1076 			perr("%s(%d): %s mode redefined", cfn, ln, s);
1077 			rc = (-1);
1078 			goto premature_exit;
1079 		}
1080 		get_token();
1081 		if (! check_equal(&rc)) goto premature_exit;
1082 		get_token();
1083 		if (! check_keyword(&rc)) goto premature_exit;
1084 		if (! wcscmp(token_ident, L"text")) {
1085 			*raw_p = 0;
1086 		} else if (! wcscmp(token_ident, L"raw")) {
1087 			*raw_p = 1;
1088 		} else {
1089 			pexpected("text or raw");
1090 			rc = (-1);
1091 			goto premature_exit;
1092 		}
1093 		get_token();
1094 	} else {
1095 		pexpected("file or mode");
1096 		rc = (-1);
1097 		goto premature_exit;
1098 	}
1099 premature_exit:
1100 	return rc;
1101 }
1102 
1103 
1104 /*
1105  * parse dump options form configuration file
1106  */
1107 static int
parse_dump(enum dump * dp)1108 parse_dump(enum dump *dp) {
1109 	int rc = 0;
1110 	get_token();
1111 	if (! check_equal(&rc)) goto premature_exit;
1112 	if (*dp) {
1113 	    	perr("%s(%d): dump options redefined", cfn, ln);
1114 		rc = (-1);
1115 		goto premature_exit;
1116 	}
1117 	do {
1118 		get_token();
1119 		if (token != 'i') {
1120 			pexpected("dump option");
1121 			rc = (-1);
1122 			goto premature_exit;
1123 		}
1124 		if (! wcscmp(token_ident, L"all")) {
1125 			*dp |= DUMP_ALL;
1126 		} else if (! wcscmp(token_ident, L"none")) {
1127 			*dp |= DUMP_NONE;
1128 		} else if (! wcscmp(token_ident, L"startup")) {
1129 			*dp |= DUMP_STARTUP;
1130 		} else if (! wcscmp(token_ident, L"signal")) {
1131 			*dp |= DUMP_SIGNAL;
1132 		} else if (! wcscmp(token_ident, L"exit")) {
1133 			*dp |= DUMP_EXIT;
1134 		} else if (! wcscmp(token_ident, L"error")) {
1135 			*dp |= DUMP_ERROR;
1136 		} else {
1137 			pexpected("dump option");
1138 			rc = (-1);
1139 			goto premature_exit;
1140 		}
1141 		get_token();
1142 	} while (token == ',');
1143 	/*
1144 	 * check for illegal combinations
1145 	 */
1146 	if (((*dp & DUMP_ALL) && (*dp & ~DUMP_ALL)) ||
1147 	    ((*dp & DUMP_NONE) && (*dp & ~DUMP_NONE)) ||
1148 	    ((*dp & DUMP_ERROR) && (*dp & DUMP_EXIT))) {
1149 	    	perr("%s(%d): illegal dump option combination", cfn, ln);
1150 		rc = (-1);
1151 		goto premature_exit;
1152 	}
1153 	/*
1154 	 * "all" is a macro
1155 	 */
1156 	if (*dp & DUMP_ALL) *dp |= DUMP_STARTUP | DUMP_EXIT | DUMP_SIGNAL;
1157 premature_exit:
1158 	return rc;
1159 }
1160 
1161 
1162 /*
1163  * parse color options line from configuration file:
1164  * "colors" "=" ( "false" | "true" ) [ "," <int 0..7> "," <int 0..7> ]
1165  */
1166 static int
parse_colors(int * colors_p,int * fg_p,int * bg_p)1167 parse_colors(int *colors_p, int *fg_p, int *bg_p) {
1168 	int rc = 0;
1169 	if (*colors_p != (-1)) {
1170 	    	perr("%s(%d): color options redefined", cfn, ln);
1171 		rc = (-1);
1172 		goto premature_exit;
1173 	}
1174 	/*
1175 	 * check if colors should be used
1176 	 */
1177 	rc = parse_boolean(colors_p);
1178 	if (rc) goto premature_exit;
1179 	if (token == ',') {
1180 		/*
1181 		 * default colors specified
1182 		 */
1183 		get_token();
1184 		/*
1185 		 * parse foreground color
1186 		 */
1187 		if (token != '0' || token_ul > 7) {
1188 			pexpected("foreground color number (0..7)");
1189 			rc = (-1);
1190 			goto premature_exit;
1191 		}
1192 		*fg_p = (int) token_ul;
1193 		get_token();
1194 		/*
1195 		 * check for separator
1196 		 */
1197 		if (token != ',') {
1198 			pexpected(",");
1199 			rc = (-1);
1200 			goto premature_exit;
1201 		}
1202 		get_token();
1203 		/*
1204 		 * parse background color
1205 		 */
1206 		if (token != '0' || token_ul > 7) {
1207 			pexpected("background color number (0..7)");
1208 			rc = (-1);
1209 			goto premature_exit;
1210 		}
1211 		*bg_p = (int) token_ul;
1212 		get_token();
1213 	}
1214 premature_exit:
1215 	return rc;
1216 }
1217 
1218 
1219 /*
1220  * read parameters from the configuration file; parameters already
1221  * defined on the command line take precedence
1222  */
1223 int
parse_config(void)1224 parse_config(void) {
1225 	int rc = 0, alt, n, drive_no, temp_altkeys = (-1),
1226 	    temp_dont_close = (-1), temp_interactive = (-1),
1227 	    temp_screen_delay = (-1), temp_default_drive = (-1),
1228 	    temp_reverse_bs_del = (-1), temp_delay_count = (-1),
1229 	    temp_delay_nanoseconds = (-1), temp_color = (-1),
1230 	    temp_foreground = (-1), temp_background = (-1);
1231 	enum dump temp_dump = 0;
1232 	wchar_t line[L_LINE];
1233 	size_t l;
1234 	enum charset default_cs[2] = { CS_NONE, CS_NONE };
1235 	wchar_t **cs;
1236 	enum log_level temp_log_level = LL_UNSET;
1237 	/*
1238 	 * get wctype("blank") once
1239 	 */
1240 	wctype_blank = wctype("blank");
1241 	/*
1242 	 * parse the file
1243 	 */
1244 	for (;;) {
1245 		/*
1246 		 * get next line from configuration file and initialize
1247 		 * the token scanner
1248 		 */
1249 		ln++;
1250 		if (! fgetws(line, L_LINE, cf)) {
1251 			if (ferror(cf)) {
1252 				perr("error reading %s: %s", cfn,
1253 				    strerror(errno));
1254 				rc = (-1);
1255 				goto premature_exit;
1256 			}
1257 			break;
1258 		}
1259 		l = wcslen(line) - 1;
1260 		if (line[l] != L'\n') {
1261 			perr("%s(%d): line too long", cfn, ln);
1262 			rc = (-1);
1263 			goto premature_exit;
1264 		}
1265 		line[l] = L'\0';
1266 		curr_p = line;
1267 		/*
1268 		 * get first token; skip empty lines and lines containing
1269 		 * only comments
1270 		 */
1271 		get_token();
1272 		if (! token) continue;
1273 		/*
1274 		 * if the first token is the identifier alt, it must be
1275 		 * followed by another keyword
1276 		 */
1277 		alt = (token == 'i') && (! wcscmp(token_ident, L"alt"));
1278 		if (alt) get_token();
1279 		if (! check_keyword(&rc)) continue;
1280 		if (! wcscmp(token_ident, L"charset")) {
1281 			/*
1282 			 * charset resp. alt charset: define a default for
1283 			 * characters not explicitly defined in the
1284 			 * configuration file
1285 			 */
1286 			if (default_cs[alt] != CS_NONE) {
1287 				predefined(alt ? "alt charset" : "charset");
1288 				rc = (-1);
1289 				continue;
1290 			}
1291 			get_token();
1292 			if (! check_equal(&rc)) continue;
1293 			get_token();
1294 			if (token != 'i') {
1295 				pexpected("charset name");
1296 				rc = (-1);
1297 				continue;
1298 			}
1299 			/*
1300 			 * valid options are vt52, ascii, latin1, and tnylpo
1301 			 */
1302 			if (! wcscmp(token_ident, L"vt52")) {
1303 				default_cs[alt] = CS_VT52;
1304 			} else if (! wcscmp(token_ident, L"ascii")) {
1305 				default_cs[alt] = CS_ASCII;
1306 			} else if (! wcscmp(token_ident, L"latin1")) {
1307 				default_cs[alt] = CS_LATIN1;
1308 			} else if (! wcscmp(token_ident, L"tnylpo")) {
1309 				default_cs[alt] = CS_TNYLPO;
1310 			} else {
1311 				pinvalid("charset name");
1312 				rc = (-1);
1313 				continue;
1314 			}
1315 			get_token();
1316 		} else if (! wcscmp(token_ident, L"char")) {
1317 			/*
1318 			 * char resp. alt char: explicitly define
1319 			 * a CP/M character
1320 			 */
1321 			get_token();
1322 			if (token != '0' || token_ul > 256) {
1323 				pexpected("number (0..255)");
1324 				rc = (-1);
1325 				continue;
1326 			}
1327 			n = (int) token_ul;
1328 			cs = (alt ? conf_alt_charset : conf_charset) + n;
1329 			if (*cs) {
1330 				predefined(alt ? "alt char" : "char");
1331 				rc = (-1);
1332 				continue;
1333 			}
1334 			get_token();
1335 			if (! check_equal(&rc)) continue;
1336 			/*
1337 			 * parameter must be a string of length 1
1338 			 */
1339 			get_token();
1340 			if (! check_char(&rc)) continue;
1341 			*cs = token_string;
1342 			token_string = NULL;
1343 			get_token();
1344 		} else if (alt) {
1345 			/*
1346 			 * all other keywords may not be prefixed by alt
1347 			 */
1348 			perr("%s(%d): keyword alt unexpected", cfn, ln);
1349 			rc = (-1);
1350 			continue;
1351 		} else if (! wcscmp(token_ident, L"cpu")) {
1352 			/*
1353 			 * specify CPU delay: an instruction count
1354 			 * and a number of nanoseconds, separated by a
1355 			 * comma
1356 			 */
1357 			get_token();
1358 			if (token != 'i' || wcscmp(token_ident, L"delay")) {
1359 				pexpected("delay");
1360 				rc = (-1);
1361 				continue;
1362 			}
1363 			if (temp_delay_count != (-1)) {
1364 				predefined("cpu delay");
1365 				rc = (-1);
1366 				continue;
1367 			}
1368 			get_token();
1369 			if (! check_equal(&rc)) continue;
1370 			get_token();
1371 			if (! check_number(&rc)) continue;
1372 			if (token_ul < 1 || token_ul > INT_MAX) {
1373 				perr("%s(%d): cpu delay count out of range",
1374 				    cfn, ln);
1375 				rc = (-1);
1376 				continue;
1377 			}
1378 			temp_delay_count = (int) token_ul;
1379 			get_token();
1380 			if (token != ',') {
1381 				pexpected(",");
1382 				rc = (-1);
1383 				continue;
1384 			}
1385 			get_token();
1386 			if (! check_number(&rc)) continue;
1387 			if (token_ul < 1 || token_ul > INT_MAX) {
1388 				perr("%s(%d): cpu delay nanoseconds out "
1389 				    "of range", cfn, ln);
1390 				rc = (-1);
1391 				continue;
1392 			}
1393 			temp_delay_nanoseconds = (int) token_ul;
1394 			get_token();
1395 		} else if (! wcscmp(token_ident, L"console")) {
1396 			/*
1397 			 * use emulated terminal (full) or line
1398 			 * orientated/batch console (line)?
1399 			 */
1400 			if (temp_interactive != (-1)) {
1401 				predefined("console");
1402 				rc = (-1);
1403 				continue;
1404 			}
1405 			get_token();
1406 			if (! check_equal(&rc)) continue;
1407 			get_token();
1408 			if (! check_keyword(&rc)) continue;
1409 			if (! wcscmp(token_ident, L"full")) {
1410 				temp_interactive = 1;
1411 			} else if (! wcscmp(token_ident, L"line")) {
1412 				temp_interactive = 0;
1413 			} else {
1414 				pexpected("full or line");
1415 				rc = (-1);
1416 				continue;
1417 			}
1418 			get_token();
1419 		} else if (! wcscmp(token_ident, L"unprintable")) {
1420 			/*
1421 			 * character to be used to represent an
1422 			 * undefined character written to the console
1423 			 */
1424 			if (conf_unprintable) {
1425 				predefined("unprintable char");
1426 				rc = (-1);
1427 				continue;
1428 			}
1429 			get_token();
1430 			if (! check_equal(&rc)) continue;
1431 			get_token();
1432 			if (! check_char(&rc)) continue;
1433 			conf_unprintable = token_string;
1434 			token_string = NULL;
1435 			get_token();
1436 		} else if (! wcscmp(token_ident, L"close")) {
1437 			/*
1438 			 * close files determines whether BDOS function 19
1439 			 * actually closes the corresponding Unix file
1440 			 * or not
1441 			 */
1442 			get_token();
1443 			if (token != 'i' || wcscmp(token_ident, L"files")) {
1444 				pexpected("files");
1445 				rc = (-1);
1446 				continue;
1447 			}
1448 			if (temp_dont_close != (-1)) {
1449 				predefined("close files");
1450 				rc = (-1);
1451 				continue;
1452 			}
1453 			if (parse_boolean(&temp_dont_close) == (-1)) {
1454 				rc = (-1);
1455 				continue;
1456 			}
1457 			temp_dont_close = ! temp_dont_close;
1458 
1459 		} else if (! wcscmp(token_ident, L"screen")) {
1460 			/*
1461 			 * define a delay in seconds between program
1462 			 * termination and resetting the terminal emulation
1463 			 * to allow reading of the final screen contents
1464 			 * (ncurses restores the previous screen contents
1465 			 * on exit if possible); a value of key  waits for
1466 			 * a keypress before resetting the terminal.
1467 			 */
1468 			get_token();
1469 			if (token != 'i' || wcscmp(token_ident, L"delay")) {
1470 				pexpected("delay");
1471 				rc = (-1);
1472 				continue;
1473 			}
1474 			if (temp_screen_delay != (-1)) {
1475 				predefined("screen delay");
1476 				rc = (-1);
1477 				continue;
1478 			}
1479 			get_token();
1480 			if (! check_equal(&rc)) continue;
1481 			get_token();
1482 			if (token == 'i' && ! wcscmp(token_ident, L"key")) {
1483 				temp_screen_delay = (-2);
1484 			} else {
1485 				if (! check_number(&rc)) continue;
1486 				if (token_ul > INT_MAX) {
1487 					perr("%s(%d): screen delay out of "
1488 					    "range", cfn, ln);
1489 					rc = (-1);
1490 					continue;
1491 				}
1492 				temp_screen_delay = (int) token_ul;
1493 			}
1494 			get_token();
1495 		} else if (! wcscmp(token_ident, L"application")) {
1496 			/*
1497 			 * application cursor determines whether the
1498 			 * terminal emulation returns WordStar cursor
1499 			 * movement codes instead of the VT52 cursor
1500 			 * key sequences
1501 			 */
1502 			get_token();
1503 			if (token != 'i' || wcscmp(token_ident, L"cursor")) {
1504 				pexpected("cursor");
1505 				rc = (-1);
1506 				continue;
1507 			}
1508 			if (temp_dont_close != (-1)) {
1509 				predefined("application cursor");
1510 				rc = (-1);
1511 				continue;
1512 			}
1513 			if (parse_boolean(&temp_altkeys) == (-1)) {
1514 				rc = (-1);
1515 				continue;
1516 			}
1517 		} else if (! wcscmp(token_ident, L"exchange")) {
1518 			/*
1519 			 * exchange delete exchanges delete and backspace
1520 			 * keys in full screen mode
1521 			 */
1522 			get_token();
1523 			if (token != 'i' || wcscmp(token_ident, L"delete")) {
1524 				pexpected("delete");
1525 				rc = (-1);
1526 				continue;
1527 			}
1528 			if (temp_reverse_bs_del != (-1)) {
1529 				predefined("exchange delete");
1530 				rc = (-1);
1531 				continue;
1532 			}
1533 			if (parse_boolean(&reverse_bs_del) == (-1)) {
1534 				rc = (-1);
1535 				continue;
1536 			}
1537 		} else if (! wcscmp(token_ident, L"default")) {
1538 			/*
1539 			 * defines the default drive
1540 			 */
1541 			get_token();
1542 			if (token != 'i' || wcscmp(token_ident, L"drive")) {
1543 				pexpected("drive");
1544 				rc = (-1);
1545 				continue;
1546 			}
1547 			get_token();
1548 			if (! check_equal(&rc)) continue;
1549 			get_token();
1550 			if (temp_default_drive != (-1)) {
1551 				predefined("default drive");
1552 				rc = (-1);
1553 				goto premature_exit;
1554 			}
1555 			if (token != 'i' || wcslen(token_ident) != 1) {
1556 				temp_default_drive = (-1);
1557 			} else {
1558 				temp_default_drive = cpm_drive(token_ident[0]);
1559 			}
1560 			if (temp_default_drive == (-1)) {
1561 				pinvalid("drive name");
1562 				rc = (-1);
1563 				continue;
1564 			}
1565 			get_token();
1566 		} else if (! wcscmp(token_ident, L"drive")) {
1567 			/*
1568 			 * defines one of the 16 CP/M disk drives A...P;
1569 			 * the drive name is a identifier a, b, c, ..., p;
1570 			 * the parameter value is a string containing a
1571 			 * Unix path, optionally preceded by the
1572 			 * identifier readonly and a comma
1573 			 */
1574 			get_token();
1575 			if (token != 'i' || wcslen(token_ident) != 1) {
1576 				drive_no = (-1);
1577 			} else {
1578 				drive_no = cpm_drive(token_ident[0]);
1579 			}
1580 			if (drive_no == (-1)) {
1581 				pinvalid("drive name");
1582 				rc = (-1);
1583 				continue;
1584 			}
1585 			if (conf_drives[drive_no]) {
1586 				predefined("drive");
1587 				rc = (-1);
1588 				continue;
1589 			}
1590 			get_token();
1591 			if (! check_equal(&rc)) continue;
1592 			get_token();
1593 			if (token == 'i') {
1594 				/*
1595 				 * read only?
1596 				 */
1597 				if (wcscmp(token_ident, L"readonly")) {
1598 					pexpected("string");
1599 					rc = (-1);
1600 					continue;
1601 				}
1602 				conf_readonly[drive_no] = 1;
1603 				get_token();
1604 				if (token != ',') {
1605 					pexpected(",");
1606 					rc = (-1);
1607 					continue;
1608 				}
1609 				get_token();
1610 			}
1611 			if (! check_string(&rc)) continue;
1612 			if (unix_path(token_string, conf_drives + drive_no)) {
1613 				pinvalid("file name");
1614 				rc = (-1);
1615 				continue;
1616 			}
1617 			get_token();
1618 		} else if (! wcscmp(token_ident, L"logfile")) {
1619 			/*
1620 			 * the parameter of logfile is a string
1621 			 * containing a Unix path
1622 			 */
1623 			get_token();
1624 			if (! check_equal(&rc)) continue;
1625 			get_token();
1626 			if (! check_string(&rc)) continue;
1627 			if (unix_path(token_string, &conf_log)) {
1628 				pinvalid("file name");
1629 				rc = (-1);
1630 				continue;
1631 			}
1632 			get_token();
1633 		} else if (! wcscmp(token_ident, L"loglevel")) {
1634 			/*
1635 			 * the parameter of loglevel is a number
1636 			 */
1637 			get_token();
1638 			if (! check_equal(&rc)) continue;
1639 			get_token();
1640 			if (! check_number(&rc)) continue;
1641 			if (token_ul >= LL_INVALID) {
1642 				perr("%s(%d): log level out of range",
1643 				    cfn, ln);
1644 				rc = (-1);
1645 				continue;
1646 			}
1647 			if (temp_log_level != LL_UNSET) {
1648 				predefined("log level");
1649 				rc = (-1);
1650 				continue;
1651 			}
1652 			temp_log_level = (enum log_level) token_ul;
1653 			get_token();
1654 		} else if (! wcscmp(token_ident, L"lines")) {
1655 			/*
1656 			 * number of lines used by the VT52 emulation
1657 			 */
1658 			if (parse_dim("line", MIN_LINES, MAX_LINES, &n)) {
1659 				rc = (-1);
1660 				continue;
1661 			}
1662 			if (! lines) lines = n;
1663 		} else if (! wcscmp(token_ident, L"columns")) {
1664 			/*
1665 			 * number of columns used by the VT52 emulation
1666 			 */
1667 			if (parse_dim("column", MIN_COLS, MAX_COLS, &n)) {
1668 				rc = (-1);
1669 				continue;
1670 			}
1671 			if (! cols) cols = n;
1672 		} else if (! wcscmp(token_ident, L"printer")) {
1673 			/*
1674 			 * printer file definition
1675 			 */
1676 			if (parse_aux("printer", &conf_printer,
1677 			    &conf_printer_raw)) {
1678 			    	rc = (-1);
1679 				continue;
1680 			}
1681 		} else if (! wcscmp(token_ident, L"punch")) {
1682 			/*
1683 			 * punch file definition
1684 			 */
1685 			if (parse_aux("punch", &conf_punch,
1686 			    &conf_punch_raw)) {
1687 			    	rc = (-1);
1688 				continue;
1689 			}
1690 		} else if (! wcscmp(token_ident, L"reader")) {
1691 			/*
1692 			 * reader file definition
1693 			 */
1694 			if (parse_aux("reader", &conf_reader,
1695 			    &conf_reader_raw)) {
1696 			    	rc = (-1);
1697 				continue;
1698 			}
1699 		} else if (! wcscmp(token_ident, L"dump")) {
1700 			/*
1701 			 * dump definition
1702 			 */
1703 			if (parse_dump(&temp_dump)) {
1704 				rc = (-1);
1705 				continue;
1706 			}
1707 		} else if (! wcscmp(token_ident, L"colors")) {
1708 			/*
1709 			 * color definition
1710 			 */
1711 			if (parse_colors(&temp_color, &temp_foreground,
1712 			    &temp_background)) {
1713 				rc = (-1);
1714 				continue;
1715 			}
1716 		}
1717 		if (token) {
1718 			perr("%s(%d): syntax error", cfn, ln);
1719 			rc = (-1);
1720 		}
1721 	}
1722 	/*
1723 	 * In case of a syntax or semantical error the parser skips
1724 	 * to the next line in order to check as much as possible
1725 	 * of the configuration file; if any errors occurred up to
1726 	 * this point, further processing of the configuration
1727 	 * is skipped.
1728 	 */
1729 	if (rc) goto premature_exit;
1730 	/*
1731 	 * take values from the configuration file only if
1732 	 * they have not already been defined on the command line
1733 	 */
1734 	if (log_level == LL_UNSET) log_level = temp_log_level;
1735 	if (dont_close == (-1)) dont_close = temp_dont_close;
1736 	if (altkeys == (-1)) altkeys = temp_altkeys;
1737 	if (reverse_bs_del == (-1)) reverse_bs_del = temp_reverse_bs_del;
1738 	if (screen_delay == (-1)) screen_delay = temp_screen_delay;
1739 	if (conf_interactive == (-1)) conf_interactive = temp_interactive;
1740 	if (default_drive == (-1)) default_drive = temp_default_drive;
1741 	if (conf_dump == 0) conf_dump = temp_dump;
1742 	if (conf_color == (-1)) conf_color = temp_color;
1743 	if (conf_foreground == (-1)) conf_foreground = temp_foreground;
1744 	if (conf_background == (-1)) conf_background = temp_background;
1745 	if (delay_count == (-1)) {
1746 		delay_count = temp_delay_count;
1747 		delay_nanoseconds = temp_delay_nanoseconds;
1748 	}
1749 	/*
1750 	 * characters and character sets cannot be specified on
1751 	 * the command line
1752 	 */
1753 	if (default_cs[0] != CS_NONE) {
1754 		set_charset(default_cs[0], conf_charset);
1755 	} else {
1756 		set_charset(CS_VT52, conf_charset);
1757 	}
1758 	if (default_cs[1] != CS_NONE) {
1759 		set_charset(default_cs[1], conf_alt_charset);
1760 	} else {
1761 		set_charset(CS_VT52, conf_alt_charset);
1762 	}
1763 premature_exit:
1764 	free_token();
1765 	return rc;
1766 }
1767 
1768 
1769 /*
1770  * read the optional configuration file
1771  */
read_config(char * fn)1772 int read_config(char *fn) {
1773 	int rc = 0;
1774 	const char *home;
1775 	/*
1776 	 * handle configuration file
1777 	 */
1778 	if (fn) {
1779 		/*
1780 		 * configuration file given on the command line
1781 		 */
1782 		/*
1783 		 * make configuration file name available to subprograms
1784 		 */
1785 		cfn = fn;
1786 		cf = fopen(cfn, "r");
1787 		if (! cf) {
1788 			perr("cannot open %s: %s", cfn, strerror(errno));
1789 			rc = (-1);
1790 			goto premature_exit;
1791 		}
1792 	} else {
1793 		/*
1794 		 * take the configuraton file in the current working directory,
1795 		 * if one exists
1796 		 */
1797 		cfn = "./.tnylpo.conf";
1798 		cf = fopen(cfn, "r");
1799 		if (! cf) {
1800 			/*
1801 			 * otherwise, use the configuration file from the
1802 			 * user's home directory, if present
1803 			 */
1804 			home = getenv("HOME");
1805 			if (home) {
1806 				cfn = alloc(strlen(home) + 14);
1807 				sprintf(cfn, "%s/.tnylpo.conf",home);
1808 				cf = fopen(cfn, "r");
1809 			}
1810 		}
1811 	}
1812 	/*
1813 	 * Even if there is no configuration file, this program
1814 	 * assumes a minimalistic working configuration: one CP/M
1815 	 * drive (A; the current Unix working directory); VT52
1816 	 * character set; actually close files; screen size 80x24; use
1817 	 * VT52 cursor keys (the last two assumptions only have
1818 	 * an effect if the user passes the -s option on the command
1819 	 * line, since the console is line orientated by default).
1820 	 */
1821 	if (cf) {
1822 		rc = parse_config();
1823 		if (rc) goto premature_exit;
1824 	} else {
1825 		/*
1826 		 * without a config file, assume the VT52 character set
1827 		 * both as primary and as secondary character set
1828 		 */
1829 		set_charset(CS_VT52, conf_charset);
1830 		set_charset(CS_VT52, conf_alt_charset);
1831 	}
1832 premature_exit:
1833 	if (cf) {
1834 		if (fclose(cf)) {
1835 			perr("cannot close %s: %s", cfn, strerror(errno));
1836 			rc = (-1);
1837 		}
1838 	}
1839 	return rc;
1840 }
1841