/* ----------------------------------------------------------------------- * * * Copyright 2004-2014 H. Peter Anvin - All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, Inc., 53 Temple Place Ste 330, * Bostom MA 02111-1307, USA; either version 2 of the License, or * (at your option) any later version; incorporated herein by reference. * * ----------------------------------------------------------------------- */ /* * abcprintd.c * * Read a serial port; send data to lpr when receiving FF 00 * * Usage: abcprintd port [options to lpr] */ #include #include #include #include #include #include #include #include #include #include #include #ifndef O_TEXT # define O_TEXT 0 #endif #ifndef O_BINARY # define O_BINARY 0 #endif static int lpr_argc; static const char **lpr_argv; enum print_state { ps_first, /* Brand new job */ ps_percent, /* Job started with %, might be ps */ ps_binary, /* Don't touch this */ ps_text /* It's text, OK to modify */ }; static void print_setup(FILE **tfp, enum print_state *psp) { FILE *tf = *tfp; pid_t f; if ( tf ) { fflush(tf); rewind(tf); f = fork(); if ( f < 0 ) { perror("fork"); exit(1); } else if ( f == 0 ) { dup2(fileno(tf), STDIN_FILENO); fclose(tf); execvp(lpr_argv[0], (char **)lpr_argv); _exit(255); } else { fclose(tf); while ( waitpid(f, NULL, 0) != f ); } } *tfp = tmpfile(); if ( !*tfp ) { perror("tmpfile"); exit(1); } *psp = ps_first; } static int open_port(const char *path, speed_t speed) { struct termios tio; int fd; int flags; if ( (fd = open(path, O_RDWR|O_NONBLOCK|O_BINARY)) < 0 ) return -1; if ( tcgetattr(fd, &tio) ) return -1; tio.c_iflag &= ~(BRKINT|PARMRK|INPCK|ISTRIP|ICRNL|INLCR|IGNCR| IXON|IXOFF); tio.c_iflag |= IGNBRK|IGNPAR; tio.c_cflag &= ~(CSIZE|CSTOPB|HUPCL|PARENB|PARODD); tio.c_cflag |= CLOCAL|CREAD|CS8; tio.c_oflag &= ~OPOST; tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON|ISIG|NOFLSH|TOSTOP|IEXTEN); tio.c_cc[VMIN] = 1; tio.c_cc[VTIME] = 0; cfsetispeed(&tio, speed); cfsetospeed(&tio, speed); tcflush(fd, TCIFLUSH); if ( tcsetattr(fd, TCSANOW, &tio) ) return -1; if ( (flags = fcntl(fd, F_GETFL)) == -1 ) return -1; flags &= ~O_NONBLOCK; if ( fcntl(fd, F_SETFL, flags) ) return -1; return fd; } static void output(int c, FILE *tf, enum print_state *psp) { static const wchar_t abc_to_unicode[256] = L"\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017" L"\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037" L" !\"#¤%&\'()*+,-./0123456789:;<=>?" L"ÉABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÅÜ_" L"éabcdefghijklmnopqrstuvwxyzäöåü\x25a0" L"\x20ac\x25a1\x201a\x0192\x201e\x2026\x2020\x2021" L"\x02c6\x2030\x0160\x2039\x0152\x2190\x017d\x2192" L"\x2191\x2018\x2019\x201c\x201d\x2022\x2013\x2014" L"\x02dc\x2122\x0161\x203a\x0153\x2193\x017e\x0178" L"\240\241\242\243$\245\246\247\250\251\252\253\254\255\256\257" L"\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277" L"\300\301\302\303[]\306\307\310@\312\313\314\315\316\317" L"\320\321\322\323\324\325\\\327\330\331\332\333^\335\336\337" L"\340\341\342\343{}\346\347\350`\352\353\354\355\356\357" L"\360\361\362\363\364\365|\367\370\371\372\373~\375\376\377"; switch ( *psp ) { case ps_first: if ( c == 27 ) *psp = ps_binary; else if ( c == '%' ) { *psp = ps_percent; return; } else { *psp = ps_text; } output(c, tf, psp); break; case ps_percent: if ( c == '!' ) { *psp = ps_binary; } else { *psp = ps_text; } output('%', tf, psp); output(c, tf, psp); break; case ps_text: if (c != '\r') putwc(abc_to_unicode[(unsigned char)c], tf); break; case ps_binary: putc(c, tf); break; } } extern bool file_op(unsigned char, int); extern bool fileops; #define BUF_SIZE 4096 int main(int argc, char *argv[]) { int fd; FILE *tf = NULL; enum { st_normal, /* Normal operation */ st_ff, /* 0xFF received */ st_file, /* File operation in progress */ st_cons, /* Console output in progress */ } state; char ibuf[BUF_SIZE]; unsigned char c; int i, b; enum print_state ps; static const char *lpr_default_argv[] = { "lpr", NULL }; int o; extern char *optarg; extern int optind, opterr, optopt; bool fg = false; bool console = false; bool badopt = false; speed_t speed = B115200; setlocale(LC_ALL, ""); fileops = false; while ((o = getopt(argc, argv, "fd:c")) != EOF) { switch (o) { case 'd': if (chdir(optarg)) { fprintf(stderr, "%s: chdir %s: %s\n", argv[0], optarg, strerror(errno)); exit(1); } fileops = true; break; case 'f': fg = true; break; case 'c': console = true; break; default: badopt = true; break; } } if ( badopt || optind >= argc ) { fprintf(stderr, "Usage: %s [options] port [lpr_program [lpr_args...]]\n" "Options:\n" " -f run in the foreground\n" " -d dir enable file access to directory dir\n" " -c enable debug console output\n" , argv[0]); exit(1); } if ( (fd = open_port(argv[optind], speed)) < 0 ) { perror(argv[optind]); exit(1); } if ( !argv[optind+1] ) { lpr_argc = sizeof(lpr_default_argv)/sizeof(const char *) - 1; lpr_argv = lpr_default_argv; } else { lpr_argc = argc-optind-1; if ( !(lpr_argv = malloc(sizeof(char *)*(lpr_argc+1))) ) { perror("malloc"); exit(1); } memcpy(lpr_argv, argv+optind+1, sizeof(char *)*(lpr_argc+1)); } if (!fg) daemon(fileops, console); print_setup(&tf, &ps); state = st_normal; for ( ;; ) { if ( (b = read(fd, ibuf, sizeof ibuf)) < 1 ) { perror(argv[1]); exit(1); } for ( i = 0 ; i < b ; i++ ) { c = ibuf[i]; switch ( state ) { case st_normal: if ( c == 0xff ) state = st_ff; else output(c, tf, &ps); break; case st_ff: if ( c == 0 ) { print_setup(&tf, &ps); /* BREAK received, end of job */ state = st_normal; } else if ( c >= 0xa0 && c <= 0xbf ) { /* Opcode range reserved for file ops */ state = file_op(c, fd) ? st_file : st_normal; } else if ( c == 0xc0 ) { /* Debug console output */ state = st_cons; } else { output(c, tf, &ps); state = st_normal; } break; case st_file: state = file_op(c, fd) ? st_file : st_normal; break; case st_cons: if ( c == 0x00 ) { if (console) fflush(stdout); state = st_normal; } else if (console) { putchar(c); } break; } } } }