/* * CUPS Filter Generating Xerox Accounting Attributes * * (c) 2016 Martin Mares <mj@ucw.cz> */ #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> #define PRINTF(i,j) __attribute__((format(printf,i,j))) #define NONRET __attribute__((noreturn)) #if 0 #define DEBUG(...) debug(__VA_ARGS__) #else #define DEBUG(...) do { } while(0) #endif static char *job_id; static char *job_user; static char *job_title; /*** Utility functions ***/ static void PRINTF(1,2) NONRET bug(const char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "ERROR: ACCT: "); vfprintf(stderr, fmt, args); fputc('\n', stderr); va_end(args); exit(1); } #define ASSERT(_cond) do { if (!(_cond)) bug("Assertion failed: %s", #_cond); } while (0) #if 0 static void PRINTF(1,2) error(const char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "ERROR: ACCT: "); vfprintf(stderr, fmt, args); fputc('\n', stderr); va_end(args); } #endif #if 0 static void PRINTF(1,2) debug(const char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "DEBUG: ACCT: "); vfprintf(stderr, fmt, args); fputc('\n', stderr); va_end(args); } #endif static char *xstrdup(const char *x) { char *p = strdup(x); if (!p) bug("Out of memory"); return p; } /** * This is our parser of PJL. A hacky one, indeed, but it is expected * to parse only PJL directives generated by our PPD or by CUPS itself, * so we need not pay attention to all obscure details of the language. **/ #define LINESIZE 256 static char *skip_spaces(char *p) { while (*p == ' ' || *p == '\t') p++; return p; } static int my_toupper(int c) { if (c >= 'a' && c <= 'z') return c - 32; else return c; } static char *token_buf; static char *get_token(char **pp) { char *token_start = token_buf; char *tok = token_start; char *p = skip_spaces(*pp); if (!*p) return NULL; int c = my_toupper(*p); if (c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '_') { while (c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '_') { *tok++ = c; c = my_toupper(*++p); } } else *tok++ = *p++; *tok++ = 0; *pp = p; token_buf = tok; return token_start; } static void parse_pjl(void) { static const char uel[] = "\e%-12345X"; for (int i=0; uel[i]; i++) if (getchar() != uel[i]) bug("PJL error: No UEL found (pos %u)", i); printf("\e%%-12345X"); char line[LINESIZE], tokens[2*LINESIZE]; for (;;) { int i = 0; for (;;) { int c = getchar(); if (c < 0) bug("PJL error: Premature EOF"); if (i < 4 && c != "@PJL"[i]) bug("PJL error: Unrecognized line"); if (c == '\r') continue; if (c == '\n') break; if (i >= LINESIZE-1) bug("PJL error: Line too long"); line[i++] = c; } while (i > 0 && (line[i-1] == ' ' || line[i-1] == '\t')) i--; line[i] = 0; DEBUG("PJL: %s", line); char *p = line+1; token_buf = tokens; char *t, *t2; if (!(t = get_token(&p)) || strcasecmp(t, "PJL")) bug("PJL error: Malformed line"); if (!(t = get_token(&p))) { puts(line); continue; } if (!strcasecmp(t, "ENTER")) { if ((t2 = get_token(&p)) && !strcasecmp(t2, "LANGUAGE")) { puts(line); return; } } puts(line); } } static void copy_body(void) { char buf[1024]; size_t n; while (n = fread(buf, 1, sizeof(buf), stdin)) { if (fwrite(buf, 1, n, stdout) != n) bug("Write error"); } } /*** Main ***/ int main(int argc, char **argv) { if (argc < 5) { fprintf(stderr, "Usage: xerox-acct <job> <user> <title> <num-copies> [<options>]\n"); return 1; } job_id = xstrdup(argv[1]); job_user = xstrdup(argv[2]); job_title = xstrdup(argv[3]); parse_pjl(); copy_body(); return 0; }