/* * Copyright (C) 2012, The AROS Development Team. All rights reserved. * Author: Jason S. McMullan * * Licensed under the AROS PUBLIC LICENSE (APL) Version 1.1 * * Text printing */ #include #include #include "printer_intern.h" #define WBUFSIZE 256 #define FLUSH_BUFFER() \ do { \ if (blen > 0) { \ pd->pd_PWrite(buffer, blen); \ buffsel ^= 1; \ buffer = buff[buffsel]; \ blen = 0; \ } \ } while (0) #define CSI '\033' #define CSI_BRACK '[' #define CSI_BRACK_SEMI ';' #define CSI_BRACK_QUOTE '"' #define CSI_PAREN '(' #define CSI_HASH '#' #define CSI_END -1 struct StateTable { BYTE state; UBYTE code; BYTE parm; BYTE cmd; }; static const struct StateTable StateTable[] = { { .state = 0, .code = 0, .parm = -1, .cmd = -1 }, { .state = CSI, .code = 'D', .parm = -1, .cmd = aIND }, { .state = CSI, .code = 'E', .parm = -1, .cmd = aNEL }, { .state = CSI, .code = 'H', .parm = -1, .cmd = aHTS }, { .state = CSI, .code = 'J', .parm = -1, .cmd = aVTS }, { .state = CSI, .code = 'K', .parm = -1, .cmd = aPLD }, { .state = CSI, .code = 'L', .parm = -1, .cmd = aPLU }, { .state = CSI, .code = 'M', .parm = -1, .cmd = aRI }, { .state = CSI, .code = 'c', .parm = -1, .cmd = aRIS }, { .state = CSI_HASH, .code = '0', .parm = -1, .cmd = aRMS }, { .state = CSI_HASH, .code = '1', .parm = -1, .cmd = aRIN }, { .state = CSI_HASH, .code = '2', .parm = -1, .cmd = aBMS }, { .state = CSI_HASH, .code = '3', .parm = -1, .cmd = aCAM }, { .state = CSI_HASH, .code = '4', .parm = -1, .cmd = aTBCALL }, { .state = CSI_HASH, .code = '5', .parm = -1, .cmd = aTBSALL }, { .state = CSI_HASH, .code = '8', .parm = -1, .cmd = aTMS }, { .state = CSI_HASH, .code = '9', .parm = -1, .cmd = aLMS }, { .state = CSI_PAREN, .code = '6', .parm = -1, .cmd = aFNT9 }, { .state = CSI_PAREN, .code = 'A', .parm = -1, .cmd = aFNT3 }, { .state = CSI_PAREN, .code = 'B', .parm = -1, .cmd = aFNT0 }, { .state = CSI_PAREN, .code = 'C', .parm = -1, .cmd = aFNT10 }, { .state = CSI_PAREN, .code = 'E', .parm = -1, .cmd = aFNT4 }, { .state = CSI_PAREN, .code = 'H', .parm = -1, .cmd = aFNT5 }, { .state = CSI_PAREN, .code = 'J', .parm = -1, .cmd = aFNT8 }, { .state = CSI_PAREN, .code = 'K', .parm = -1, .cmd = aFNT2 }, { .state = CSI_PAREN, .code = 'R', .parm = -1, .cmd = aFNT1 }, { .state = CSI_PAREN, .code = 'Y', .parm = -1, .cmd = aFNT6 }, { .state = CSI_PAREN, .code = 'Z', .parm = -1, .cmd = aFNT7 }, { .state = CSI_BRACK, .code = 'E', .parm = -1, .cmd = aTSS }, { .state = CSI_BRACK, .code = 'F', .parm = 0, .cmd = aJFY0 }, { .state = CSI_BRACK, .code = 'F', .parm = 1, .cmd = aJFY1 }, { .state = CSI_BRACK, .code = 'F', .parm = 3, .cmd = aJFY3 }, { .state = CSI_BRACK, .code = 'F', .parm = 5, .cmd = aJFY5 }, { .state = CSI_BRACK, .code = 'F', .parm = 6, .cmd = aJFY6 }, { .state = CSI_BRACK, .code = 'F', .parm = 7, .cmd = aJFY7 }, { .state = CSI_BRACK, .code = 'g', .parm = 0, .cmd = aTBC0 }, { .state = CSI_BRACK, .code = 'g', .parm = 1, .cmd = aTBC1 }, { .state = CSI_BRACK, .code = 'g', .parm = 3, .cmd = aTBC3 }, { .state = CSI_BRACK, .code = 'g', .parm = 4, .cmd = aTBC4 }, { .state = CSI_BRACK, .code = 'm', .parm = 0, .cmd = aSGR0 }, { .state = CSI_BRACK, .code = 'm', .parm = 1, .cmd = aSGR1 }, { .state = CSI_BRACK, .code = 'm', .parm = 22, .cmd = aSGR22 }, { .state = CSI_BRACK, .code = 'm', .parm = 23, .cmd = aSGR23 }, { .state = CSI_BRACK, .code = 'm', .parm = 24, .cmd = aSGR24 }, { .state = CSI_BRACK, .code = 'm', .parm = 3, .cmd = aSGR3 }, { .state = CSI_BRACK, .code = 'm', .parm = 30, .cmd = aSFC }, { .state = CSI_BRACK, .code = 'm', .parm = 31, .cmd = aSFC }, { .state = CSI_BRACK, .code = 'm', .parm = 32, .cmd = aSFC }, { .state = CSI_BRACK, .code = 'm', .parm = 33, .cmd = aSFC }, { .state = CSI_BRACK, .code = 'm', .parm = 34, .cmd = aSFC }, { .state = CSI_BRACK, .code = 'm', .parm = 35, .cmd = aSFC }, { .state = CSI_BRACK, .code = 'm', .parm = 36, .cmd = aSFC }, { .state = CSI_BRACK, .code = 'm', .parm = 37, .cmd = aSFC }, { .state = CSI_BRACK, .code = 'm', .parm = 38, .cmd = aSFC }, { .state = CSI_BRACK, .code = 'm', .parm = 39, .cmd = aSFC }, { .state = CSI_BRACK, .code = 'm', .parm = 4, .cmd = aSGR4 }, { .state = CSI_BRACK, .code = 'm', .parm = 40, .cmd = aSBC }, { .state = CSI_BRACK, .code = 'm', .parm = 41, .cmd = aSBC }, { .state = CSI_BRACK, .code = 'm', .parm = 42, .cmd = aSBC }, { .state = CSI_BRACK, .code = 'm', .parm = 43, .cmd = aSBC }, { .state = CSI_BRACK, .code = 'm', .parm = 44, .cmd = aSBC }, { .state = CSI_BRACK, .code = 'm', .parm = 45, .cmd = aSBC }, { .state = CSI_BRACK, .code = 'm', .parm = 46, .cmd = aSBC }, { .state = CSI_BRACK, .code = 'm', .parm = 47, .cmd = aSBC }, { .state = CSI_BRACK, .code = 'm', .parm = 48, .cmd = aSBC }, { .state = CSI_BRACK, .code = 'm', .parm = 49, .cmd = aSBC }, { .state = CSI_BRACK, .code = 'p', .parm = 0, .cmd = aPROP0 }, { .state = CSI_BRACK, .code = 'p', .parm = 1, .cmd = aPROP1 }, { .state = CSI_BRACK, .code = 'p', .parm = 2, .cmd = aPROP2 }, { .state = CSI_BRACK, .code = 'q', .parm = -1, .cmd = aPERF }, { .state = CSI_BRACK, .code = 'q', .parm = 0, .cmd = aPERF0 }, { .state = CSI_BRACK, .code = 't', .parm = -1, .cmd = aSLPP }, { .state = CSI_BRACK, .code = 'v', .parm = 0, .cmd = aSUS0 }, { .state = CSI_BRACK, .code = 'v', .parm = 1, .cmd = aSUS1 }, { .state = CSI_BRACK, .code = 'v', .parm = 2, .cmd = aSUS2 }, { .state = CSI_BRACK, .code = 'v', .parm = 3, .cmd = aSUS3 }, { .state = CSI_BRACK, .code = 'v', .parm = 4, .cmd = aSUS4 }, { .state = CSI_BRACK, .code = 'w', .parm = 0, .cmd = aSHORP0 }, { .state = CSI_BRACK, .code = 'w', .parm = 1, .cmd = aSHORP1 }, { .state = CSI_BRACK, .code = 'w', .parm = 2, .cmd = aSHORP2 }, { .state = CSI_BRACK, .code = 'w', .parm = 3, .cmd = aSHORP3 }, { .state = CSI_BRACK, .code = 'w', .parm = 4, .cmd = aSHORP4 }, { .state = CSI_BRACK, .code = 'w', .parm = 5, .cmd = aSHORP5 }, { .state = CSI_BRACK, .code = 'w', .parm = 6, .cmd = aSHORP6 }, { .state = CSI_BRACK, .code = 'z', .parm = 0, .cmd = aVERP0 }, { .state = CSI_BRACK, .code = 'z', .parm = 1, .cmd = aVERP1 }, { .state = CSI_BRACK_QUOTE, .code = 'r', .parm = -1, .cmd = aRAW }, { .state = CSI_BRACK_QUOTE, .code = 'x', .parm = -1, .cmd = aEXTEND }, { .state = CSI_BRACK_QUOTE, .code = 'z', .parm = 1, .cmd = aDEN1 }, { .state = CSI_BRACK_QUOTE, .code = 'z', .parm = 2, .cmd = aDEN2 }, { .state = CSI_BRACK_QUOTE, .code = 'z', .parm = 3, .cmd = aDEN3 }, { .state = CSI_BRACK_QUOTE, .code = 'z', .parm = 4, .cmd = aDEN4 }, { .state = CSI_BRACK_QUOTE, .code = 'z', .parm = 5, .cmd = aDEN5 }, { .state = CSI_BRACK_QUOTE, .code = 'z', .parm = 6, .cmd = aDEN6 }, { .state = CSI_BRACK_SEMI, .code = 'r', .parm = -1, .cmd = aSTBM }, { .state = CSI_BRACK_SEMI, .code = 's', .parm = -1, .cmd = aSLRM }, { .state = CSI_END }, }; static inline int isdigit(int x) { return (x >= '0' && x <= '9'); } LONG Printer_Text_Command(struct PrinterData *pd, UWORD command, UBYTE p0, UBYTE p1, UBYTE p2, UBYTE p3) { struct PrinterExtendedData *ped = &pd->pd_SegmentData->ps_PED; struct PrinterUnitText *txt = &pd->pd_PUnit->pu_Text; CONST_STRPTR *Commands = pd->pd_Device.dd_CmdVectors; LONG CommandMax = pd->pd_Device.dd_NumCommands; /* Use pd_Stack as the command buffer */ /* pd_OldStk[8..11] is used for the 'TPMATCHWORD' magic */ UBYTE *buff[2] = { (UBYTE *)&pd->pd_Stk[0], (UBYTE *)&pd->pd_OldStk[12] }; int buffsel = 0; UBYTE *buffer = buff[buffsel]; LONG buffmax = ((P_STKSIZE > (P_OLDSTKSIZE-12)) ? (P_OLDSTKSIZE-12) : P_STKSIZE)/2; CONST_STRPTR cmd; LONG blen = 0; UBYTE parm[4] = { p0, p1, p2, p3 }; if (command >= CommandMax) return -1; cmd = Commands[command]; D(bug("%s: cmd=%d(%s), (%d, %d, %d, %d)\n", __func__, command, cmd, p0, p1, p2, p3)); if (cmd == NULL) return -1; if (command == aRIN || command == aRIS) { txt->pt_CurrentLine = 0; txt->pt_CRLF = 0; txt->pt_Spacing = pd->pd_Preferences.PrintSpacing; } for (; *cmd; cmd++) { D(bug("%s: command=%d '%c'(%d), ped_DoSpecial=%p \n", __func__, command, *cmd, *cmd, ped->ped_DoSpecial)); if ((TEXT)*cmd == (TEXT)'\377' && ped->ped_DoSpecial) { LONG err; if ((TEXT)cmd[1] == (TEXT)'\377') { /* NOTE: From printer.device 44.12: flush before calling DoSpecial */ pd->pd_PBothReady(); cmd++; } err = ped->ped_DoSpecial(&command, &buffer[blen], &txt->pt_CurrentLine, &txt->pt_Spacing, &txt->pt_CRLF, parm); D(bug("%s: ped_DoSpecial: cmd => %d, buf='%s', err=%d\n", __func__, command, &buffer[blen], err)); if (err > 0) { blen += err; } else if (err == -2) { blen += 0; } else if (err == 0) { buffer[blen++] = *cmd; } else { return err; } } else { buffer[blen++] = *cmd; } if (blen >= buffmax) { FLUSH_BUFFER(); } } FLUSH_BUFFER(); return 0; } LONG noopConvFunc(UBYTE *buf, UBYTE c, LONG crlf_flag) { return -1; } /* Returns: * -1 if not in a command string * -2 if processing a possible command, * >= 0 if a command found */ static LONG doState(UBYTE *state, UBYTE c, UBYTE *parm, UBYTE *parm_index) { const struct StateTable *loc = &StateTable[*state]; BYTE currstate = loc->state; if (currstate == 0) { if (c != CSI) return -1; parm[0] = 0; *state = 1; return -2; } if (currstate != 0 && currstate != CSI && currstate != CSI_HASH) { if (isdigit(c)) { UWORD p = parm[*parm_index]; p = (p * 10) + (c - '0'); if (p > 100) { *state = 0; return -1; } parm[*parm_index] = (UBYTE)p; return -2; } } /* State promotion */ if ((currstate == CSI && (c == CSI_HASH || c == CSI_PAREN || c == CSI_BRACK)) || (currstate == CSI_BRACK && (c == CSI_BRACK_SEMI || c == CSI_BRACK_QUOTE))) { /* Advance to next matching state */ for (; loc->state != CSI_END && loc->state != c; loc++, (*state)++); if (loc->state == CSI_END) { *state = 0; return -1; } if (c == CSI_BRACK_SEMI) { parm[++(*parm_index)]=0; } return -2; } /* Code search */ for (; loc->state == currstate && loc->code != c; loc++, (*state)++); if (loc->state != currstate) { *state = 0; return -1; } /* Parm search */ for (; loc->state == currstate; loc++, (*state)++) { if ((loc->parm == -1) || (loc->parm == parm[0])) { *state = 0; return loc->cmd; } } /* No match */ *state = 0; return -1; } LONG Printer_Text_Write(struct PrinterData *pd, UBYTE *text, LONG length) { struct PrinterExtendedData *ped = &pd->pd_SegmentData->ps_PED; struct PrinterUnitText *txt = &pd->pd_PUnit->pu_Text; LONG (*ConvFunc)(UBYTE *buf, UBYTE c, LONG crlf_flag); CONST_STRPTR *Char8Bit = NULL; UBYTE parm[4]; UBYTE state = 0; UBYTE pindex = 0; UBYTE buff[2][WBUFSIZE]; UBYTE *buffer = &buff[0][0]; int buffsel = 0; int blen = 0; LONG err; if (pd->pd_SegmentData->ps_Version >= 33) { Char8Bit = (CONST_STRPTR *)ped->ped_8BitChars; } if (pd->pd_SegmentData->ps_Version >= 34) { ConvFunc = ped->ped_ConvFunc; } else { ConvFunc = noopConvFunc; } for (;(length < 0 && *text) || (length > 0); text++, length--) { LONG cmd; D(bug("%s: c='%c' (\\%o)\n", __func__, *text, *text)); cmd = doState(&state, *text, parm, &pindex); if (cmd == -2) continue; if (cmd < 0) { if (Char8Bit && *text >= 0xa0) { CONST_STRPTR str = Char8Bit[*text-0xa0]; pd->pd_PWrite((APTR)str, strlen(str)); continue; } err = ConvFunc(&buffer[blen], *text, txt->pt_CRLF); D(bug("%s: ConvFunc(%d) => %d\n", __func__, *text, err)); if (err < 0) { if (*text == '\n') { FLUSH_BUFFER(); Printer_Text_Command(pd, aNEL, 0, 0, 0, 0); } else { buffer[blen++] = *text; } } else { blen += err; } } else { FLUSH_BUFFER(); Printer_Text_Command(pd, cmd, parm[0], parm[1], parm[2], parm[3]); } if (blen > WBUFSIZE/2) FLUSH_BUFFER(); } return 0; }