Merge branch 'master' into browser
[projects/chimara/chimara.git] / babel / babel.c
diff --git a/babel/babel.c b/babel/babel.c
new file mode 100644 (file)
index 0000000..5bb7213
--- /dev/null
@@ -0,0 +1,248 @@
+/* babel.c   The babel command line program\r
+ * (c) 2006 By L. Ross Raszewski\r
+ *\r
+ * This code is freely usable for all purposes.\r
+ *\r
+ * This work is licensed under the Creative Commons Attribution2.5 License.\r
+ * To view a copy of this license, visit\r
+ * http://creativecommons.org/licenses/by/2.5/ or send a letter to\r
+ * Creative Commons,\r
+ * 543 Howard Street, 5th Floor,\r
+ * San Francisco, California, 94105, USA.\r
+ *\r
+ * This file depends upon misc.c and babel.h\r
+ *\r
+ * This file exports one variable: char *rv, which points to the file name\r
+ * for an ifiction file.  This is used only by babel_ifiction_verify\r
+ */\r
+\r
+#include "babel.h"\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+int chdir(const char *);\r
+char *getcwd(char *, int);\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+char *fn;\r
+\r
+/* checked malloc function */\r
+void *my_malloc(int, char *);\r
+\r
+/* babel performs several fundamental operations, which are specified\r
+   by command-line objects. Each of these functions corresponds to\r
+   a story function (defined in babel_story_functions.c) or an\r
+   ifiction function (defined in babel_ifiction_functions.c) or both.\r
+   These are the types of those functions.\r
+*/\r
+\r
+typedef void (*story_function)(void);\r
+typedef void (*ifiction_function)(char *);\r
+typedef void (*multi_function)(char **, char *, int);\r
+/* This structure tells babel what to do with its command line arguments.\r
+   if either of story or ifiction are NULL, babel considers this command line\r
+   option inappropriate for that type of file.\r
+*/\r
+struct function_handler {\r
+        char *function;         /* the textual command line option */\r
+        story_function story;   /* handler for story files */\r
+        ifiction_function ifiction; /* handler for ifiction files */\r
+        char *desc;             /* Textual description for help text */\r
+        };\r
+\r
+struct multi_handler {\r
+        char *function;\r
+        char *argnames;\r
+        multi_function handler;\r
+        int nargsm;\r
+        int nargsx;\r
+        char *desc;\r
+        };\r
+/* This is an array of function_handler objects which specify the legal\r
+   arguments.  It is terminated by a function_handler with a NULL function\r
+ */\r
+static struct function_handler functions[] = {\r
+        { "-ifid", babel_story_ifid, babel_ifiction_ifid, "Deduce IFID"},\r
+        { "-format", babel_story_format, NULL, "Deduce story format" },\r
+        { "-ifiction", babel_story_ifiction, NULL, "Extract iFiction file" },\r
+        { "-meta", babel_story_meta, NULL, "Print story metadata" },\r
+        { "-identify", babel_story_identify, NULL, "Describe story file" },\r
+        { "-cover", babel_story_cover, NULL, "Extract cover art" },\r
+        { "-story", babel_story_story, NULL, "Extract story file (ie. from a blorb)" },\r
+        { "-verify", NULL, babel_ifiction_verify, "Verify integrity of iFiction file" },\r
+        { "-lint", NULL, babel_ifiction_lint, "Verify style of iFiction file" },\r
+        { "-fish", babel_story_fish, babel_ifiction_fish, "Extract all iFiction and cover art"},\r
+        { "-unblorb", babel_story_unblorb, NULL, "As -fish, but also extract story files"},\r
+        { NULL, NULL, NULL }\r
+        };\r
+static struct multi_handler multifuncs[] = {\r
+        { "-blorb", "<storyfile> <ifictionfile> [<cover art>]", babel_multi_blorb, 2, 3, "Bundle story file and (sparse) iFiction into blorb" },\r
+        { "-blorbs", "<storyfile> <ifictionfile> [<cover art>]", babel_multi_blorb1, 2, 3, "Bundle story file and (sparse) iFiction into sensibly-named blorb" },\r
+        { "-complete", "<storyfile> <ifictionfile>", babel_multi_complete, 2, 2, "Create complete iFiction file from sparse iFiction" },\r
+        { NULL, NULL, NULL, 0, 0, NULL }\r
+};\r
+\r
+int main(int argc, char **argv)\r
+{\r
+ char *todir=".";\r
+ char cwd[512];\r
+ int ok=1,i, l, ll;\r
+ FILE *f;\r
+ char *md=NULL;\r
+ /* Set the input filename.  Note that if this is invalid, babel should\r
+   abort before anyone notices\r
+ */\r
+ fn=argv[2];\r
+\r
+ if (argc < 3) ok=0;\r
+ /* Detect the presence of the "-to <directory>" argument.\r
+  */\r
+ if (ok && argc >=5 && strcmp(argv[argc-2], "-to")==0)\r
+ {\r
+  todir=argv[argc-1];\r
+  argc-=2;\r
+ }\r
+ if (ok) for(i=0;multifuncs[i].function;i++)\r
+          if (strcmp(argv[1],multifuncs[i].function)==0 &&\r
+              argc>= multifuncs[i].nargsm+2 &&\r
+              argc <= multifuncs[i].nargsx+2)\r
+          {\r
+\r
+   multifuncs[i].handler(argv+2, todir, argc-2);\r
+   exit(0);\r
+          }\r
+\r
+ if (argc!=3) ok=0;\r
+\r
+ /* Find the apropriate function_handler */\r
+ if (ok) {\r
+ for(i=0;functions[i].function && strcmp(functions[i].function,argv[1]);i++);\r
+ if (!functions[i].function) ok=0;\r
+ else  if (strcmp(fn,"-")) {\r
+   f=fopen(argv[2],"r");\r
+   if (!f) ok=0;\r
+  }\r
+ }\r
+\r
+ /* Print usage error if anything has gone wrong */\r
+ if (!ok)\r
+ {\r
+  printf("%s: Treaty of Babel Analysis Tool (%s, %s)\n"\r
+         "Usage:\n", argv[0],BABEL_VERSION, TREATY_COMPLIANCE);\r
+  for(i=0;functions[i].function;i++)\r
+  {\r
+   if (functions[i].story)\r
+    printf(" babel %s <storyfile>\n",functions[i].function);\r
+   if (functions[i].ifiction)\r
+    printf(" babel %s <ifictionfile>\n",functions[i].function);\r
+   printf("   %s\n",functions[i].desc);\r
+  }\r
+  for(i=0;multifuncs[i].function;i++)\r
+  {\r
+   printf("babel %s %s\n   %s\n",\r
+           multifuncs[i].function,\r
+           multifuncs[i].argnames,\r
+           multifuncs[i].desc);\r
+  }\r
+\r
+  printf ("\nFor functions which extract files, add \"-to <directory>\" to the command\n"\r
+          "to set the output directory.\n"\r
+          "The input file can be specified as \"-\" to read from standard input\n"\r
+          "(This may only work for .iFiction files)\n");\r
+  return 1;\r
+ }\r
+\r
+ /* For story files, we end up reading the file in twice.  This\r
+    is unfortunate, but unavoidable, since we want to be all\r
+    cross-platformy, so the first time we read it in, we\r
+    do the read in text mode, and the second time, we do it in binary\r
+    mode, and there are platforms where this makes a difference.\r
+ */\r
+ ll=0;\r
+ if (strcmp(fn,"-"))\r
+ {\r
+  fseek(f,0,SEEK_END);\r
+  l=ftell(f)+1;\r
+  fseek(f,0,SEEK_SET);\r
+  md=(char *)my_malloc(l,"Input file buffer");\r
+  fread(md,1,l-1,f);\r
+  md[l-1]=0;\r
+ }\r
+ else\r
+  while(!feof(stdin))\r
+  {\r
+   char *tt, mdb[1024];\r
+   int ii;\r
+   ii=fread(mdb,1,1024,stdin);\r
+   tt=(char *)my_malloc(ll+ii,"file buffer");\r
+   if (md) { memcpy(tt,md,ll); free(md); }\r
+   memcpy(tt+ll,mdb,ii);\r
+   md=tt;\r
+   ll+=ii;\r
+   if (ii<1024) break;\r
+  }\r
+\r
+\r
+  if (strstr(md,"<?xml version=") && strstr(md,"<ifindex"))\r
+  { /* appears to be an ifiction file */\r
+   char *pp;\r
+   pp=strstr(md,"</ifindex>");\r
+   if (pp) *(pp+10)=0;\r
+   getcwd(cwd,512);\r
+   chdir(todir);\r
+   l=0;\r
+   if (functions[i].ifiction)\r
+    functions[i].ifiction(md);\r
+   else\r
+    fprintf(stderr,"Error: option %s is not valid for iFiction files\n",\r
+             argv[1]);\r
+   chdir(cwd);\r
+ }\r
+\r
+ if (strcmp(fn,"-"))\r
+ {\r
+ free(md);\r
+ fclose(f);\r
+ }\r
+ if (l)\r
+ { /* Appears to be a story */\r
+   char *lt;\r
+   if (functions[i].story)\r
+   {\r
+    if (strcmp(fn,"-")) lt=babel_init(argv[2]);\r
+    else { lt=babel_init_raw(md,ll);\r
+           free(md);\r
+         }\r
+\r
+    if (lt)\r
+    {\r
+     getcwd(cwd,512);\r
+     chdir(todir);\r
+     if (!babel_get_authoritative() && strcmp(argv[1],"-format"))\r
+      printf("Warning: Story format could not be positively identified. Guessing %s\n",lt);\r
+     functions[i].story();\r
+\r
+     chdir(cwd);\r
+    }\r
+    else if (strcmp(argv[1],"-ifid")==0) /* IFID is calculable for all files */\r
+    {\r
+     babel_md5_ifid(cwd,512);\r
+     printf("IFID: %s\n",cwd);\r
+    }\r
+    else\r
+     fprintf(stderr,"Error: Did not recognize format of story file\n");\r
+    babel_release();\r
+   }\r
+   else\r
+    fprintf(stderr,"Error: option %s is not valid for story files\n",\r
+             argv[1]);\r
+  }    \r
+\r
+ return 0;\r
+}\r