+/*
+ * PaperJam -- Main program
+ *
+ * (c) 2018--2022 Martin Mares <mj@ucw.cz>
+ */
+
#include <cassert>
#include <cstdarg>
#include <cstdlib>
#include <cstdio>
-
-using namespace std;
+#include <getopt.h>
#include "jam.h"
-/*** Lexer ***/
-
-enum token_type {
- TOK_NONE,
- TOK_END,
- TOK_EQUAL,
- TOK_COMMA,
- TOK_OPEN_PAREN,
- TOK_CLOSE_PAREN,
- TOK_OPEN_BRACE,
- TOK_CLOSE_BRACE,
- TOK_IDENT,
- TOK_STRING,
- TOK_NUMBER,
-};
+/*** Options ***/
-const char *in_pos;
-static token_type this_token = TOK_NONE;
-static token_type buffered_token = TOK_NONE;
-static string token;
-static double token_num;
+const char *in_name;
+const char *out_name;
+bool recalc_bbox;
+bool no_auto_transforms;
+int debug_level;
+int debug_indent;
-static void NONRET parse_error(const char *msg, ...);
+/*** Messages ***/
-static void parse_error(const char *msg, ...)
+void debug(const char *msg, ...)
{
+ if (!debug_level)
+ return;
va_list args;
va_start(args, msg);
- fprintf(stderr, "Parse error: ");
+ fprintf(stderr, "%*s", debug_indent, "");
vfprintf(stderr, msg, args);
- fprintf(stderr, "\n");
+ fputc('\n', stderr);
va_end(args);
- exit(1);
}
-static token_type get_next_token()
+void warn(const char *msg, ...)
{
- while (*in_pos == ' ' || *in_pos == '\t' || *in_pos == '\r' || *in_pos == '\n')
- in_pos++;
-
- token = "";
- if (!*in_pos)
- return TOK_END;
-
- if (*in_pos >= '0' && *in_pos <= '9' ||
- *in_pos == '-' && in_pos[1] >= '0' && in_pos[1] <= '9')
- {
- token += *in_pos++;
- while (*in_pos >= '0' && *in_pos <= '9' || *in_pos == '.')
- token += *in_pos++;
-
- size_t end_pos;
- token_num = stod(token, &end_pos);
- if (end_pos < token.length())
- parse_error("Invalid number %s", token.c_str());
- return TOK_NUMBER;
- }
-
- if (*in_pos >= 'A' && *in_pos <= 'Z' ||
- *in_pos >= 'a' && *in_pos <= 'z')
- {
- while (*in_pos >= 'A' && *in_pos <= 'Z' ||
- *in_pos >= 'a' && *in_pos <= 'z' ||
- *in_pos >= '0' && *in_pos <= '9')
- token += *in_pos++;
- return TOK_IDENT;
- }
-
- if (*in_pos == '"')
- {
- in_pos++;
- while (*in_pos != '"')
- {
- if (!*in_pos)
- parse_error("Unterminated string");
- if (*in_pos == '\\')
- {
- in_pos++;
- if (*in_pos == '"')
- parse_error("Unrecognized escape sequence \\%c", *in_pos);
- }
- token += *in_pos++;
- }
- in_pos++;
- return TOK_STRING;
- }
-
- uint c = *in_pos++;
- switch (c)
- {
- case '=':
- return TOK_EQUAL;
- case ',':
- return TOK_COMMA;
- case '(':
- return TOK_OPEN_PAREN;
- case ')':
- return TOK_CLOSE_PAREN;
- case '{':
- return TOK_OPEN_BRACE;
- case '}':
- return TOK_CLOSE_BRACE;
- default:
- parse_error("Unrecognized character '%c'", c);
- }
+ va_list args;
+ va_start(args, msg);
+ fprintf(stderr, "Warning: ");
+ vfprintf(stderr, msg, args);
+ fputc('\n', stderr);
+ va_end(args);
}
-static token_type next_token()
+void die(const char *msg, ...)
{
- this_token = get_next_token();
- return this_token;
+ va_list args;
+ va_start(args, msg);
+ vfprintf(stderr, msg, args);
+ fputc('\n', stderr);
+ va_end(args);
+ exit(1);
}
-static void return_token()
+void err(const char *msg, ...)
{
- assert(this_token != TOK_NONE);
- assert(buffered_token == TOK_NONE);
- buffered_token = this_token;
- this_token = TOK_NONE;
+ va_list args;
+ va_start(args, msg);
+ char buf[1024];
+ vsnprintf(buf, sizeof(buf), msg, args);
+ va_end(args);
+ throw paperjam_error(buf);
}
-/*** Parser ***/
+/*** Arguments ***/
-static const arg_def move_args[] = {
- { "x", AT_DIMEN | AT_MANDATORY | AT_POSITIONAL },
- { "y", AT_DIMEN | AT_MANDATORY | AT_POSITIONAL },
- { NULL, 0 }
+enum opt {
+ OPT_HELP = 256,
+ OPT_VERSION,
+ OPT_NO_AUTO,
};
-static const cmd_def cmd_table[] = {
- { "move", move_args, NULL },
- { NULL, NULL, NULL }
+static const struct option long_opts[] = {
+ { "debug", 0, 0, 'd' },
+ { "help", 0, 0, OPT_HELP },
+ { "version", 0, 0, OPT_VERSION },
+ { "bbox", 0, 0, 'b' },
+ { 0, 0, 0, 0 }
};
-struct unit {
- const char *name;
- double multiplier;
-};
+static const char short_opts[] = "bd";
-#define MM (72/25.4)
-
-static const unit units[] = {
- { "mm", MM },
- { "cm", 10*MM },
- { "dm", 100*MM },
- { "m", 1000*MM },
- { "in", 72 },
- { "pt", 1 },
- { NULL, 0 }
-};
-
-static double parse_dimen(const arg_def *adef)
+static void usage()
{
- token_type t = next_token();
- if (t != TOK_NUMBER)
- parse_error("Paremeter %s must be a dimension", adef->name);
- double tmp = token_num;
-
- t = next_token();
- if (t != TOK_IDENT)
- parse_error("Paremeter %s must have a unit", adef->name);
- for (uint i; units[i].name; i++)
- if (token == units[i].name)
- return tmp * units[i].multiplier;
- parse_error("Unknown unit %s", token.c_str());
+ printf("Usage: paperjam [<options>] <commands> <in> <out>\n\
+\n\
+Options:\n\
+-b, --bbox Recalculate bounding boxes\n\
+-d, --debug Show debugging messages\n\
+ --no-auto Disable automatic rotation of pages\n\
+\n\
+<command> = <name>(<args>, <named-arg>[=<value>], ...) [<pipeline>]\n\
+<pipeline> = { <stage>, <stage>, ... }\n\
+<stage> = <page> <page> ... [: <commands>]\n\
+\n\
+Example:\n\
+paperjam 'book nup(2, paper=a4)' input.pdf output.pdf\n\
+\n\
+Commands:\n\
+");
+ parser_help();
}
-static cmd_args *parse_args(const cmd_def *cdef)
+static void show_version()
{
- cmd_args *args = new cmd_args;
-
- const arg_def *adefs = cdef->arg_defs;
- uint num_args = 0;
- while (adefs[num_args].name)
- {
- args->arg.push_back(arg_val());
- args->arg_given.push_back(0);
- num_args++;
- }
-
- token_type t = next_token();
- if (t != TOK_OPEN_PAREN)
- {
- return_token();
- return args;
- }
-
- bool saw_named = false;
- uint next_pos = 0;
- for (;;)
- {
- t = next_token();
- int argi = 0;
- if (t == TOK_IDENT)
- {
- while (adefs[argi].name && token != adefs[argi].name)
- argi++;
- if (!adefs[argi].name)
- parse_error("Command %s has no parameter %s", cdef->name, token.c_str());
- t = next_token();
- if (t != TOK_EQUAL)
- parse_error("Parameter name must be followed by '='");
- saw_named = true;
- }
- else if (saw_named)
- parse_error("Positional parameters must precede named ones");
- else
- {
- while (next_pos < num_args && !(adefs[next_pos].type & AT_POSITIONAL))
- next_pos++;
- if (next_pos >= num_args)
- parse_error("Too many positional arguments for command %s", cdef->name);
- argi = next_pos++;
- }
-
- const arg_def *adef = &adefs[argi];
- switch (adef->type & AT_TYPE_MASK)
- {
- case AT_STRING:
- t = next_token();
- if (t != TOK_STRING)
- parse_error("Paremeter %s must be a string", adef->name);
- args->arg[argi].s = token;
- break;
- case AT_DOUBLE:
- t = next_token();
- if (t != TOK_NUMBER)
- parse_error("Paremeter %s must be a number", adef->name);
- args->arg[argi].d = token_num;
- break;
- case AT_DIMEN:
- args->arg[argi].d = parse_dimen(adef);
- break;
- default:
- abort();
- }
-
- t = next_token();
- if (t == TOK_CLOSE_PAREN)
- break;
- if (t != TOK_COMMA)
- parse_error("Comma expected after parameter %s", adef->name);
- }
-
- return args;
+ printf("PaperJam " VERSION " -- a PDF processor\n");
+ printf("(c) " YEAR " Martin Mares, distributed under GNU GPL 2+\n");
+ printf("Built on " BUILD_DATE " from Git commit " BUILD_COMMIT "\n");
}
-static cmd *parse_cmd()
+int main(int argc, char **argv)
{
- const cmd_def *cdef = cmd_table;
- while (cdef->name && token != cdef->name)
- cdef++;
- if (!cdef->name)
- parse_error("Unknown command %s", token.c_str());
+ int c;
+ while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) >= 0)
+ switch (c)
+ {
+ case 'b':
+ recalc_bbox = 1;
+ break;
+ case 'd':
+ debug_level++;
+ break;
+ case OPT_VERSION:
+ show_version();
+ return 0;
+ case OPT_HELP:
+ usage();
+ return 0;
+ case OPT_NO_AUTO:
+ no_auto_transforms = true;
+ break;
+ default:
+ exit(1);
+ }
- cmd_args *args = parse_args(cdef);
-}
+ if (optind + 3 != argc)
+ die("Exactly three positional parameters should be given");
-static void parse(list<cmd> *cmds)
-{
- for (;;)
- {
- token_type t = next_token();
- if (t != TOK_IDENT)
- {
- return_token();
- return;
- }
+ list<cmd *> cmds;
+ parse(argv[optind], cmds);
+ in_name = argv[optind+1];
+ out_name = argv[optind+2];
- cmd *c = parse_cmd();
- cmds->push_back(c);
+ try
+ {
+ process(cmds);
}
-}
-
-/*** Main ***/
-
-int main(int argc, char **argv)
-{
- if (argc != 4)
+ catch (exception& e)
{
- fprintf(stderr, "Usage: pdfjam <commands> <input> <output>\n");
- return 1;
+ die("%s", e.what());
}
- list<cmd> cmds;
- in_pos = argv[1];
- parse(&cmds);
-
return 0;
}