Merge branch 'master' into browser
[projects/chimara/chimara.git] / babel / babel_handler.c
diff --git a/babel/babel_handler.c b/babel/babel_handler.c
new file mode 100644 (file)
index 0000000..f7afb78
--- /dev/null
@@ -0,0 +1,366 @@
+/* babel_handler.c   dispatches Treaty of Babel queries to the treaty modules\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 register.c, misc.c, babel.h, and treaty.h\r
+ * and L. Peter Deutsch's md5.c\r
+ * usage:\r
+ *  char *babel_init(char *filename)\r
+ *      Initializes babel to use the specified file. MUST be called before\r
+ *      babel_treaty.  Returns the human-readable name of the format\r
+ *      or NULL if the format is not known. Do not call babel_treaty unless\r
+ *      babel_init returned a nonzero value.\r
+ *      The returned string will be the name of a babel format, possibly\r
+ *      prefixed by "blorbed " to indicate that babel will process this file\r
+ *      as a blorb.\r
+ * int32 babel_treaty(int32 selector, void *output, void *output_extent)\r
+ *      Dispatches the call to the treaty handler for the currently loaded\r
+ *      file.\r
+ *      When processing a blorb, all treaty calls will be deflected to the\r
+ *      special blorb handler.  For the case of GET_STORY_FILE_IFID_SEL,\r
+ *      The treaty handler for the underlying format will be called if an\r
+ *      IFID is not found in the blorb resources.\r
+ * void babel_release()\r
+ *      Frees all resources allocated during babel_init.\r
+ *      You should do this even if babel_init returned NULL.\r
+ *      After this is called, do not call babel_treaty until after\r
+ *      another successful call to babel_init.\r
+ * char *babel_get_format()\r
+ *      Returns the same value as the last call to babel_init (ie, the format name)\r
+ * int32 babel_md5_ifid(char *buffer, int extent);\r
+ *      Generates an MD5 IFID from the loaded story.  Returns zero if something\r
+ *      went seriously wrong.\r
+ *\r
+ * If you wish to use babel in multiple threads, you must use the contextualized\r
+ * versions of the above functions.\r
+ * Each function above has a companion function whose name ends in _ctx (eg.\r
+ * "babel_treaty_ctx") which takes one additional argument.  This argument is\r
+ * the babel context. A new context is returned by void *ctx=get_babel_ctx(),\r
+ * and should be released when finished by calling release_babel_ctx(ctx);\r
+ */\r
+\r
+                      \r
+#include "treaty.h"\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <stdio.h>\r
+#include <ctype.h>\r
+#include "md5.h"\r
+\r
+void *my_malloc(int, char *);\r
+\r
+struct babel_handler\r
+{\r
+ TREATY treaty_handler;\r
+ TREATY treaty_backup;\r
+ void *story_file;\r
+ int32 story_file_extent;\r
+ void *story_file_blorbed;\r
+ int32 story_file_blorbed_extent;\r
+ char blorb_mode;\r
+ char *format_name;\r
+ char auth;\r
+};\r
+\r
+static struct babel_handler default_ctx;\r
+\r
+extern TREATY treaty_registry[];\r
+extern TREATY container_registry[];\r
+\r
+static char *deeper_babel_init(char *story_name, void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ int i;\r
+ char *ext;\r
+\r
+ static char buffer[TREATY_MINIMUM_EXTENT];\r
+ int best_candidate;\r
+ char buffert[TREATY_MINIMUM_EXTENT];\r
+\r
+ if (story_name)\r
+  {\r
+   ext=strrchr(story_name,'.');\r
+   if (ext) for(i=0;ext[i];i++) ext[i]=tolower(ext[i]);\r
+  }\r
+ else ext=NULL;\r
+ best_candidate=-1;\r
+ if (ext) /* pass 1: try best candidates */\r
+  for(i=0;container_registry[i];i++)\r
+   if (container_registry[i](GET_FILE_EXTENSIONS_SEL,NULL,0,buffer,TREATY_MINIMUM_EXTENT) >=0 &&\r
+       strstr(buffer,ext) &&\r
+       container_registry[i](CLAIM_STORY_FILE_SEL,bh->story_file,bh->story_file_extent,NULL,0)>=NO_REPLY_RV)\r
+    break;\r
+  if (!ext || !container_registry[i]) /* pass 2: try all candidates */\r
+  {\r
+  \r
+  for(i=0;container_registry[i];i++)\r
+   {int l=container_registry[i](CLAIM_STORY_FILE_SEL,bh->story_file,bh->story_file_extent,NULL,0);\r
+    \r
+    if (l==VALID_STORY_FILE_RV)\r
+    break;\r
+    else if (l==NO_REPLY_RV && best_candidate < 0) best_candidate=i;\r
+    }\r
+}\r
+ if (!container_registry[i] && best_candidate >=0) { bh->auth=0; i=best_candidate; }\r
+ if (container_registry[i])\r
+ {\r
+   char buffer2[TREATY_MINIMUM_EXTENT];\r
+   \r
+   bh->treaty_handler=container_registry[i];\r
+   container_registry[i](GET_FORMAT_NAME_SEL,NULL,0,buffert,TREATY_MINIMUM_EXTENT);\r
+   bh->blorb_mode=1;\r
+\r
+   bh->story_file_blorbed_extent=container_registry[i](CONTAINER_GET_STORY_EXTENT_SEL,bh->story_file,bh->story_file_extent,NULL,0);\r
+   if (bh->story_file_blorbed_extent>0) bh->story_file_blorbed=my_malloc(bh->story_file_blorbed_extent, "contained story file");\r
+   if (bh->story_file_blorbed_extent<=0 ||\r
+       container_registry[i](CONTAINER_GET_STORY_FORMAT_SEL,bh->story_file,bh->story_file_extent,buffer2,TREATY_MINIMUM_EXTENT)<0 ||\r
+       container_registry[i](CONTAINER_GET_STORY_FILE_SEL,bh->story_file,bh->story_file_extent,bh->story_file_blorbed,bh->story_file_blorbed_extent)<=0\r
+      )\r
+    return NULL;\r
\r
+   for(i=0;treaty_registry[i];i++)\r
+    if (treaty_registry[i](GET_FORMAT_NAME_SEL,NULL,0,buffer,TREATY_MINIMUM_EXTENT)>=0 &&\r
+        strcmp(buffer,buffer2)==0 &&\r
+        treaty_registry[i](CLAIM_STORY_FILE_SEL,bh->story_file_blorbed,bh->story_file_blorbed_extent,NULL,0)>=NO_REPLY_RV)\r
+     break;\r
+  if (!treaty_registry[i])\r
+   return NULL;\r
+  bh->treaty_backup=treaty_registry[i];\r
+  sprintf(buffer,"%sed %s",buffert,buffer2);\r
+  return buffer;\r
+  }\r
+\r
+ bh->blorb_mode=0;\r
+ best_candidate=-1;\r
+\r
+ if (ext) /* pass 1: try best candidates */\r
+  for(i=0;treaty_registry[i];i++)\r
+   if (treaty_registry[i](GET_FILE_EXTENSIONS_SEL,NULL,0,buffer,TREATY_MINIMUM_EXTENT) >=0 &&\r
+       strstr(buffer,ext) && \r
+       treaty_registry[i](CLAIM_STORY_FILE_SEL,bh->story_file,bh->story_file_extent,NULL,0)>=NO_REPLY_RV)\r
+    break;\r
+  if (!ext || !treaty_registry[i]) /* pass 2: try all candidates */\r
+  {\r
+  \r
+  for(i=0;treaty_registry[i];i++)\r
+   {int l;\r
+   l=treaty_registry[i](CLAIM_STORY_FILE_SEL,bh->story_file,bh->story_file_extent,NULL,0);\r
+\r
+    if (l==VALID_STORY_FILE_RV)\r
+    break;\r
+    else if (l==NO_REPLY_RV && best_candidate < 0) best_candidate=i;\r
+    }\r
+  }\r
+  if (!treaty_registry[i])\r
+   if (best_candidate>0) { i=best_candidate; bh->auth=0; }\r
+   else return NULL;\r
+  bh->treaty_handler=treaty_registry[i];\r
+\r
+  if (bh->treaty_handler(GET_FORMAT_NAME_SEL,NULL,0,buffer,TREATY_MINIMUM_EXTENT)>=0)\r
+  return buffer;\r
+  return NULL;\r
+\r
+\r
+}\r
+\r
+static char *deep_babel_init(char *story_name, void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ FILE *file;\r
+\r
+ bh->treaty_handler=NULL;\r
+ bh->treaty_backup=NULL;\r
+ bh->story_file=NULL;\r
+ bh->story_file_extent=0;\r
+ bh->story_file_blorbed=NULL;\r
+ bh->story_file_blorbed_extent=0;\r
+ bh->format_name=NULL;\r
+ file=fopen(story_name, "rb");\r
+ if (!file) return NULL;\r
+ fseek(file,0,SEEK_END);\r
+ bh->story_file_extent=ftell(file);\r
+ fseek(file,0,SEEK_SET);\r
+ bh->auth=1; \r
+ bh->story_file=my_malloc(bh->story_file_extent,"story file storage");\r
+ fread(bh->story_file,1,bh->story_file_extent,file);\r
+ fclose(file);\r
+\r
+ return deeper_babel_init(story_name, bhp);\r
+}\r
+\r
+char *babel_init_ctx(char *sf, void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ char *b;\r
+ b=deep_babel_init(sf,bh);\r
+ if (b) bh->format_name=strdup(b);\r
+ return b;\r
+}\r
+char *babel_init(char *sf)\r
+{\r
+  return babel_init_ctx(sf, &default_ctx);\r
+}\r
+\r
+char *babel_init_raw_ctx(void *sf, int32 extent, void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ char *b;\r
+ bh->treaty_handler=NULL;\r
+ bh->treaty_backup=NULL;\r
+ bh->story_file=NULL;\r
+ bh->story_file_extent=0;\r
+ bh->story_file_blorbed=NULL;\r
+ bh->story_file_blorbed_extent=0;\r
+ bh->format_name=NULL;\r
+ bh->story_file_extent=extent;\r
+ bh->auth=1; \r
+ bh->story_file=my_malloc(bh->story_file_extent,"story file storage");\r
+ memcpy(bh->story_file,sf,extent);\r
+\r
+ b=deeper_babel_init(NULL, bhp);\r
+ if (b) bh->format_name=strdup(b);\r
+ return b;\r
+}\r
+char *babel_init_raw(void *sf, int32 extent)\r
+{\r
+  return babel_init_raw_ctx(sf, extent, &default_ctx);\r
+}\r
+\r
+void babel_release_ctx(void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ if (bh->story_file) free(bh->story_file);\r
+ bh->story_file=NULL;\r
+ if (bh->story_file_blorbed) free(bh->story_file_blorbed);\r
+ bh->story_file_blorbed=NULL;\r
+ if (bh->format_name) free(bh->format_name);\r
+ bh->format_name=NULL;\r
+}\r
+void babel_release()\r
+{\r
+ babel_release_ctx(&default_ctx);\r
+}\r
+int32 babel_md5_ifid_ctx(char *buffer, int32 extent, void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ md5_state_t md5;\r
+ int i;\r
+ unsigned char ob[16];\r
+ if (extent <33 || bh->story_file==NULL)\r
+  return 0;\r
+ md5_init(&md5);\r
+ md5_append(&md5,bh->story_file,bh->story_file_extent);\r
+ md5_finish(&md5,ob);\r
+ for(i=0;i<16;i++)\r
+  sprintf(buffer+(2*i),"%02X",ob[i]);\r
+ buffer[32]=0;\r
+ return 1;\r
+\r
+}\r
+int32 babel_md5_ifid(char *buffer, int32 extent)\r
+{\r
+ return babel_md5_ifid_ctx(buffer, extent,\r
+                &default_ctx);\r
+}\r
+\r
+int32 babel_treaty_ctx(int32 sel, void *output, int32 output_extent,void *bhp)\r
+{\r
+ int32 rv;\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ if (!(sel & TREATY_SELECTOR_INPUT) && bh->blorb_mode)\r
+  rv=bh->treaty_backup(sel,bh->story_file_blorbed,bh->story_file_blorbed_extent,output, output_extent);\r
+ else\r
+ {\r
+  rv=bh->treaty_handler(sel,bh->story_file,bh->story_file_extent,output,output_extent);\r
+  if ((!rv|| rv==UNAVAILABLE_RV) && bh->blorb_mode)\r
+   rv=bh->treaty_backup(sel,bh->story_file_blorbed,bh->story_file_blorbed_extent,output, output_extent);\r
+  }\r
+ if (!rv && sel==GET_STORY_FILE_IFID_SEL)\r
+  return babel_md5_ifid_ctx(output,output_extent, bh);\r
+ if (rv==INCOMPLETE_REPLY_RV && sel==GET_STORY_FILE_IFID_SEL)\r
+  return babel_md5_ifid_ctx((void *)((char *) output+strlen((char *)output)),\r
+                            output_extent-strlen((char *)output),\r
+                            bh);\r
+\r
+ return rv;\r
+}\r
+int32 babel_treaty(int32 sel, void *output, int32 output_extent)\r
+{\r
+ return babel_treaty_ctx(sel, output, output_extent, &default_ctx);\r
+}\r
+char *babel_get_format_ctx(void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ return bh->format_name;\r
+}\r
+char *babel_get_format()\r
+{\r
+ return babel_get_format_ctx(&default_ctx);\r
+}\r
+void *get_babel_ctx()\r
+{\r
+ return my_malloc(sizeof(struct babel_handler), "babel handler context");\r
+}\r
+void release_babel_ctx(void *b)\r
+{\r
+ free(b);\r
+}\r
+\r
+int32 babel_get_length_ctx(void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ return bh->story_file_extent;\r
+}\r
+int32 babel_get_length()\r
+{\r
+ return babel_get_length_ctx(&default_ctx);\r
+}\r
+\r
+int32 babel_get_authoritative_ctx(void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ return bh->auth;\r
+}\r
+int32 babel_get_authoritative()\r
+{\r
+  return babel_get_authoritative_ctx(&default_ctx);\r
+}\r
+void *babel_get_file_ctx(void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ return bh->story_file;\r
+}\r
+void *babel_get_file()\r
+{\r
+ return babel_get_file_ctx(&default_ctx);\r
+}\r
+\r
+int32 babel_get_story_length_ctx(void *ctx)\r
+{\r
+  struct babel_handler *bh=(struct babel_handler *) ctx;\r
+  if (bh->blorb_mode) return bh->story_file_blorbed_extent;\r
+  return bh->story_file_extent;\r
+}\r
+int32 babel_get_story_length()\r
+{\r
+\r
+ return babel_get_story_length_ctx(&default_ctx);\r
+}\r
+void *babel_get_story_file_ctx(void *ctx)\r
+{\r
+  struct babel_handler *bh=(struct babel_handler *) ctx;\r
+  if (bh->blorb_mode) return bh->story_file_blorbed;\r
+  return bh->story_file;\r
+}\r
+void *babel_get_story_file()\r
+{\r
+ return babel_get_story_file_ctx(&default_ctx);\r
+}\r