Merge branch 'master' into gtk3
[projects/chimara/chimara.git] / babel / babel.c
1 /* babel.c   The babel command line program\r
2  * (c) 2006 By L. Ross Raszewski\r
3  *\r
4  * This code is freely usable for all purposes.\r
5  *\r
6  * This work is licensed under the Creative Commons Attribution2.5 License.\r
7  * To view a copy of this license, visit\r
8  * http://creativecommons.org/licenses/by/2.5/ or send a letter to\r
9  * Creative Commons,\r
10  * 543 Howard Street, 5th Floor,\r
11  * San Francisco, California, 94105, USA.\r
12  *\r
13  * This file depends upon misc.c and babel.h\r
14  *\r
15  * This file exports one variable: char *rv, which points to the file name\r
16  * for an ifiction file.  This is used only by babel_ifiction_verify\r
17  */\r
18 \r
19 #include "babel.h"\r
20 #include <stdio.h>\r
21 #include <stdlib.h>\r
22 #include <string.h>\r
23 \r
24 #ifdef __cplusplus\r
25 extern "C" {\r
26 #endif\r
27 int chdir(const char *);\r
28 char *getcwd(char *, int);\r
29 #ifdef __cplusplus\r
30 }\r
31 #endif\r
32 \r
33 char *fn;\r
34 \r
35 /* checked malloc function */\r
36 void *my_malloc(int, char *);\r
37 \r
38 /* babel performs several fundamental operations, which are specified\r
39    by command-line objects. Each of these functions corresponds to\r
40    a story function (defined in babel_story_functions.c) or an\r
41    ifiction function (defined in babel_ifiction_functions.c) or both.\r
42    These are the types of those functions.\r
43 */\r
44 \r
45 typedef void (*story_function)(void);\r
46 typedef void (*ifiction_function)(char *);\r
47 typedef void (*multi_function)(char **, char *, int);\r
48 /* This structure tells babel what to do with its command line arguments.\r
49    if either of story or ifiction are NULL, babel considers this command line\r
50    option inappropriate for that type of file.\r
51 */\r
52 struct function_handler {\r
53         char *function;         /* the textual command line option */\r
54         story_function story;   /* handler for story files */\r
55         ifiction_function ifiction; /* handler for ifiction files */\r
56         char *desc;             /* Textual description for help text */\r
57         };\r
58 \r
59 struct multi_handler {\r
60         char *function;\r
61         char *argnames;\r
62         multi_function handler;\r
63         int nargsm;\r
64         int nargsx;\r
65         char *desc;\r
66         };\r
67 /* This is an array of function_handler objects which specify the legal\r
68    arguments.  It is terminated by a function_handler with a NULL function\r
69  */\r
70 static struct function_handler functions[] = {\r
71         { "-ifid", babel_story_ifid, babel_ifiction_ifid, "Deduce IFID"},\r
72         { "-format", babel_story_format, NULL, "Deduce story format" },\r
73         { "-ifiction", babel_story_ifiction, NULL, "Extract iFiction file" },\r
74         { "-meta", babel_story_meta, NULL, "Print story metadata" },\r
75         { "-identify", babel_story_identify, NULL, "Describe story file" },\r
76         { "-cover", babel_story_cover, NULL, "Extract cover art" },\r
77         { "-story", babel_story_story, NULL, "Extract story file (ie. from a blorb)" },\r
78         { "-verify", NULL, babel_ifiction_verify, "Verify integrity of iFiction file" },\r
79         { "-lint", NULL, babel_ifiction_lint, "Verify style of iFiction file" },\r
80         { "-fish", babel_story_fish, babel_ifiction_fish, "Extract all iFiction and cover art"},\r
81         { "-unblorb", babel_story_unblorb, NULL, "As -fish, but also extract story files"},\r
82         { NULL, NULL, NULL }\r
83         };\r
84 static struct multi_handler multifuncs[] = {\r
85         { "-blorb", "<storyfile> <ifictionfile> [<cover art>]", babel_multi_blorb, 2, 3, "Bundle story file and (sparse) iFiction into blorb" },\r
86         { "-blorbs", "<storyfile> <ifictionfile> [<cover art>]", babel_multi_blorb1, 2, 3, "Bundle story file and (sparse) iFiction into sensibly-named blorb" },\r
87         { "-complete", "<storyfile> <ifictionfile>", babel_multi_complete, 2, 2, "Create complete iFiction file from sparse iFiction" },\r
88         { NULL, NULL, NULL, 0, 0, NULL }\r
89 };\r
90 \r
91 int main(int argc, char **argv)\r
92 {\r
93  char *todir=".";\r
94  char cwd[512];\r
95  int ok=1,i, l, ll;\r
96  FILE *f;\r
97  char *md=NULL;\r
98  /* Set the input filename.  Note that if this is invalid, babel should\r
99    abort before anyone notices\r
100  */\r
101  fn=argv[2];\r
102 \r
103  if (argc < 3) ok=0;\r
104  /* Detect the presence of the "-to <directory>" argument.\r
105   */\r
106  if (ok && argc >=5 && strcmp(argv[argc-2], "-to")==0)\r
107  {\r
108   todir=argv[argc-1];\r
109   argc-=2;\r
110  }\r
111  if (ok) for(i=0;multifuncs[i].function;i++)\r
112           if (strcmp(argv[1],multifuncs[i].function)==0 &&\r
113               argc>= multifuncs[i].nargsm+2 &&\r
114               argc <= multifuncs[i].nargsx+2)\r
115           {\r
116 \r
117    multifuncs[i].handler(argv+2, todir, argc-2);\r
118    exit(0);\r
119           }\r
120 \r
121  if (argc!=3) ok=0;\r
122 \r
123  /* Find the apropriate function_handler */\r
124  if (ok) {\r
125  for(i=0;functions[i].function && strcmp(functions[i].function,argv[1]);i++);\r
126  if (!functions[i].function) ok=0;\r
127  else  if (strcmp(fn,"-")) {\r
128    f=fopen(argv[2],"r");\r
129    if (!f) ok=0;\r
130   }\r
131  }\r
132 \r
133  /* Print usage error if anything has gone wrong */\r
134  if (!ok)\r
135  {\r
136   printf("%s: Treaty of Babel Analysis Tool (%s, %s)\n"\r
137          "Usage:\n", argv[0],BABEL_VERSION, TREATY_COMPLIANCE);\r
138   for(i=0;functions[i].function;i++)\r
139   {\r
140    if (functions[i].story)\r
141     printf(" babel %s <storyfile>\n",functions[i].function);\r
142    if (functions[i].ifiction)\r
143     printf(" babel %s <ifictionfile>\n",functions[i].function);\r
144    printf("   %s\n",functions[i].desc);\r
145   }\r
146   for(i=0;multifuncs[i].function;i++)\r
147   {\r
148    printf("babel %s %s\n   %s\n",\r
149            multifuncs[i].function,\r
150            multifuncs[i].argnames,\r
151            multifuncs[i].desc);\r
152   }\r
153 \r
154   printf ("\nFor functions which extract files, add \"-to <directory>\" to the command\n"\r
155           "to set the output directory.\n"\r
156           "The input file can be specified as \"-\" to read from standard input\n"\r
157           "(This may only work for .iFiction files)\n");\r
158   return 1;\r
159  }\r
160 \r
161  /* For story files, we end up reading the file in twice.  This\r
162     is unfortunate, but unavoidable, since we want to be all\r
163     cross-platformy, so the first time we read it in, we\r
164     do the read in text mode, and the second time, we do it in binary\r
165     mode, and there are platforms where this makes a difference.\r
166  */\r
167  ll=0;\r
168  if (strcmp(fn,"-"))\r
169  {\r
170   fseek(f,0,SEEK_END);\r
171   l=ftell(f)+1;\r
172   fseek(f,0,SEEK_SET);\r
173   md=(char *)my_malloc(l,"Input file buffer");\r
174   fread(md,1,l-1,f);\r
175   md[l-1]=0;\r
176  }\r
177  else\r
178   while(!feof(stdin))\r
179   {\r
180    char *tt, mdb[1024];\r
181    int ii;\r
182    ii=fread(mdb,1,1024,stdin);\r
183    tt=(char *)my_malloc(ll+ii,"file buffer");\r
184    if (md) { memcpy(tt,md,ll); free(md); }\r
185    memcpy(tt+ll,mdb,ii);\r
186    md=tt;\r
187    ll+=ii;\r
188    if (ii<1024) break;\r
189   }\r
190 \r
191 \r
192   if (strstr(md,"<?xml version=") && strstr(md,"<ifindex"))\r
193   { /* appears to be an ifiction file */\r
194    char *pp;\r
195    pp=strstr(md,"</ifindex>");\r
196    if (pp) *(pp+10)=0;\r
197    getcwd(cwd,512);\r
198    chdir(todir);\r
199    l=0;\r
200    if (functions[i].ifiction)\r
201     functions[i].ifiction(md);\r
202    else\r
203     fprintf(stderr,"Error: option %s is not valid for iFiction files\n",\r
204              argv[1]);\r
205    chdir(cwd);\r
206  }\r
207 \r
208  if (strcmp(fn,"-"))\r
209  {\r
210  free(md);\r
211  fclose(f);\r
212  }\r
213  if (l)\r
214  { /* Appears to be a story */\r
215    char *lt;\r
216    if (functions[i].story)\r
217    {\r
218     if (strcmp(fn,"-")) lt=babel_init(argv[2]);\r
219     else { lt=babel_init_raw(md,ll);\r
220            free(md);\r
221          }\r
222 \r
223     if (lt)\r
224     {\r
225      getcwd(cwd,512);\r
226      chdir(todir);\r
227      if (!babel_get_authoritative() && strcmp(argv[1],"-format"))\r
228       printf("Warning: Story format could not be positively identified. Guessing %s\n",lt);\r
229      functions[i].story();\r
230 \r
231      chdir(cwd);\r
232     }\r
233     else if (strcmp(argv[1],"-ifid")==0) /* IFID is calculable for all files */\r
234     {\r
235      babel_md5_ifid(cwd,512);\r
236      printf("IFID: %s\n",cwd);\r
237     }\r
238     else\r
239      fprintf(stderr,"Error: Did not recognize format of story file\n");\r
240     babel_release();\r
241    }\r
242    else\r
243     fprintf(stderr,"Error: option %s is not valid for story files\n",\r
244              argv[1]);\r
245   }    \r
246 \r
247  return 0;\r
248 }\r