]> mj.ucw.cz Git - paperjam.git/blobdiff - paperjam.cc
TODO: a4r paper
[paperjam.git] / paperjam.cc
index 6ff37d1757ab8a4fe120b41a8f1d184c3e819500..0d3bf12c57fe95452fcd8cb1ec5b626966ce2f6d 100644 (file)
+/*
+ *     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;
 }