First Commit of my working state
[simh.git] / Ibm1130 / ibm1130_stddev.c
CommitLineData
196ba1fc
PH
1/* ibm1130_stddev.c: IBM 1130 standard I/O devices simulator\r
2\r
3 Based on the SIMH simulator package written by Robert M Supnik\r
4\r
5 Brian Knittel\r
6\r
7 Revision History:\r
8\r
9 2004.10.22 - Removed stub for xio_1134_papertape as it's now a supported device\r
10\r
11 2003.11.23 - Fixed bug in new routine "quotefix" that made sim crash\r
12 for all non-Windows builds :(\r
13\r
14 2003.06.15 - added output translation code to accomodate APL font\r
15 added input translation feature to assist emulation of 1130 console keyboard for APL\r
16 changes to console input and output IO emulation, fixed bugs exposed by APL interpreter\r
17\r
18 2002.09.13 - pulled 1132 printer out of this file into ibm1130_prt.c\r
19\r
20 * (C) Copyright 2002, Brian Knittel.\r
21 * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN\r
22 * RISK basis, there is no warranty of fitness for any purpose, and the rest of the\r
23 * usual yada-yada. Please keep this notice and the copyright in any distributions\r
24 * or modifications.\r
25 *\r
26 * This is not a supported product, but I welcome bug reports and fixes.\r
27 * Mail to simh@ibm1130.org\r
28 *\r
29 * Notes about overstrike mapping:\r
30 * The 1130 console printer used a Selectric typewriter element. The APL interpreter\r
31 * used overprinting to construct some APL symbols, for example, a round O overstruck]\r
32 * with | to get the greek phi. This doesn't accomodate a glass terminal! Instead,\r
33 * modern APL fonts have separate character codes for the complex characters. \r
34 * To have APL\1130 output appear correctly, we have to do three things:\r
35 *\r
36 * use simh's telnet feature to connect to the 1130 console stream\r
37 * have the telnet program use an APL font\r
38 * detect combinations of overstruck symbols, and generate the approrpiate alternate codes.\r
39 *\r
40 * There is a built-in table of font mappings and overstrike mappings, for the APLPLUS.TTF\r
41 * truetype font widely available on the Internet. An font descriptor file can be used\r
42 * to specify alternate mappings.\r
43 *\r
44 * The APL font codes and overstrike mapping can be enabled with the simh command\r
45 *\r
46 * set tto apl\r
47 *\r
48 * and disabled with\r
49 *\r
50 * set tto ascii (this is the default)\r
51 *\r
52 * APL also uses the red and black ribbon selection. The emulator will output\r
53 * ansi red/black foreground commands with the setting\r
54 *\r
55 * set tto ansi\r
56 *\r
57 * The codes can be disabled with\r
58 *\r
59 * set tto noansi (this is the default)\r
60 *\r
61 * Finally, when APL mode is active, the emulator does some input key translations\r
62 * to let the standard ASCII keyboard more closely match the physical layout of the\r
63 * 1130 console keyboard. The numeric and punctuation key don't have their\r
64 * traditional meaning under APL. The input mapping lets you use the APL keyboard\r
65 * layout shown in the APL documentation.\r
66 *\r
67 * The translations are:\r
68 * FROM\r
69 * ASCII Position on keyboard To 1130 Key APL interpretation\r
70 * ------------------------------------ --------------------------------\r
71 * [ (key to right of P) \r Enter left arrow\r
72 * ; (1st key to right of L) \b Backspace [\r
73 * ' (2nd key to right of L) ^U Erase Fld ]\r
74 * 2 (key above Q) @ @ up shift\r
75 * 3 (key above W) % % up right shift\r
76 * 4 (key above E) * * +\r
77 * 5 (key above R) < < multiply\r
78 * 8 (key above U) - - Return\r
79 * 9 (key above I) / / Backspace\r
80 * - (key above P) ^Q INT REQ ATTN\r
81 * Enter - - Return \r
82 * backsp / / Backspace\r
83 */\r
84\r
85#include "ibm1130_defs.h"\r
86#include <memory.h>\r
87\r
88/* #define DEBUG_CONSOLE */\r
89\r
90/* ---------------------------------------------------------------------------- */\r
91\r
92static void badio (char *dev)\r
93{\r
94/* the real 1130 just ignores attempts to use uninstalled devices. They get tested\r
95 * at times, so it's best to just be quiet about this\r
96 * printf("%s I/O is not yet supported", dev);\r
97 */\r
98}\r
99\r
100void xio_1231_optical (int32 addr, int32 func, int32 modify) {badio("optical mark");}\r
101void xio_system7 (int32 addr, int32 func, int32 modify) {badio("System 7");}\r
102\r
103/* ---------------------------------------------------------------------------- */\r
104\r
105#define MAX_OUTPUT_COLUMNS 100 /* width of 1130 console printer */\r
106#define MAX_OS_CHARS 4 /* maximum number of overstruck characters that can be mapped */\r
107#define MAX_OS_MAPPINGS 100 /* maximum number of overstrike mappings */\r
108\r
109typedef struct tag_os_map { /* os_map = overstrike mapping */\r
110 int ch; /* ch = output character */\r
111 int nin; /* nin = number of overstruck characters */\r
112 unsigned char inlist[MAX_OS_CHARS]; /* inlist = overstruck ASCII characters, sorted. NOT NULL TERMINATED */\r
113} OS_MAP;\r
114\r
115extern UNIT *sim_clock_queue;\r
116extern int cgi;\r
117\r
118static int32 tti_dsw = 0; /* device status words */\r
119static int32 tto_dsw = 0;\r
120 int32 con_dsw = 0;\r
121 \r
122static unsigned char conout_map[256]; /* 1130 console code to ASCII translation. 0 = undefined, 0xFF = IGNR_ = no output */\r
123static unsigned char conin_map[256]; /* input mapping */\r
124static int curcol = 0; /* current typewriter element column, leftmost = 0 */\r
125static int maxcol = 0; /* highest curcol seen in this output line */\r
126static unsigned char black_ribbon[30]; /* output escape sequence for black ribbon shift */\r
127static unsigned char red_ribbon[30]; /* output escape sequence for red ribbon shift */\r
128\r
129static OS_MAP os_buf[MAX_OUTPUT_COLUMNS]; /* current typewriter output line, holds character struck in each column */\r
130static OS_MAP os_map[MAX_OS_MAPPINGS]; /* overstrike mapping entries */\r
131static int n_os_mappings; /* number of overstrike mappings */\r
132\r
133static t_stat tti_svc(UNIT *uptr);\r
134static t_stat tto_svc(UNIT *uptr);\r
135static t_stat tti_reset(DEVICE *dptr);\r
136static t_stat tto_reset(DEVICE *dptr);\r
137\r
138static t_stat emit_conout_character(int ch);\r
139static t_stat map_conout_character(int ch);\r
140static void reset_mapping (void);\r
141static void set_conout_mapping(int32 flags);\r
142static t_stat validate_conout_mapping(UNIT *uptr, int32 match, char *cvptr, void *desc);\r
143static void set_default_mapping(int32 flags);\r
144static void finish_conout_mapping(int32 flags);\r
145static void strsort (int n, unsigned char *s); /* sorts an array of n characters */\r
146static int os_map_comp (OS_MAP *a, OS_MAP *b); /* compares two mapping entries */\r
147static t_stat font_cmd(int32 flag, char *cptr); /* handles font command */\r
148static void read_map_file(FILE *fd); /* reads a font map file */\r
149static t_bool str_match(char *str, char *keyword); /* keyword/string comparison */\r
150static char * handle_map_ansi_definition(char **pc); /* input line parsers for map file sections */\r
151static char * handle_map_input_definition(char **pc);\r
152static char * handle_map_output_definition(char **pc);\r
153static char * handle_map_overstrike_definition(char **pc);\r
154\r
155extern t_stat sim_poll_kbd(void);\r
156extern t_stat sim_wait_kbd(void);\r
157extern t_stat sim_putchar(int32 out);\r
158\r
159#define UNIT_V_CSET (UNIT_V_UF + 0) /* user flag: character set */\r
160#define UNIT_V_LOCKED (UNIT_V_UF + 2) /* user flag: keyboard locked */\r
161#define UNIT_V_ANSI (UNIT_V_UF + 3)\r
162\r
163#define CSET_ASCII (0u << UNIT_V_CSET)\r
164#define CSET_1130 (1u << UNIT_V_CSET)\r
165#define CSET_APL (2u << UNIT_V_CSET) \r
166#define CSET_MASK (3u << UNIT_V_CSET)\r
167#define ENABLE_ANSI (1u << UNIT_V_ANSI)\r
168\r
169#define KEYBOARD_LOCKED (1u << UNIT_V_LOCKED)\r
170\r
171#define IRQ_KEY 0x11 /* ctrl-Q */\r
172#define PROGRAM_STOP_KEY 0x10 /* ctrl-P */\r
173\r
174#include "ibm1130_conout.h" /* conout_to_ascii table */\r
175#include "ibm1130_conin.h" /* ascii_to_conin table */\r
176\r
177/* TTI data structures\r
178\r
179 tti_dev TTI device descriptor\r
180 tti_unit TTI unit descriptor\r
181 tti_reg TTI register list\r
182*/\r
183\r
184UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT };\r
185\r
186REG tti_reg[] = {\r
187 { ORDATA (BUF, tti_unit.buf, 16) },\r
188 { ORDATA (DSW, tti_dsw, 16) },\r
189 { DRDATA (POS, tti_unit.pos, 31), PV_LEFT },\r
190 { DRDATA (STIME, tti_unit.wait, 24), REG_NZ + PV_LEFT },\r
191 { NULL } };\r
192\r
193MTAB tti_mod[] = {\r
194 { CSET_MASK, CSET_ASCII, "ASCII", "ASCII", NULL},\r
195 { CSET_MASK, CSET_1130, "1130", "1130", NULL},\r
196 { 0 } };\r
197\r
198DEVICE tti_dev = {\r
199 "KEYBOARD", &tti_unit, tti_reg, tti_mod,\r
200 1, 10, 31, 1, 8, 8,\r
201 NULL, NULL, &tti_reset,\r
202 NULL, basic_attach, NULL };\r
203\r
204/* TTO data structures\r
205\r
206 tto_dev TTO device descriptor\r
207 tto_unit TTO unit descriptor\r
208 tto_reg TTO register list\r
209*/\r
210\r
211 /* 14-Nov-03 -- the wait time was SERIAL_OUT_WAIT, but recent versions of SIMH reduced\r
212 * this to 100, and wouldn't you know it, APL\1130 has about 120 instructions between the XIO WRITE\r
213 * to the console and the associated WAIT.\r
214 */\r
215\r
216UNIT tto_unit = { UDATA (&tto_svc, 0, 0), 200 };\r
217\r
218REG tto_reg[] = {\r
219 { ORDATA (BUF, tto_unit.buf, 16) },\r
220 { ORDATA (DSW, tto_dsw, 16) },\r
221 { DRDATA (POS, tto_unit.pos, 31), PV_LEFT },\r
222 { DRDATA (STIME, tto_unit.wait, 24), PV_LEFT },\r
223 { NULL } };\r
224\r
225MTAB tto_mod[] = {\r
226 { CSET_MASK, CSET_ASCII, "ASCII", "ASCII", validate_conout_mapping, NULL, NULL},\r
227 { CSET_MASK, CSET_1130, "1130", "1130", validate_conout_mapping, NULL, NULL},\r
228 { CSET_MASK, CSET_APL, "APL", "APL", validate_conout_mapping, NULL, NULL},\r
229 { ENABLE_ANSI,0, "NOANSI", "NOANSI", NULL},\r
230 { ENABLE_ANSI,ENABLE_ANSI, "ANSI", "ANSI", NULL},\r
231 { 0 } };\r
232\r
233DEVICE tto_dev = {\r
234 "TTO", &tto_unit, tto_reg, tto_mod,\r
235 1, 10, 31, 1, 8, 8,\r
236 NULL, NULL, &tto_reset,\r
237 NULL, basic_attach, NULL };\r
238\r
239/* Terminal input routines\r
240\r
241 tti_svc process event (character ready)\r
242 tti_reset process reset\r
243 tto_svc process event (print character)\r
244 tto_reset process reset\r
245*/\r
246\r
247#define TT_DSW_PRINTER_RESPONSE 0x8000\r
248#define TT_DSW_KEYBOARD_RESPONSE 0x4000\r
249#define TT_DSW_INTERRUPT_REQUEST 0x2000\r
250#define TT_DSW_KEYBOARD_CONSOLE 0x1000\r
251#define TT_DSW_PRINTER_BUSY 0x0800\r
252#define TT_DSW_PRINTER_NOT_READY 0x0400\r
253#define TT_DSW_KEYBOARD_BUSY 0x0200\r
254\r
255void xio_1131_console (int32 iocc_addr, int32 func, int32 modify)\r
256{\r
257 int ch;\r
258 char msg[80];\r
259\r
260 switch (func) {\r
261 case XIO_CONTROL:\r
262 SETBIT(tti_dsw, TT_DSW_KEYBOARD_BUSY); /* select and unlock the keyboard */\r
263 keyboard_selected(TRUE);\r
264 CLRBIT(tti_unit.flags, KEYBOARD_LOCKED);\r
265 tti_unit.buf = 0; /* no key character yet */\r
266 break;\r
267\r
268 case XIO_READ: \r
269 WriteW(iocc_addr, tti_unit.buf); /* return keycode */\r
270 CLRBIT(tti_dsw, TT_DSW_KEYBOARD_BUSY); /* this ends selected mode */\r
271 keyboard_selected(FALSE);\r
272 SETBIT(tti_unit.flags, KEYBOARD_LOCKED); /* keyboard is locked when not selected */\r
273 tti_unit.buf = 0; /* subsequent reads will return zero */\r
274 break;\r
275\r
276 case XIO_WRITE:\r
277 ch = (ReadW(iocc_addr) >> 8) & 0xFF; /* get character to write */\r
278 tto_unit.buf = emit_conout_character(ch); /* output character and save write status */\r
279\r
280/* fprintf(stderr, "[CONOUT] %02x\n", ch); */\r
281\r
282 SETBIT(tto_dsw, TT_DSW_PRINTER_BUSY);\r
283 sim_activate(&tto_unit, tto_unit.wait); /* schedule interrupt */\r
284 break;\r
285\r
286 case XIO_SENSE_DEV:\r
287 ACC = tto_dsw | tti_dsw;\r
288 if (modify & 0x01) { /* reset interrupts */\r
289 CLRBIT(tto_dsw, TT_DSW_PRINTER_RESPONSE);\r
290 CLRBIT(tti_dsw, TT_DSW_KEYBOARD_RESPONSE);\r
291 CLRBIT(tti_dsw, TT_DSW_INTERRUPT_REQUEST);\r
292 CLRBIT(ILSW[4], ILSW_4_CONSOLE);\r
293 }\r
294 break;\r
295\r
296 default:\r
297 sprintf(msg, "Invalid console XIO function %x", func);\r
298 xio_error(msg);\r
299 }\r
300\r
301/* fprintf(stderr, "After XIO %04x %04x\n", tti_dsw, tto_dsw); */\r
302}\r
303\r
304/* emit_conout_character - write character with 1130 console code 'ch' */\r
305\r
306t_stat emit_conout_character (int ch)\r
307{\r
308 t_stat status;\r
309\r
310#ifdef DEBUG_CONSOLE\r
311 printf("{%02x}", ch); \r
312#endif\r
313\r
314 if ((tto_unit.flags & CSET_MASK) == CSET_1130) /* 1130 (binary) mode, write the raw 8-bit value */\r
315 return sim_putchar(ch);\r
316\r
317 if (ch & COUT_IS_CTRL) {\r
318 /* red/black shift can be combined with another control */\r
319 /* if present, emit the color shift characters alone */\r
320\r
321 if (ch & COUT_CTRL_BLACK) { \r
322 if ((status = map_conout_character(COUT_IS_CTRL|COUT_CTRL_BLACK)) != SCPE_OK)\r
323 return status;\r
324 }\r
325 else if (ch & COUT_CTRL_RED) {\r
326 if ((status = map_conout_character(COUT_IS_CTRL|COUT_CTRL_RED)) != SCPE_OK)\r
327 return status;\r
328 }\r
329\r
330 ch &= ~(COUT_CTRL_BLACK|COUT_CTRL_RED); /* remove the ribbon shift bits */\r
331\r
332 if (ch & ~COUT_IS_CTRL) { /* if another control remains, emit it */\r
333 if ((status = map_conout_character(ch)) != SCPE_OK)\r
334 return status;\r
335 }\r
336\r
337 return SCPE_OK;\r
338 }\r
339\r
340 return map_conout_character(ch);\r
341}\r
342\r
343static void Beep (void) /* notify user keyboard was locked or key was bad */\r
344{\r
345 sim_putchar(7);\r
346}\r
347\r
348/* tti_svc - keyboard polling (never stops) */\r
349\r
350static t_stat tti_svc (UNIT *uptr)\r
351{\r
352 int32 temp;\r
353\r
354 if (cgi) /* if running in CGI mode, no keyboard and no keyboard polling! */\r
355 return SCPE_OK;\r
356 /* otherwise, so ^E can interrupt the simulator, */\r
357 sim_activate(&tti_unit, tti_unit.wait); /* always continue polling keyboard */\r
358\r
359 assert(sim_clock_queue != NULL);\r
360\r
361 temp = sim_poll_kbd();\r
362\r
363 if (temp < SCPE_KFLAG)\r
364 return temp; /* no char or error? */\r
365\r
366 temp &= 0xFF; /* remove SCPE_KFLAG */\r
367\r
368 if ((tti_unit.flags & CSET_MASK) == CSET_ASCII)\r
369 temp = conin_map[temp] & 0xFF; /* perform input translation */\r
370\r
371 if (temp == IRQ_KEY) { /* INT REQ (interrupt request) key */\r
372 SETBIT(tti_dsw, TT_DSW_INTERRUPT_REQUEST); /* queue interrupt */\r
373 SETBIT(ILSW[4], ILSW_4_CONSOLE);\r
374 calc_ints();\r
375\r
376 CLRBIT(tti_unit.flags, KEYBOARD_LOCKED); /* keyboard restore, according to func. char. manual */\r
377\r
378#ifdef DEBUG_CONSOLE\r
379 printf("[*IRQ*]");\r
380#endif\r
381 tti_unit.buf = 0; /* subsequent reads need to return 0 (required by APL\1130) */\r
382 return SCPE_OK;\r
383 }\r
384\r
385 if (temp == PROGRAM_STOP_KEY) { /* simulate the program stop button */\r
386 SETBIT(con_dsw, CPU_DSW_PROGRAM_STOP);\r
387 SETBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP);\r
388 calc_ints();\r
389\r
390#ifdef DEBUG_CONSOLE\r
391 printf("[*PSTOP*]");\r
392#endif\r
393\r
394 return SCPE_OK;\r
395 }\r
396\r
397 if ((tti_unit.flags & KEYBOARD_LOCKED) || ! (tti_dsw & TT_DSW_KEYBOARD_BUSY)) {\r
398 Beep();\r
399 return SCPE_OK;\r
400 }\r
401\r
402 if ((tti_unit.flags & CSET_MASK) == CSET_ASCII) \r
403 temp = ascii_to_conin[temp];\r
404\r
405 if (temp == 0) { /* ignore invalid characters */\r
406 Beep();\r
407 calc_ints();\r
408 return SCPE_OK;\r
409 }\r
410\r
411 tti_unit.buf = temp & 0xFFFE; /* save keystroke except last bit (not defined) */\r
412 tti_unit.pos = tti_unit.pos + 1; /* but it lets us distinguish 0 from no punch ' ' */\r
413\r
414#ifdef DEBUG_CONSOLE\r
415 printf("[%04x]", tti_unit.buf & 0xFFFF);\r
416#endif\r
417\r
418 SETBIT(tti_unit.flags, KEYBOARD_LOCKED); /* prevent further keystrokes */\r
419\r
420 SETBIT(tti_dsw, TT_DSW_KEYBOARD_RESPONSE); /* queue interrupt */\r
421 SETBIT(ILSW[4], ILSW_4_CONSOLE);\r
422 calc_ints();\r
423\r
424/* fprintf(stderr, "TTI interrupt svc SET %04x %04x\n", tti_dsw, tto_dsw); */\r
425\r
426 return SCPE_OK;\r
427}\r
428\r
429static t_stat tti_reset (DEVICE *dptr)\r
430{\r
431 tti_unit.buf = 0;\r
432 tti_dsw = 0;\r
433\r
434 CLRBIT(ILSW[4], ILSW_4_CONSOLE);\r
435 calc_ints();\r
436 keyboard_selected(FALSE);\r
437\r
438 SETBIT(tti_unit.flags, KEYBOARD_LOCKED);\r
439\r
440 if (cgi)\r
441 sim_cancel(&tti_unit); /* in cgi mode, never poll keyboard */\r
442 else\r
443 sim_activate(&tti_unit, tti_unit.wait); /* otherwise, always poll keyboard */\r
444\r
445 return SCPE_OK;\r
446}\r
447\r
448/* basic_attach - fix quotes in filename, then call standard unit attach routine */\r
449\r
450t_stat basic_attach (UNIT *uptr, char *cptr)\r
451{\r
452 return attach_unit(uptr, quotefix(cptr)); /* fix quotes in filenames & attach */\r
453}\r
454\r
455/* quotefix - strip off quotes around filename, if present */\r
456\r
457char * quotefix (char * cptr)\r
458{\r
459#ifdef WIN32 /* do this only for Windows builds, for the time being */\r
460 char *c;\r
461 int quote;\r
462\r
463 if (*cptr == '"' || *cptr == '\'') {\r
464 quote = *cptr++; /* remember quote and skip over it */\r
465\r
466 for (c = cptr; *c && *c != quote; c++)\r
467 ; /* find closing quote, or end of string */\r
468\r
469 if (*c) /* terminate string at closing quote */\r
470 *c = '\0';\r
471 }\r
472\r
473#endif\r
474 return cptr; /* return pointer to cleaned-up name */\r
475}\r
476\r
477t_bool keyboard_is_busy (void) /* return TRUE if keyboard is not expecting a character */\r
478{\r
479 return (tti_dsw & TT_DSW_KEYBOARD_BUSY);\r
480}\r
481\r
482static t_stat tto_svc (UNIT *uptr)\r
483{\r
484 CLRBIT(tto_dsw, TT_DSW_PRINTER_BUSY);\r
485 SETBIT(tto_dsw, TT_DSW_PRINTER_RESPONSE);\r
486\r
487 SETBIT(ILSW[4], ILSW_4_CONSOLE);\r
488 calc_ints();\r
489\r
490/* fprintf(stderr, "TTO interrupt svc SET %04x %04x\n", tti_dsw, tto_dsw); */\r
491\r
492 return (t_stat) tto_unit.buf; /* return status saved during output conversion */\r
493}\r
494\r
495static t_stat tto_reset (DEVICE *dptr)\r
496{\r
497 tto_unit.buf = 0;\r
498 tto_dsw = 0;\r
499\r
500 CLRBIT(ILSW[4], ILSW_4_CONSOLE);\r
501 calc_ints();\r
502\r
503 sim_cancel(&tto_unit); /* deactivate unit */\r
504\r
505 set_conout_mapping(tto_unit.flags); /* initialize the overstrike mappings */\r
506 /* register the font-mapping command */\r
507 register_cmd("FONT", font_cmd, 0, "font MAPFILE use font mapping definitions in MAPFILE\n");\r
508\r
509 return SCPE_OK;\r
510}\r
511\r
512#ifdef _MSC_VER\r
513# pragma warning(disable:4245) /* enable int->char demotion warning caused by characters with high-bit set */\r
514#endif\r
515\r
516static struct { /* default input mapping for APL */\r
517 unsigned char in;\r
518 unsigned char out;\r
519} conin_to_APL[] =\r
520{ /* these map input keys to those in like positions on 1130 keyboard */\r
521 '[', '\r', /* enter (EOF) is APL left arrow */\r
522 ';', '\b', /* backspace is APL [ */\r
523 '\'', '\x15', /* ctrl-U, erase field, is APL ]*/\r
524 '2', '@', /* APL upshift */\r
525 '3', '%', /* APL rightshift */\r
526 '4', '*', /* APL + and - */\r
527 '5', '<', /* APL x and divide */\r
528 '8', '-', /* APL return */\r
529 '9', '/', /* APL backspace */\r
530 '-', IRQ_KEY, /* ctrl-q (INT REQ), APL ATTN */\r
531 '\r', '-', /* APL return */\r
532 '\b', '/' /* APL backspace */\r
533};\r
534\r
535#define NCONIN_TO_APL (sizeof(conin_to_APL)/sizeof(conin_to_APL[0]))\r
536\r
537static struct { /* default output mapping for APLPLUS font */\r
538 unsigned char in;\r
539 unsigned char out;\r
540} conout_to_APL[] =\r
541{\r
542 '\x01', IGNR_, /* controls */\r
543 '\x03', '\n',\r
544 '\x05', IGNR_, /* (black and red are handled by ansi sequences) */\r
545 '\x09', IGNR_,\r
546 '\x11', '\b',\r
547 '\x21', ' ',\r
548 '\x41', '\t',\r
549 '\x81', CRLF_,\r
550\r
551 '\xC4', '\x30', /* (if you're curious, order here is position on APL typeball) */\r
552 '\xE4', '\x38',\r
553 '\xD4', '\x37',\r
554 '\xF4', '\x35',\r
555 '\xDC', '\x33',\r
556 '\xFC', '\x31',\r
557 '\xC2', '\x29',\r
558 '\xE2', '\x9F',\r
559 '\xD2', '\x89',\r
560 '\xF2', '\x88',\r
561 '\xDA', '\xAF',\r
562 '\xC6', '\x5E',\r
563 '\xE6', '\xAC',\r
564 '\xD6', '\x3E',\r
565 '\xF6', '\x3D',\r
566 '\xDE', '\x3C',\r
567 '\xFE', '\xA8',\r
568 '\xC0', '\x5D',\r
569 '\xE0', '\x39',\r
570 '\xD0', '\x36',\r
571 '\xF0', '\x34',\r
572 '\xD8', '\x32',\r
573\r
574 '\x84', '\x84',\r
575 '\xA4', '\x59',\r
576 '\x94', '\x58',\r
577 '\xB4', '\x56',\r
578 '\x9C', '\x54',\r
579 '\xBC', '\x2F',\r
580 '\x82', '\x3B',\r
581 '\xA2', '\x9B',\r
582 '\x92', '\xBE',\r
583 '\xB2', '\x87',\r
584 '\x9A', '\x97',\r
585 '\x86', '\x85',\r
586 '\xA6', '\x86',\r
587 '\x96', '\x9C',\r
588 '\xB6', '\x9E',\r
589 '\x9E', '\x7E',\r
590 '\xBE', '\x5C',\r
591 '\x80', '\x2C',\r
592 '\xA0', '\x5A',\r
593 '\x90', '\x57',\r
594 '\xB0', '\x55',\r
595 '\x98', '\x53',\r
596\r
597 '\x44', '\x2B',\r
598 '\x64', '\x51',\r
599 '\x54', '\x50',\r
600 '\x74', '\x4E',\r
601 '\x5C', '\x4C',\r
602 '\x7C', '\x4A',\r
603 '\x42', '\x28',\r
604 '\x62', '\xBD',\r
605 '\x52', '\xB1',\r
606 '\x72', '\x7C',\r
607 '\x5A', '\x27',\r
608 '\x46', '\x2D',\r
609 '\x66', '\x3F',\r
610 '\x56', '\x2A',\r
611 '\x76', '\x82',\r
612 '\x5E', '\x8C',\r
613 '\x7E', '\xB0',\r
614 '\x40', '\x5B',\r
615 '\x60', '\x52',\r
616 '\x50', '\x4F',\r
617 '\x70', '\x4D',\r
618 '\x58', '\x4B',\r
619\r
620 '\x04', '\xD7',\r
621 '\x24', '\x48',\r
622 '\x14', '\x47',\r
623 '\x34', '\x45',\r
624 '\x1C', '\x43',\r
625 '\x3C', '\x41',\r
626 '\x02', '\x3A',\r
627 '\x22', '\xBC',\r
628 '\x12', '\x5F',\r
629 '\x32', '\x98',\r
630 '\x1A', '\x83',\r
631 '\x06', '\xF7',\r
632 '\x26', '\x91',\r
633 '\x16', '\x92',\r
634 '\x36', '\xB9',\r
635 '\x1E', '\x9D',\r
636 '\x3E', '\xB8',\r
637 '\x00', '\x2E',\r
638 '\x20', '\x49',\r
639 '\x10', '\x46',\r
640 '\x30', '\x44',\r
641 '\x18', '\x42',\r
642};\r
643\r
644#define NCONOUT_TO_APL (sizeof(conout_to_APL)/sizeof(conout_to_APL[0]))\r
645\r
646static OS_MAP default_os_map[] = /* overstrike mapping for APLPLUS font */\r
647{\r
648 '\x8a', 2, "\x5e\x7e",\r
649 '\x8b', 2, "\x9f\x7e",\r
650 '\x8d', 2, "\x8c\x27",\r
651 '\x8e', 3, "\x8c\x2d\x3a",\r
652 '\x8f', 2, "\x91\x5f",\r
653 '\x90', 2, "\x92\x7e",\r
654 '\x93', 2, "\x91\x7c",\r
655 '\x94', 2, "\x92\x7c",\r
656 '\x95', 2, "\xb0\x82",\r
657 '\x96', 2, "\xb0\x83",\r
658 '\x99', 2, "\x2d\x5c",\r
659 '\x9a', 2, "\x2d\x2f",\r
660 '\xae', 2, "\x2c\x2d",\r
661 '\xb2', 2, "\xb1\x7c",\r
662 '\xb3', 2, "\xb1\x5c",\r
663 '\xb4', 2, "\xb1\x2d",\r
664 '\xb5', 2, "\xb1\x2a",\r
665 '\xba', 2, "\xb9\x5f",\r
666 '\xd0', 2, "\x30\x7e",\r
667 '\xd8', 2, "\x4f\x2f",\r
668 '\x21', 2, "\x27\x2e",\r
669 '\xa4', 2, "\xb0\xb1", /* map degree in circle to circle cross (APL uses this as character error symbol) */\r
670 '\xf0', 2, "\xb0\xa8",\r
671 '\xfe', 2, "\x3a\xa8",\r
672};\r
673\r
674#ifdef _MSC_VER\r
675# pragma warning(default:4245) /* enable int->char demotion warning */\r
676#endif\r
677\r
678/* os_map_comp - compare to OS_MAP entries */\r
679\r
680static int os_map_comp (OS_MAP *a, OS_MAP *b)\r
681{\r
682 unsigned char *sa, *sb;\r
683 int i;\r
684\r
685 if (a->nin > b->nin)\r
686 return +1;\r
687\r
688 if (a->nin < b->nin)\r
689 return -1;\r
690\r
691 sa = a->inlist;\r
692 sb = b->inlist;\r
693\r
694 for (i = a->nin; --i >= 0;) {\r
695 if (*sa > *sb)\r
696 return +1;\r
697\r
698 if (*sa < *sb)\r
699 return -1;\r
700\r
701 sa++;\r
702 sb++;\r
703 }\r
704\r
705 return 0;\r
706}\r
707\r
708/* strsort - sorts the n characters of array 's' using insertion sort */\r
709\r
710static void strsort (int n, unsigned char *s)\r
711{\r
712 unsigned char temp;\r
713 int i, big;\r
714\r
715 while (--n > 0) { /* repeatedly */\r
716 big = 0; /* find largest value of s[0]...s[n] */\r
717 for (i = 1; i <= n; i++)\r
718 if (s[i] > s[big]) big = i;\r
719\r
720 temp = s[n]; /* put largest value at end of array */\r
721 s[n] = s[big];\r
722 s[big] = temp;\r
723 } \r
724}\r
725\r
726/* file format:\r
727\r
728[font XXX] font named XXX\r
729OUT failure character\r
730OUT IN single character mapping\r
731OUT IN IN ... overstrike mapping\r
732\r
733*/\r
734\r
735static void set_conout_mapping (int32 flags)\r
736{\r
737 curcol = 0;\r
738 maxcol = 0;\r
739\r
740 /* set the default mappings. We may later override them with settings from an ini file */\r
741\r
742 set_default_mapping(flags);\r
743}\r
744\r
745/* finish_conout_mapping - sort the finalized overstrike mapping */\r
746\r
747static void finish_conout_mapping (int32 flags)\r
748{\r
749 int i, n, big;\r
750 OS_MAP temp;\r
751\r
752 for (i = 0; i < n_os_mappings; i++) /* sort the inlist strings individually */\r
753 strsort(os_map[i].nin, os_map[i].inlist);\r
754\r
755 for (n = n_os_mappings; --n > 0; ) { /* then sort the os_map array itself with insertion sort */\r
756 big = 0; /* find largest value of s[0]...s[n] */\r
757 for (i = 1; i <= n; i++)\r
758 if (os_map_comp(os_map+i, os_map+big) > 0) big = i;\r
759\r
760 if (big != n) {\r
761 temp = os_map[n]; /* put largest value at end of array */\r
762 os_map[n] = os_map[big];\r
763 os_map[big] = temp;\r
764 }\r
765 }\r
766}\r
767\r
768/* validate_conout_mapping - called when set command gets a new value */\r
769\r
770static t_stat validate_conout_mapping (UNIT *uptr, int32 match, char *cvptr, void *desc)\r
771{\r
772 set_conout_mapping(match);\r
773 return SCPE_OK;\r
774}\r
775\r
776static void reset_mapping (void)\r
777{\r
778 int i;\r
779\r
780 black_ribbon[0] = '\0'; /* erase the ribbon sequences */\r
781 red_ribbon[0] = '\0';\r
782\r
783 memset(conout_map, 0, sizeof(conout_map)); /* erase output mapping */\r
784\r
785 n_os_mappings = 0; /* erase overstrike mapping */\r
786\r
787 for (i = (sizeof(conin_map)/sizeof(conin_map[0])); --i >= 0; )\r
788 conin_map[i] = (unsigned char) i; /* default conin_map is identity map */\r
789}\r
790\r
791/* set_default_mapping - create standard font and overstrike map */\r
792\r
793static void set_default_mapping (int32 flags)\r
794{\r
795 int i;\r
796\r
797 reset_mapping();\r
798\r
799 strcpy((char *) black_ribbon, "\033[30m");\r
800 strcpy((char *) red_ribbon, "\033[31m");\r
801\r
802 switch (flags & CSET_MASK) {\r
803 case CSET_1130:\r
804 break;\r
805\r
806 case CSET_ASCII:\r
807 memcpy(conout_map, conout_to_ascii, sizeof(conout_to_ascii));\r
808 break;\r
809\r
810 case CSET_APL:\r
811 for (i = NCONOUT_TO_APL; --i >= 0; )\r
812 conout_map[conout_to_APL[i].in] = conout_to_APL[i].out;\r
813\r
814 for (i = NCONIN_TO_APL; --i >= 0; )\r
815 conin_map[conin_to_APL[i].in] = conin_to_APL[i].out;\r
816\r
817 memcpy(os_map, default_os_map, sizeof(default_os_map)); \r
818 n_os_mappings = (sizeof(default_os_map) / sizeof(default_os_map[0]));\r
819 break;\r
820 }\r
821\r
822 finish_conout_mapping(flags); /* sort conout mapping if necessary */\r
823}\r
824\r
825/* sim_putstr - write a string to the console */\r
826\r
827t_stat sim_putstr (char *s)\r
828{\r
829 t_stat status;\r
830\r
831 while (*s) {\r
832 if ((status = sim_putchar(*s)) != SCPE_OK)\r
833 return status;\r
834\r
835 s++;\r
836 }\r
837\r
838 return SCPE_OK;\r
839}\r
840\r
841/* map_conout_character - translate and write a single character */\r
842\r
843static t_stat map_conout_character (int ch)\r
844{\r
845 t_stat status;\r
846 int i, cmp;\r
847\r
848 if (ch == (COUT_IS_CTRL|COUT_CTRL_BLACK))\r
849 return (tto_unit.flags & ENABLE_ANSI) ? sim_putstr((char *) black_ribbon) : SCPE_OK;\r
850\r
851 if (ch == (COUT_IS_CTRL|COUT_CTRL_RED))\r
852 return (tto_unit.flags & ENABLE_ANSI) ? sim_putstr((char *) red_ribbon) : SCPE_OK;\r
853\r
854 if ((ch = conout_map[ch & 0xFF]) == 0)\r
855 ch = '?'; /* unknown character? print ? */\r
856\r
857 if (ch == '\n') { /* newline: reset overstrike buffer */\r
858 curcol = 0;\r
859 maxcol = -1;\r
860 }\r
861 else if (ch == '\r') { /* carriage return: rewind to column 0 */\r
862 curcol = 0;\r
863 maxcol = -1; /* assume it advances paper too */\r
864 }\r
865 else if (ch == '\b') { /* backspace: back up one character */\r
866 if (curcol > 0)\r
867 curcol--;\r
868 }\r
869 else if (n_os_mappings && ch != (unsigned char) IGNR_) {\r
870 if (curcol >= MAX_OUTPUT_COLUMNS)\r
871 map_conout_character('\x81'); /* precede with automatic carriage return/line feed, I guess */\r
872 \r
873 if (curcol > maxcol) { /* first time in this column, no overstrike possible yet */\r
874 os_buf[curcol].nin = 0;\r
875 maxcol = curcol;\r
876 }\r
877\r
878 if (ch != ' ' && ch != 0) { /* (if it's not a blank or unknown) */\r
879 os_buf[curcol].inlist[os_buf[curcol].nin] = (unsigned char) ch;\r
880 strsort(++os_buf[curcol].nin, os_buf[curcol].inlist);\r
881 }\r
882\r
883 if (os_buf[curcol].nin == 0) /* if nothing but blanks seen, */\r
884 ch = ' '; /* output is a blank */\r
885 else if (os_buf[curcol].nin == 1) { /* if only one printing character seen, display it */\r
886 ch = os_buf[curcol].inlist[0];\r
887 }\r
888 else { /* otherwise look up mapping */\r
889 ch = '?';\r
890\r
891 for (i = 0; i < n_os_mappings; i++) {\r
892 cmp = os_map_comp(&os_buf[curcol], &os_map[i]);\r
893 if (cmp == 0) { /* a hit */\r
894 ch = os_map[i].ch;\r
895 break;\r
896 }\r
897 else if (cmp < 0) /* not found */\r
898 break;\r
899 }\r
900 }\r
901\r
902 if (curcol < MAX_OUTPUT_COLUMNS) /* this should now never happen, as we automatically return */\r
903 curcol++;\r
904 }\r
905\r
906 switch (ch) {\r
907 case IGNR_:\r
908 break;\r
909\r
910 case CRLF_:\r
911 if (! cgi) {\r
912 if ((status = sim_putchar('\r')) != SCPE_OK)\r
913 return status;\r
914\r
915 tto_unit.pos++;\r
916 }\r
917\r
918 if ((status = sim_putchar('\n')) != SCPE_OK)\r
919 return status;\r
920\r
921 tto_unit.pos++; /* hmm, why do we count these? */\r
922 break;\r
923\r
924 default:\r
925 if ((status = sim_putchar(ch)) != SCPE_OK)\r
926 return status;\r
927\r
928 tto_unit.pos++;\r
929 break;\r
930 }\r
931\r
932 return SCPE_OK;\r
933}\r
934\r
935/* font_cmd - parse a font mapping file. Sets input and output translations */\r
936\r
937static t_stat font_cmd (int32 flag, char *cptr)\r
938{\r
939 char *fname, quote;\r
940 FILE *fd;\r
941\r
942 while (*cptr && (*cptr <= ' ')) cptr++; /* skip blanks */\r
943 if (! *cptr) return SCPE_2FARG; /* argument missing */\r
944\r
945 fname = cptr; /* save start */\r
946 if (*cptr == '\'' || *cptr == '"') { /* quoted string */\r
947 quote = *cptr++; /* remember quote character */\r
948 fname++; /* skip the quote */\r
949\r
950 while (*cptr && (*cptr != quote)) /* find closing quote */\r
951 cptr++;\r
952 }\r
953 else {\r
954 while (*cptr && (*cptr > ' ')) /* find terminating blank */\r
955 cptr++; \r
956 }\r
957 *cptr = '\0'; /* terminate name */\r
958\r
959 if ((fd = fopen(fname, "r")) == NULL)\r
960 return SCPE_OPENERR;\r
961\r
962 reset_mapping(); /* remove all default mappings */\r
963\r
964 read_map_file(fd);\r
965 fclose(fd);\r
966\r
967 finish_conout_mapping(tto_unit.flags);\r
968 return SCPE_OK;\r
969}\r
970\r
971/* str_match - compare the string str to the keyword, case insensitive */\r
972\r
973static t_bool str_match (char *str, char *keyword)\r
974{\r
975 char kch, sch;\r
976\r
977 while (*keyword) { /* see if str matches the keyword... */\r
978 kch = *keyword++; /* get pair of characters */\r
979 sch = *str++;\r
980\r
981 if (BETWEEN(kch, 'A', 'Z')) kch += 32; /* change upper to lower case */\r
982 if (BETWEEN(sch, 'A', 'Z')) sch += 32;\r
983\r
984 if (kch != sch) /* characters must match; if not, quit */\r
985 return FALSE;\r
986 }\r
987\r
988 return *str <= ' ' || *str == ';'; /* success if the input string ended or is in whitespace or comment */ \r
989}\r
990\r
991/* read_map_file - process definition lines in opened mapping file */\r
992\r
993static void read_map_file (FILE *fd)\r
994{\r
995 char str[256], *c, *errmsg;\r
996 int lineno = 0;\r
997 enum {SECT_UNDEFINED, SECT_DEFAULT, SECT_ANSI, SECT_INPUT, SECT_OUTPUT, SECT_OVERSTRIKE}\r
998 section = SECT_UNDEFINED;\r
999\r
1000 while (fgets(str, sizeof(str), fd) != NULL) {\r
1001 ++lineno; /* count input lines */\r
1002\r
1003 if ((c = strchr(str, '\n')) != NULL) /* terminate at newline */\r
1004 *c = '\0';\r
1005\r
1006 for (c = str; *c && *c <= ' '; c++) /* skip blanks */\r
1007 ;\r
1008\r
1009 if (c[0] == '\0' || c[0] == ';') /* ignore blank lines and lines starting with ; */\r
1010 continue;\r
1011\r
1012 if (*c == '[') {\r
1013 if (str_match(c, "[default]")) { /* check for section separators */\r
1014 set_default_mapping(tto_unit.flags);\r
1015 section = SECT_UNDEFINED;\r
1016 continue;\r
1017 }\r
1018 if (str_match(c, "[ansi]")) {\r
1019 section = SECT_ANSI;\r
1020 continue;\r
1021 }\r
1022 if (str_match(c, "[input]")) {\r
1023 section = SECT_INPUT;\r
1024 continue;\r
1025 }\r
1026 if (str_match(c, "[output]")) {\r
1027 section = SECT_OUTPUT;\r
1028 continue;\r
1029 }\r
1030 if (str_match(c, "[overstrike]")) {\r
1031 section = SECT_OVERSTRIKE;\r
1032 continue;\r
1033 }\r
1034 }\r
1035\r
1036 switch (section) { /* if we get here, we have a definition line */\r
1037 case SECT_ANSI:\r
1038 errmsg = handle_map_ansi_definition(&c);\r
1039 break;\r
1040 case SECT_INPUT:\r
1041 errmsg = handle_map_input_definition(&c);\r
1042 break;\r
1043 case SECT_OUTPUT:\r
1044 errmsg = handle_map_output_definition(&c);\r
1045 break;\r
1046 case SECT_OVERSTRIKE:\r
1047 errmsg = handle_map_overstrike_definition(&c);\r
1048 break;\r
1049 default:\r
1050 errmsg = "line occurs before valid [section]";\r
1051 break;\r
1052 }\r
1053\r
1054 if (errmsg == NULL) { /* if no other error detected, */\r
1055 while (*c && *c <= ' ') /* skip past any whitespace */\r
1056 c++;\r
1057\r
1058 if (*c && *c != ';') /* if line doesn't end or run into a comment, complain */\r
1059 errmsg = "too much stuff on input line";\r
1060 }\r
1061\r
1062 if (errmsg != NULL) { /* print error message and offending line */\r
1063 printf("* Warning: %s", errmsg);\r
1064\r
1065 switch (section) { /* add section name if possible */\r
1066 case SECT_ANSI: errmsg = "ansi"; break;\r
1067 case SECT_INPUT: errmsg = "input"; break;\r
1068 case SECT_OUTPUT: errmsg = "output"; break;\r
1069 case SECT_OVERSTRIKE: errmsg = "overstrike"; break;\r
1070 default: errmsg = NULL; break;\r
1071 }\r
1072 if (errmsg != NULL)\r
1073 printf(" in [%s] section", errmsg);\r
1074\r
1075 printf(", line %d\n%s\n", lineno, str);\r
1076 }\r
1077 }\r
1078}\r
1079\r
1080/* get_num_char - read an octal or hex character specification of exactly 'ndigits' digits\r
1081 * the input pointers is left pointing to the last character of the number, so that it\r
1082 * may be incremented by the caller\r
1083 */\r
1084\r
1085static char * get_num_char (char **pc, unsigned char *out, int ndigits, int base, char *errmsg)\r
1086{\r
1087 int ch = 0, digit;\r
1088 char *c = *pc;\r
1089\r
1090 while (--ndigits >= 0) { /* collect specified number of digits */\r
1091 if (BETWEEN(*c, '0', '9'))\r
1092 digit = *c - '0';\r
1093 else if (BETWEEN(*c, 'A', 'F'))\r
1094 digit = *c - 'A' + 10;\r
1095 else if (BETWEEN(*c, 'a', 'f'))\r
1096 digit = *c - 'a' + 10;\r
1097 else\r
1098 digit = base;\r
1099\r
1100 if (digit >= base) /* bad digit */\r
1101 return errmsg;\r
1102\r
1103 ch = ch * base + digit; /* accumulate digit */\r
1104 c++;\r
1105 }\r
1106\r
1107 *out = (unsigned char) ch; /* return parsed character */\r
1108 *pc = c-1; /* make input pointer point to last character seen */\r
1109 return NULL; /* no error */\r
1110}\r
1111\r
1112/* get_characters - read character specification(s) from input string pointed to \r
1113 * by *pc. Results stored in outstr; up to nmax characters parsed. Actual number\r
1114 * found returned in *nout. Returns NULL on success or error message if syntax\r
1115 * error encountered. *pc is advanced to next whitespace or whatever followed input.\r
1116 */\r
1117\r
1118static char * get_characters (char **pc, unsigned char *outstr, int nmax, int *nout)\r
1119{\r
1120 char *c = *pc, *errstr;\r
1121 unsigned char *out = outstr;\r
1122\r
1123 while (*c && *c <= ' ') /* skip leading whitespace */\r
1124 c++;\r
1125\r
1126 while (--nmax >= 0) { /* get up to maximum number of characters */\r
1127 if (*c == ';' || *c <= ' ') /* we ran into a comment, whitespace or end of string: we're done */\r
1128 break;\r
1129\r
1130 if (*c == '\\') { /* backslash escape of some sort */\r
1131 switch (*++c) {\r
1132 case 'b': /* backspace */\r
1133 case 'B':\r
1134 *out++ = '\b';\r
1135 break;\r
1136\r
1137 case 'e': /* ascii ESCAPE */\r
1138 case 'E':\r
1139 *out++ = '\033';\r
1140 break;\r
1141\r
1142 case 'f': /* formfeed */\r
1143 case 'F':\r
1144 *out++ = '\f';\r
1145 break;\r
1146\r
1147 case 'n': /* newline */\r
1148 case 'N':\r
1149 *out++ = '\n';\r
1150 break;\r
1151\r
1152 case 'r': /* return */\r
1153 case 'R':\r
1154 *out++ = '\r';\r
1155 break;\r
1156\r
1157 case 't': /* tab */\r
1158 case 'T':\r
1159 *out++ = '\t';\r
1160 break;\r
1161\r
1162 case 'x': /* hex specification */\r
1163 case 'X':\r
1164 c++;\r
1165 if ((errstr = get_num_char(&c, out, 2, 16, "bad hex character")) != NULL)\r
1166 return errstr;\r
1167\r
1168 out++; /* advance out pointer */\r
1169 break;\r
1170\r
1171 default: /* anything else */\r
1172 if (BETWEEN(*c, '0', '7')) { /* octal specification */\r
1173 if ((errstr = get_num_char(&c, out, 3, 8, "bad octal character")) != NULL)\r
1174 return errstr;\r
1175 \r
1176 out++; /* advance out pointer */\r
1177 }\r
1178 else if (BETWEEN(*c, 'A', 'Z') || BETWEEN(*c, 'a', 'z'))\r
1179 return "invalid \\ escape"; /* other \x letters are bad */\r
1180 else {\r
1181 *out++ = (unsigned char) *c;/* otherwise, accept \x as literal character x */\r
1182 }\r
1183 break;\r
1184 }\r
1185 }\r
1186 else if (*c == '^') { /* control character */\r
1187 c++;\r
1188 if (BETWEEN(*c, 'A', 'Z')) /* convert alpha, e.g. A -> 1 */\r
1189 *out++ = (unsigned char) (*c - 'A' + 1);\r
1190 else if (BETWEEN(*c, 'a', 'z'))\r
1191 *out++ = (unsigned char) (*c - 'z' + 1);\r
1192 else /* non alpha is bad */\r
1193 return "invalid control letter";\r
1194 }\r
1195 else if (str_match(c, "IGNORE")) { /* magic word: a character that will never be output */\r
1196 *out++ = (unsigned char) IGNR_;\r
1197 c += 6;\r
1198 }\r
1199 else {\r
1200 *out++ = (unsigned char) *c; /* save literal character */\r
1201 }\r
1202\r
1203 c++;\r
1204 }\r
1205\r
1206 if (*c && *c != ';' && *c > ' ') /* we should be at end of string, whitespace or comment */\r
1207 return "too many characters specified";\r
1208\r
1209 *pc = c; /* save advanced pointer */\r
1210 *nout = out-outstr; /* save number of characters stored */\r
1211\r
1212 return NULL; /* no error */\r
1213}\r
1214\r
1215/* handle_map_ansi_definition - process line in [ansi] section */\r
1216\r
1217static char * handle_map_ansi_definition (char **pc)\r
1218{\r
1219 unsigned char *outstr;\r
1220 char *errmsg;\r
1221 int n;\r
1222\r
1223 if (str_match(*pc, "black")) { /* find which string we're setting */\r
1224 outstr = black_ribbon; /* this is where we'll save the output string */\r
1225 *pc += 5; /* skip over the token */\r
1226 }\r
1227 else if (str_match(*pc, "red")) {\r
1228 outstr = red_ribbon;\r
1229 *pc += 3;\r
1230 }\r
1231 else\r
1232 return "invalid variable name";\r
1233 /* get list of characters */\r
1234 if ((errmsg = get_characters(pc, outstr, sizeof(black_ribbon)-1, &n)) != NULL)\r
1235 return errmsg;\r
1236\r
1237 outstr[n] = '\0'; /* null terminate the string */\r
1238\r
1239 return (n > 0) ? NULL : "missing output string"; /* NULL if OK, error msg if no characters */\r
1240}\r
1241\r
1242/* handle_map_input_definition - process line in [input] section */\r
1243\r
1244static char * handle_map_input_definition (char **pc)\r
1245{\r
1246 unsigned char cin, cout;\r
1247 char *errmsg;\r
1248 int n;\r
1249\r
1250 if ((errmsg = get_characters(pc, &cin, 1, &n)) != NULL) /* get input character */\r
1251 return errmsg;\r
1252\r
1253 if (n != 1)\r
1254 return "missing input character";\r
1255\r
1256 if ((errmsg = get_characters(pc, &cout, 1, &n)) != NULL) /* get output character */\r
1257 return errmsg;\r
1258\r
1259 if (n != 1)\r
1260 return "missing output character";\r
1261\r
1262 conin_map[cin] = cout; /* set the mapping */\r
1263 return NULL;\r
1264}\r
1265\r
1266/* handle_map_output_definition - process line in [output] section */\r
1267\r
1268static char * handle_map_output_definition (char **pc)\r
1269{\r
1270 unsigned char cin, cout;\r
1271 char *errmsg;\r
1272 int n;\r
1273\r
1274 if ((errmsg = get_characters(pc, &cin, 1, &n)) != NULL) /* get input character */\r
1275 return errmsg;\r
1276\r
1277 if (n != 1)\r
1278 return "missing input character";\r
1279\r
1280 if ((errmsg = get_characters(pc, &cout, 1, &n)) != NULL) /* get output character */\r
1281 return errmsg;\r
1282\r
1283 if (n != 1)\r
1284 return "missing output character";\r
1285\r
1286 conout_map[cin] = cout; /* set the mapping */\r
1287 return NULL;\r
1288}\r
1289\r
1290/* handle_map_overstrike_definition - process line in [overstrike] section */\r
1291\r
1292static char * handle_map_overstrike_definition (char **pc)\r
1293{\r
1294 unsigned char ch, inlist[MAX_OS_CHARS];\r
1295 char *errmsg;\r
1296 int nin;\r
1297\r
1298 if (n_os_mappings >= MAX_OS_MAPPINGS) /* os_map is full, no more room */\r
1299 return "too many overstrike mappings";\r
1300 /* get output character */\r
1301 if ((errmsg = get_characters(pc, &ch, 1, &nin)) != NULL)\r
1302 return errmsg;\r
1303\r
1304 if (nin != 1)\r
1305 return "missing output character";\r
1306 /* get input list */\r
1307 if ((errmsg = get_characters(pc, inlist, MAX_OS_CHARS, &nin)) != NULL)\r
1308 return errmsg;\r
1309\r
1310 if (nin < 2) /* expect at least two characters overprinted */\r
1311 return "missing input list";\r
1312\r
1313 os_map[n_os_mappings].ch = ch; /* save in next os_map slot */\r
1314 os_map[n_os_mappings].nin = nin;\r
1315 memmove(os_map[n_os_mappings].inlist, inlist, nin);\r
1316\r
1317 n_os_mappings++;\r
1318 return NULL;\r
1319}\r