diff --git a/xerox-acct/Makefile b/xerox-acct/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..c1d7bf32428d7f66c3ae657c72f7f18291800f5a --- /dev/null +++ b/xerox-acct/Makefile @@ -0,0 +1,8 @@ +CC=gcc +CFLAGS=-O2 -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wredundant-decls -std=gnu99 + +all: xerox-acct + +clean: + rm -f `find . -name "*~" -or -name "*.[oa]" -or -name TAGS -or -name core -or -name .depend -or -name .#*` + rm -f xerox-acct diff --git a/xerox-acct/xerox-acct.c b/xerox-acct/xerox-acct.c new file mode 100644 index 0000000000000000000000000000000000000000..d24b1b3dfe34e285162fa499476e9a7b6ff9cc8b --- /dev/null +++ b/xerox-acct/xerox-acct.c @@ -0,0 +1,211 @@ +/* + * 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; +}