Added a test for MUIA_Listview_SelectChange.
[AROS.git] / workbench / devs / printer / text.c
blob8c4ddec4395896c51979d3c9a75ac029821ba622
1 /*
2 * Copyright (C) 2012, The AROS Development Team. All rights reserved.
3 * Author: Jason S. McMullan <jason.mcmullan@gmail.com>
5 * Licensed under the AROS PUBLIC LICENSE (APL) Version 1.1
7 * Text printing
8 */
10 #include <string.h>
12 #include <aros/debug.h>
14 #include "printer_intern.h"
16 #define WBUFSIZE 256
19 #define FLUSH_BUFFER() \
20 do { \
21 if (blen > 0) { \
22 pd->pd_PWrite(buffer, blen); \
23 buffsel ^= 1; \
24 buffer = buff[buffsel]; \
25 blen = 0; \
26 } \
27 } while (0)
29 #define CSI '\033'
30 #define CSI_BRACK '['
31 #define CSI_BRACK_SEMI ';'
32 #define CSI_BRACK_QUOTE '"'
33 #define CSI_PAREN '('
34 #define CSI_HASH '#'
35 #define CSI_END -1
37 struct StateTable {
38 BYTE state;
39 UBYTE code;
40 BYTE parm;
41 BYTE cmd;
44 static const struct StateTable StateTable[] = {
45 { .state = 0, .code = 0, .parm = -1, .cmd = -1 },
46 { .state = CSI, .code = 'D', .parm = -1, .cmd = aIND },
47 { .state = CSI, .code = 'E', .parm = -1, .cmd = aNEL },
48 { .state = CSI, .code = 'H', .parm = -1, .cmd = aHTS },
49 { .state = CSI, .code = 'J', .parm = -1, .cmd = aVTS },
50 { .state = CSI, .code = 'K', .parm = -1, .cmd = aPLD },
51 { .state = CSI, .code = 'L', .parm = -1, .cmd = aPLU },
52 { .state = CSI, .code = 'M', .parm = -1, .cmd = aRI },
53 { .state = CSI, .code = 'c', .parm = -1, .cmd = aRIS },
54 { .state = CSI_HASH, .code = '0', .parm = -1, .cmd = aRMS },
55 { .state = CSI_HASH, .code = '1', .parm = -1, .cmd = aRIN },
56 { .state = CSI_HASH, .code = '2', .parm = -1, .cmd = aBMS },
57 { .state = CSI_HASH, .code = '3', .parm = -1, .cmd = aCAM },
58 { .state = CSI_HASH, .code = '4', .parm = -1, .cmd = aTBCALL },
59 { .state = CSI_HASH, .code = '5', .parm = -1, .cmd = aTBSALL },
60 { .state = CSI_HASH, .code = '8', .parm = -1, .cmd = aTMS },
61 { .state = CSI_HASH, .code = '9', .parm = -1, .cmd = aLMS },
62 { .state = CSI_PAREN, .code = '6', .parm = -1, .cmd = aFNT9 },
63 { .state = CSI_PAREN, .code = 'A', .parm = -1, .cmd = aFNT3 },
64 { .state = CSI_PAREN, .code = 'B', .parm = -1, .cmd = aFNT0 },
65 { .state = CSI_PAREN, .code = 'C', .parm = -1, .cmd = aFNT10 },
66 { .state = CSI_PAREN, .code = 'E', .parm = -1, .cmd = aFNT4 },
67 { .state = CSI_PAREN, .code = 'H', .parm = -1, .cmd = aFNT5 },
68 { .state = CSI_PAREN, .code = 'J', .parm = -1, .cmd = aFNT8 },
69 { .state = CSI_PAREN, .code = 'K', .parm = -1, .cmd = aFNT2 },
70 { .state = CSI_PAREN, .code = 'R', .parm = -1, .cmd = aFNT1 },
71 { .state = CSI_PAREN, .code = 'Y', .parm = -1, .cmd = aFNT6 },
72 { .state = CSI_PAREN, .code = 'Z', .parm = -1, .cmd = aFNT7 },
73 { .state = CSI_BRACK, .code = 'E', .parm = -1, .cmd = aTSS },
74 { .state = CSI_BRACK, .code = 'F', .parm = 0, .cmd = aJFY0 },
75 { .state = CSI_BRACK, .code = 'F', .parm = 1, .cmd = aJFY1 },
76 { .state = CSI_BRACK, .code = 'F', .parm = 3, .cmd = aJFY3 },
77 { .state = CSI_BRACK, .code = 'F', .parm = 5, .cmd = aJFY5 },
78 { .state = CSI_BRACK, .code = 'F', .parm = 6, .cmd = aJFY6 },
79 { .state = CSI_BRACK, .code = 'F', .parm = 7, .cmd = aJFY7 },
80 { .state = CSI_BRACK, .code = 'g', .parm = 0, .cmd = aTBC0 },
81 { .state = CSI_BRACK, .code = 'g', .parm = 1, .cmd = aTBC1 },
82 { .state = CSI_BRACK, .code = 'g', .parm = 3, .cmd = aTBC3 },
83 { .state = CSI_BRACK, .code = 'g', .parm = 4, .cmd = aTBC4 },
84 { .state = CSI_BRACK, .code = 'm', .parm = 0, .cmd = aSGR0 },
85 { .state = CSI_BRACK, .code = 'm', .parm = 1, .cmd = aSGR1 },
86 { .state = CSI_BRACK, .code = 'm', .parm = 22, .cmd = aSGR22 },
87 { .state = CSI_BRACK, .code = 'm', .parm = 23, .cmd = aSGR23 },
88 { .state = CSI_BRACK, .code = 'm', .parm = 24, .cmd = aSGR24 },
89 { .state = CSI_BRACK, .code = 'm', .parm = 3, .cmd = aSGR3 },
90 { .state = CSI_BRACK, .code = 'm', .parm = 30, .cmd = aSFC },
91 { .state = CSI_BRACK, .code = 'm', .parm = 31, .cmd = aSFC },
92 { .state = CSI_BRACK, .code = 'm', .parm = 32, .cmd = aSFC },
93 { .state = CSI_BRACK, .code = 'm', .parm = 33, .cmd = aSFC },
94 { .state = CSI_BRACK, .code = 'm', .parm = 34, .cmd = aSFC },
95 { .state = CSI_BRACK, .code = 'm', .parm = 35, .cmd = aSFC },
96 { .state = CSI_BRACK, .code = 'm', .parm = 36, .cmd = aSFC },
97 { .state = CSI_BRACK, .code = 'm', .parm = 37, .cmd = aSFC },
98 { .state = CSI_BRACK, .code = 'm', .parm = 38, .cmd = aSFC },
99 { .state = CSI_BRACK, .code = 'm', .parm = 39, .cmd = aSFC },
100 { .state = CSI_BRACK, .code = 'm', .parm = 4, .cmd = aSGR4 },
101 { .state = CSI_BRACK, .code = 'm', .parm = 40, .cmd = aSBC },
102 { .state = CSI_BRACK, .code = 'm', .parm = 41, .cmd = aSBC },
103 { .state = CSI_BRACK, .code = 'm', .parm = 42, .cmd = aSBC },
104 { .state = CSI_BRACK, .code = 'm', .parm = 43, .cmd = aSBC },
105 { .state = CSI_BRACK, .code = 'm', .parm = 44, .cmd = aSBC },
106 { .state = CSI_BRACK, .code = 'm', .parm = 45, .cmd = aSBC },
107 { .state = CSI_BRACK, .code = 'm', .parm = 46, .cmd = aSBC },
108 { .state = CSI_BRACK, .code = 'm', .parm = 47, .cmd = aSBC },
109 { .state = CSI_BRACK, .code = 'm', .parm = 48, .cmd = aSBC },
110 { .state = CSI_BRACK, .code = 'm', .parm = 49, .cmd = aSBC },
111 { .state = CSI_BRACK, .code = 'p', .parm = 0, .cmd = aPROP0 },
112 { .state = CSI_BRACK, .code = 'p', .parm = 1, .cmd = aPROP1 },
113 { .state = CSI_BRACK, .code = 'p', .parm = 2, .cmd = aPROP2 },
114 { .state = CSI_BRACK, .code = 'q', .parm = -1, .cmd = aPERF },
115 { .state = CSI_BRACK, .code = 'q', .parm = 0, .cmd = aPERF0 },
116 { .state = CSI_BRACK, .code = 't', .parm = -1, .cmd = aSLPP },
117 { .state = CSI_BRACK, .code = 'v', .parm = 0, .cmd = aSUS0 },
118 { .state = CSI_BRACK, .code = 'v', .parm = 1, .cmd = aSUS1 },
119 { .state = CSI_BRACK, .code = 'v', .parm = 2, .cmd = aSUS2 },
120 { .state = CSI_BRACK, .code = 'v', .parm = 3, .cmd = aSUS3 },
121 { .state = CSI_BRACK, .code = 'v', .parm = 4, .cmd = aSUS4 },
122 { .state = CSI_BRACK, .code = 'w', .parm = 0, .cmd = aSHORP0 },
123 { .state = CSI_BRACK, .code = 'w', .parm = 1, .cmd = aSHORP1 },
124 { .state = CSI_BRACK, .code = 'w', .parm = 2, .cmd = aSHORP2 },
125 { .state = CSI_BRACK, .code = 'w', .parm = 3, .cmd = aSHORP3 },
126 { .state = CSI_BRACK, .code = 'w', .parm = 4, .cmd = aSHORP4 },
127 { .state = CSI_BRACK, .code = 'w', .parm = 5, .cmd = aSHORP5 },
128 { .state = CSI_BRACK, .code = 'w', .parm = 6, .cmd = aSHORP6 },
129 { .state = CSI_BRACK, .code = 'z', .parm = 0, .cmd = aVERP0 },
130 { .state = CSI_BRACK, .code = 'z', .parm = 1, .cmd = aVERP1 },
131 { .state = CSI_BRACK_QUOTE, .code = 'r', .parm = -1, .cmd = aRAW },
132 { .state = CSI_BRACK_QUOTE, .code = 'x', .parm = -1, .cmd = aEXTEND },
133 { .state = CSI_BRACK_QUOTE, .code = 'z', .parm = 1, .cmd = aDEN1 },
134 { .state = CSI_BRACK_QUOTE, .code = 'z', .parm = 2, .cmd = aDEN2 },
135 { .state = CSI_BRACK_QUOTE, .code = 'z', .parm = 3, .cmd = aDEN3 },
136 { .state = CSI_BRACK_QUOTE, .code = 'z', .parm = 4, .cmd = aDEN4 },
137 { .state = CSI_BRACK_QUOTE, .code = 'z', .parm = 5, .cmd = aDEN5 },
138 { .state = CSI_BRACK_QUOTE, .code = 'z', .parm = 6, .cmd = aDEN6 },
139 { .state = CSI_BRACK_SEMI, .code = 'r', .parm = -1, .cmd = aSTBM },
140 { .state = CSI_BRACK_SEMI, .code = 's', .parm = -1, .cmd = aSLRM },
141 { .state = CSI_END },
144 static inline int isdigit(int x)
146 return (x >= '0' && x <= '9');
150 LONG Printer_Text_Command(struct PrinterData *pd, UWORD command, UBYTE p0, UBYTE p1, UBYTE p2, UBYTE p3)
152 struct PrinterExtendedData *ped = &pd->pd_SegmentData->ps_PED;
153 struct PrinterUnitText *txt = &pd->pd_PUnit->pu_Text;
154 CONST_STRPTR *Commands = pd->pd_Device.dd_CmdVectors;
155 LONG CommandMax = pd->pd_Device.dd_NumCommands;
156 /* Use pd_Stack as the command buffer */
157 /* pd_OldStk[8..11] is used for the 'TPMATCHWORD' magic
159 UBYTE *buff[2] = { (UBYTE *)&pd->pd_Stk[0], (UBYTE *)&pd->pd_OldStk[12] };
160 int buffsel = 0;
161 UBYTE *buffer = buff[buffsel];
162 LONG buffmax = ((P_STKSIZE > (P_OLDSTKSIZE-12)) ? (P_OLDSTKSIZE-12) : P_STKSIZE)/2;
163 CONST_STRPTR cmd;
164 LONG blen = 0;
165 UBYTE parm[4] = { p0, p1, p2, p3 };
167 if (command >= CommandMax)
168 return -1;
170 cmd = Commands[command];
172 D(bug("%s: cmd=%d(%s), (%d, %d, %d, %d)\n", __func__, command, cmd, p0, p1, p2, p3));
174 if (cmd == NULL)
175 return -1;
177 if (command == aRIN || command == aRIS) {
178 txt->pt_CurrentLine = 0;
179 txt->pt_CRLF = 0;
180 txt->pt_Spacing = pd->pd_Preferences.PrintSpacing;
183 for (; *cmd; cmd++) {
184 D(bug("%s: command=%d '%c'(%d), ped_DoSpecial=%p \n", __func__, command, *cmd, *cmd, ped->ped_DoSpecial));
186 if ((TEXT)*cmd == (TEXT)'\377' && ped->ped_DoSpecial) {
187 LONG err;
189 if ((TEXT)cmd[1] == (TEXT)'\377') {
190 /* NOTE: From printer.device 44.12: flush before calling DoSpecial */
191 pd->pd_PBothReady();
192 cmd++;
194 err = ped->ped_DoSpecial(&command, &buffer[blen], &txt->pt_CurrentLine, &txt->pt_Spacing, &txt->pt_CRLF, parm);
195 D(bug("%s: ped_DoSpecial: cmd => %d, buf='%s', err=%d\n", __func__, command, &buffer[blen], err));
196 if (err > 0) {
197 blen += err;
198 } else if (err == -2) {
199 blen += 0;
200 } else if (err == 0) {
201 buffer[blen++] = *cmd;
202 } else {
203 return err;
205 } else {
206 buffer[blen++] = *cmd;
209 if (blen >= buffmax) {
210 FLUSH_BUFFER();
213 FLUSH_BUFFER();
215 return 0;
218 LONG noopConvFunc(UBYTE *buf, UBYTE c, LONG crlf_flag)
220 return -1;
223 /* Returns:
224 * -1 if not in a command string
225 * -2 if processing a possible command,
226 * >= 0 if a command found
228 static LONG doState(UBYTE *state, UBYTE c, UBYTE *parm, UBYTE *parm_index)
230 const struct StateTable *loc = &StateTable[*state];
231 BYTE currstate = loc->state;
233 if (currstate == 0) {
234 if (c != CSI)
235 return -1;
237 parm[0] = 0;
238 *state = 1;
239 return -2;
242 if (currstate != 0 && currstate != CSI && currstate != CSI_HASH) {
243 if (isdigit(c)) {
244 UWORD p = parm[*parm_index];
246 p = (p * 10) + (c - '0');
247 if (p > 100) {
248 *state = 0;
249 return -1;
251 parm[*parm_index] = (UBYTE)p;
252 return -2;
256 /* State promotion */
257 if ((currstate == CSI && (c == CSI_HASH || c == CSI_PAREN || c == CSI_BRACK)) ||
258 (currstate == CSI_BRACK && (c == CSI_BRACK_SEMI || c == CSI_BRACK_QUOTE))) {
259 /* Advance to next matching state */
260 for (; loc->state != CSI_END && loc->state != c; loc++, (*state)++);
261 if (loc->state == CSI_END) {
262 *state = 0;
263 return -1;
266 if (c == CSI_BRACK_SEMI) {
267 parm[++(*parm_index)]=0;
269 return -2;
272 /* Code search */
273 for (; loc->state == currstate && loc->code != c; loc++, (*state)++);
275 if (loc->state != currstate) {
276 *state = 0;
277 return -1;
280 /* Parm search */
281 for (; loc->state == currstate; loc++, (*state)++) {
282 if ((loc->parm == -1) || (loc->parm == parm[0])) {
283 *state = 0;
284 return loc->cmd;
288 /* No match */
289 *state = 0;
290 return -1;
293 LONG Printer_Text_Write(struct PrinterData *pd, UBYTE *text, LONG length)
295 struct PrinterExtendedData *ped = &pd->pd_SegmentData->ps_PED;
296 struct PrinterUnitText *txt = &pd->pd_PUnit->pu_Text;
297 LONG (*ConvFunc)(UBYTE *buf, UBYTE c, LONG crlf_flag);
298 CONST_STRPTR *Char8Bit = NULL;
299 UBYTE parm[4];
300 UBYTE state = 0;
301 UBYTE pindex = 0;
302 UBYTE buff[2][WBUFSIZE];
303 UBYTE *buffer = &buff[0][0];
304 int buffsel = 0;
305 int blen = 0;
306 LONG err;
308 if (pd->pd_SegmentData->ps_Version >= 33) {
309 Char8Bit = (CONST_STRPTR *)ped->ped_8BitChars;
312 if (pd->pd_SegmentData->ps_Version >= 34) {
313 ConvFunc = ped->ped_ConvFunc;
314 } else {
315 ConvFunc = noopConvFunc;
318 for (;(length < 0 && *text) || (length > 0); text++, length--) {
319 LONG cmd;
320 D(bug("%s: c='%c' (\\%o)\n", __func__, *text, *text));
321 cmd = doState(&state, *text, parm, &pindex);
322 if (cmd == -2)
323 continue;
325 if (cmd < 0) {
326 if (Char8Bit && *text >= 0xa0) {
327 CONST_STRPTR str = Char8Bit[*text-0xa0];
328 pd->pd_PWrite((APTR)str, strlen(str));
329 continue;
331 err = ConvFunc(&buffer[blen], *text, txt->pt_CRLF);
332 D(bug("%s: ConvFunc(%d) => %d\n", __func__, *text, err));
333 if (err < 0) {
334 if (*text == '\n') {
335 FLUSH_BUFFER();
336 Printer_Text_Command(pd, aNEL, 0, 0, 0, 0);
337 } else {
338 buffer[blen++] = *text;
340 } else {
341 blen += err;
343 } else {
344 FLUSH_BUFFER();
345 Printer_Text_Command(pd, cmd, parm[0], parm[1], parm[2], parm[3]);
348 if (blen > WBUFSIZE/2)
349 FLUSH_BUFFER();
352 return 0;