Merge branch 'master' into gtk3
[projects/chimara/chimara.git] / babel / babel_handler.c
1 /* babel_handler.c   dispatches Treaty of Babel queries to the treaty modules\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 register.c, misc.c, babel.h, and treaty.h\r
14  * and L. Peter Deutsch's md5.c\r
15  * usage:\r
16  *  char *babel_init(char *filename)\r
17  *      Initializes babel to use the specified file. MUST be called before\r
18  *      babel_treaty.  Returns the human-readable name of the format\r
19  *      or NULL if the format is not known. Do not call babel_treaty unless\r
20  *      babel_init returned a nonzero value.\r
21  *      The returned string will be the name of a babel format, possibly\r
22  *      prefixed by "blorbed " to indicate that babel will process this file\r
23  *      as a blorb.\r
24  * int32 babel_treaty(int32 selector, void *output, void *output_extent)\r
25  *      Dispatches the call to the treaty handler for the currently loaded\r
26  *      file.\r
27  *      When processing a blorb, all treaty calls will be deflected to the\r
28  *      special blorb handler.  For the case of GET_STORY_FILE_IFID_SEL,\r
29  *      The treaty handler for the underlying format will be called if an\r
30  *      IFID is not found in the blorb resources.\r
31  * void babel_release()\r
32  *      Frees all resources allocated during babel_init.\r
33  *      You should do this even if babel_init returned NULL.\r
34  *      After this is called, do not call babel_treaty until after\r
35  *      another successful call to babel_init.\r
36  * char *babel_get_format()\r
37  *      Returns the same value as the last call to babel_init (ie, the format name)\r
38  * int32 babel_md5_ifid(char *buffer, int extent);\r
39  *      Generates an MD5 IFID from the loaded story.  Returns zero if something\r
40  *      went seriously wrong.\r
41  *\r
42  * If you wish to use babel in multiple threads, you must use the contextualized\r
43  * versions of the above functions.\r
44  * Each function above has a companion function whose name ends in _ctx (eg.\r
45  * "babel_treaty_ctx") which takes one additional argument.  This argument is\r
46  * the babel context. A new context is returned by void *ctx=get_babel_ctx(),\r
47  * and should be released when finished by calling release_babel_ctx(ctx);\r
48  */\r
49 \r
50                       \r
51 #include "treaty.h"\r
52 #include <stdlib.h>\r
53 #include <string.h>\r
54 #include <stdio.h>\r
55 #include <ctype.h>\r
56 #include "md5.h"\r
57 \r
58 void *my_malloc(int, char *);\r
59 \r
60 struct babel_handler\r
61 {\r
62  TREATY treaty_handler;\r
63  TREATY treaty_backup;\r
64  void *story_file;\r
65  int32 story_file_extent;\r
66  void *story_file_blorbed;\r
67  int32 story_file_blorbed_extent;\r
68  char blorb_mode;\r
69  char *format_name;\r
70  char auth;\r
71 };\r
72 \r
73 static struct babel_handler default_ctx;\r
74 \r
75 extern TREATY treaty_registry[];\r
76 extern TREATY container_registry[];\r
77 \r
78 static char *deeper_babel_init(char *story_name, void *bhp)\r
79 {\r
80  struct babel_handler *bh=(struct babel_handler *) bhp;\r
81  int i;\r
82  char *ext;\r
83 \r
84  static char buffer[TREATY_MINIMUM_EXTENT];\r
85  int best_candidate;\r
86  char buffert[TREATY_MINIMUM_EXTENT];\r
87 \r
88  if (story_name)\r
89   {\r
90    ext=strrchr(story_name,'.');\r
91    if (ext) for(i=0;ext[i];i++) ext[i]=tolower(ext[i]);\r
92   }\r
93  else ext=NULL;\r
94  best_candidate=-1;\r
95  if (ext) /* pass 1: try best candidates */\r
96   for(i=0;container_registry[i];i++)\r
97    if (container_registry[i](GET_FILE_EXTENSIONS_SEL,NULL,0,buffer,TREATY_MINIMUM_EXTENT) >=0 &&\r
98        strstr(buffer,ext) &&\r
99        container_registry[i](CLAIM_STORY_FILE_SEL,bh->story_file,bh->story_file_extent,NULL,0)>=NO_REPLY_RV)\r
100     break;\r
101   if (!ext || !container_registry[i]) /* pass 2: try all candidates */\r
102   {\r
103   \r
104   for(i=0;container_registry[i];i++)\r
105    {int l=container_registry[i](CLAIM_STORY_FILE_SEL,bh->story_file,bh->story_file_extent,NULL,0);\r
106     \r
107     if (l==VALID_STORY_FILE_RV)\r
108     break;\r
109     else if (l==NO_REPLY_RV && best_candidate < 0) best_candidate=i;\r
110     }\r
111 }\r
112  if (!container_registry[i] && best_candidate >=0) { bh->auth=0; i=best_candidate; }\r
113  if (container_registry[i])\r
114  {\r
115    char buffer2[TREATY_MINIMUM_EXTENT];\r
116    \r
117    bh->treaty_handler=container_registry[i];\r
118    container_registry[i](GET_FORMAT_NAME_SEL,NULL,0,buffert,TREATY_MINIMUM_EXTENT);\r
119    bh->blorb_mode=1;\r
120 \r
121    bh->story_file_blorbed_extent=container_registry[i](CONTAINER_GET_STORY_EXTENT_SEL,bh->story_file,bh->story_file_extent,NULL,0);\r
122    if (bh->story_file_blorbed_extent>0) bh->story_file_blorbed=my_malloc(bh->story_file_blorbed_extent, "contained story file");\r
123    if (bh->story_file_blorbed_extent<=0 ||\r
124        container_registry[i](CONTAINER_GET_STORY_FORMAT_SEL,bh->story_file,bh->story_file_extent,buffer2,TREATY_MINIMUM_EXTENT)<0 ||\r
125        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
126       )\r
127     return NULL;\r
128  \r
129    for(i=0;treaty_registry[i];i++)\r
130     if (treaty_registry[i](GET_FORMAT_NAME_SEL,NULL,0,buffer,TREATY_MINIMUM_EXTENT)>=0 &&\r
131         strcmp(buffer,buffer2)==0 &&\r
132         treaty_registry[i](CLAIM_STORY_FILE_SEL,bh->story_file_blorbed,bh->story_file_blorbed_extent,NULL,0)>=NO_REPLY_RV)\r
133      break;\r
134   if (!treaty_registry[i])\r
135    return NULL;\r
136   bh->treaty_backup=treaty_registry[i];\r
137   sprintf(buffer,"%sed %s",buffert,buffer2);\r
138   return buffer;\r
139   }\r
140 \r
141  bh->blorb_mode=0;\r
142  best_candidate=-1;\r
143 \r
144  if (ext) /* pass 1: try best candidates */\r
145   for(i=0;treaty_registry[i];i++)\r
146    if (treaty_registry[i](GET_FILE_EXTENSIONS_SEL,NULL,0,buffer,TREATY_MINIMUM_EXTENT) >=0 &&\r
147        strstr(buffer,ext) && \r
148        treaty_registry[i](CLAIM_STORY_FILE_SEL,bh->story_file,bh->story_file_extent,NULL,0)>=NO_REPLY_RV)\r
149     break;\r
150   if (!ext || !treaty_registry[i]) /* pass 2: try all candidates */\r
151   {\r
152   \r
153   for(i=0;treaty_registry[i];i++)\r
154    {int l;\r
155    l=treaty_registry[i](CLAIM_STORY_FILE_SEL,bh->story_file,bh->story_file_extent,NULL,0);\r
156 \r
157     if (l==VALID_STORY_FILE_RV)\r
158     break;\r
159     else if (l==NO_REPLY_RV && best_candidate < 0) best_candidate=i;\r
160     }\r
161   }\r
162   if (!treaty_registry[i])\r
163    if (best_candidate>0) { i=best_candidate; bh->auth=0; }\r
164    else return NULL;\r
165   bh->treaty_handler=treaty_registry[i];\r
166 \r
167   if (bh->treaty_handler(GET_FORMAT_NAME_SEL,NULL,0,buffer,TREATY_MINIMUM_EXTENT)>=0)\r
168   return buffer;\r
169   return NULL;\r
170 \r
171 \r
172 }\r
173 \r
174 static char *deep_babel_init(char *story_name, void *bhp)\r
175 {\r
176  struct babel_handler *bh=(struct babel_handler *) bhp;\r
177  FILE *file;\r
178 \r
179  bh->treaty_handler=NULL;\r
180  bh->treaty_backup=NULL;\r
181  bh->story_file=NULL;\r
182  bh->story_file_extent=0;\r
183  bh->story_file_blorbed=NULL;\r
184  bh->story_file_blorbed_extent=0;\r
185  bh->format_name=NULL;\r
186  file=fopen(story_name, "rb");\r
187  if (!file) return NULL;\r
188  fseek(file,0,SEEK_END);\r
189  bh->story_file_extent=ftell(file);\r
190  fseek(file,0,SEEK_SET);\r
191  bh->auth=1; \r
192  bh->story_file=my_malloc(bh->story_file_extent,"story file storage");\r
193  fread(bh->story_file,1,bh->story_file_extent,file);\r
194  fclose(file);\r
195 \r
196  return deeper_babel_init(story_name, bhp);\r
197 }\r
198 \r
199 char *babel_init_ctx(char *sf, void *bhp)\r
200 {\r
201  struct babel_handler *bh=(struct babel_handler *) bhp;\r
202  char *b;\r
203  b=deep_babel_init(sf,bh);\r
204  if (b) bh->format_name=strdup(b);\r
205  return b;\r
206 }\r
207 char *babel_init(char *sf)\r
208 {\r
209   return babel_init_ctx(sf, &default_ctx);\r
210 }\r
211 \r
212 char *babel_init_raw_ctx(void *sf, int32 extent, void *bhp)\r
213 {\r
214  struct babel_handler *bh=(struct babel_handler *) bhp;\r
215  char *b;\r
216  bh->treaty_handler=NULL;\r
217  bh->treaty_backup=NULL;\r
218  bh->story_file=NULL;\r
219  bh->story_file_extent=0;\r
220  bh->story_file_blorbed=NULL;\r
221  bh->story_file_blorbed_extent=0;\r
222  bh->format_name=NULL;\r
223  bh->story_file_extent=extent;\r
224  bh->auth=1; \r
225  bh->story_file=my_malloc(bh->story_file_extent,"story file storage");\r
226  memcpy(bh->story_file,sf,extent);\r
227 \r
228  b=deeper_babel_init(NULL, bhp);\r
229  if (b) bh->format_name=strdup(b);\r
230  return b;\r
231 }\r
232 char *babel_init_raw(void *sf, int32 extent)\r
233 {\r
234   return babel_init_raw_ctx(sf, extent, &default_ctx);\r
235 }\r
236 \r
237 void babel_release_ctx(void *bhp)\r
238 {\r
239  struct babel_handler *bh=(struct babel_handler *) bhp;\r
240  if (bh->story_file) free(bh->story_file);\r
241  bh->story_file=NULL;\r
242  if (bh->story_file_blorbed) free(bh->story_file_blorbed);\r
243  bh->story_file_blorbed=NULL;\r
244  if (bh->format_name) free(bh->format_name);\r
245  bh->format_name=NULL;\r
246 }\r
247 void babel_release()\r
248 {\r
249  babel_release_ctx(&default_ctx);\r
250 }\r
251 int32 babel_md5_ifid_ctx(char *buffer, int32 extent, void *bhp)\r
252 {\r
253  struct babel_handler *bh=(struct babel_handler *) bhp;\r
254  md5_state_t md5;\r
255  int i;\r
256  unsigned char ob[16];\r
257  if (extent <33 || bh->story_file==NULL)\r
258   return 0;\r
259  md5_init(&md5);\r
260  md5_append(&md5,bh->story_file,bh->story_file_extent);\r
261  md5_finish(&md5,ob);\r
262  for(i=0;i<16;i++)\r
263   sprintf(buffer+(2*i),"%02X",ob[i]);\r
264  buffer[32]=0;\r
265  return 1;\r
266 \r
267 }\r
268 int32 babel_md5_ifid(char *buffer, int32 extent)\r
269 {\r
270  return babel_md5_ifid_ctx(buffer, extent,\r
271                 &default_ctx);\r
272 }\r
273 \r
274 int32 babel_treaty_ctx(int32 sel, void *output, int32 output_extent,void *bhp)\r
275 {\r
276  int32 rv;\r
277  struct babel_handler *bh=(struct babel_handler *) bhp;\r
278  if (!(sel & TREATY_SELECTOR_INPUT) && bh->blorb_mode)\r
279   rv=bh->treaty_backup(sel,bh->story_file_blorbed,bh->story_file_blorbed_extent,output, output_extent);\r
280  else\r
281  {\r
282   rv=bh->treaty_handler(sel,bh->story_file,bh->story_file_extent,output,output_extent);\r
283   if ((!rv|| rv==UNAVAILABLE_RV) && bh->blorb_mode)\r
284    rv=bh->treaty_backup(sel,bh->story_file_blorbed,bh->story_file_blorbed_extent,output, output_extent);\r
285   }\r
286  if (!rv && sel==GET_STORY_FILE_IFID_SEL)\r
287   return babel_md5_ifid_ctx(output,output_extent, bh);\r
288  if (rv==INCOMPLETE_REPLY_RV && sel==GET_STORY_FILE_IFID_SEL)\r
289   return babel_md5_ifid_ctx((void *)((char *) output+strlen((char *)output)),\r
290                             output_extent-strlen((char *)output),\r
291                             bh);\r
292 \r
293  return rv;\r
294 }\r
295 int32 babel_treaty(int32 sel, void *output, int32 output_extent)\r
296 {\r
297  return babel_treaty_ctx(sel, output, output_extent, &default_ctx);\r
298 }\r
299 char *babel_get_format_ctx(void *bhp)\r
300 {\r
301  struct babel_handler *bh=(struct babel_handler *) bhp;\r
302  return bh->format_name;\r
303 }\r
304 char *babel_get_format()\r
305 {\r
306  return babel_get_format_ctx(&default_ctx);\r
307 }\r
308 void *get_babel_ctx()\r
309 {\r
310  return my_malloc(sizeof(struct babel_handler), "babel handler context");\r
311 }\r
312 void release_babel_ctx(void *b)\r
313 {\r
314  free(b);\r
315 }\r
316 \r
317 int32 babel_get_length_ctx(void *bhp)\r
318 {\r
319  struct babel_handler *bh=(struct babel_handler *) bhp;\r
320  return bh->story_file_extent;\r
321 }\r
322 int32 babel_get_length()\r
323 {\r
324  return babel_get_length_ctx(&default_ctx);\r
325 }\r
326 \r
327 int32 babel_get_authoritative_ctx(void *bhp)\r
328 {\r
329  struct babel_handler *bh=(struct babel_handler *) bhp;\r
330  return bh->auth;\r
331 }\r
332 int32 babel_get_authoritative()\r
333 {\r
334   return babel_get_authoritative_ctx(&default_ctx);\r
335 }\r
336 void *babel_get_file_ctx(void *bhp)\r
337 {\r
338  struct babel_handler *bh=(struct babel_handler *) bhp;\r
339  return bh->story_file;\r
340 }\r
341 void *babel_get_file()\r
342 {\r
343  return babel_get_file_ctx(&default_ctx);\r
344 }\r
345 \r
346 int32 babel_get_story_length_ctx(void *ctx)\r
347 {\r
348   struct babel_handler *bh=(struct babel_handler *) ctx;\r
349   if (bh->blorb_mode) return bh->story_file_blorbed_extent;\r
350   return bh->story_file_extent;\r
351 }\r
352 int32 babel_get_story_length()\r
353 {\r
354 \r
355  return babel_get_story_length_ctx(&default_ctx);\r
356 }\r
357 void *babel_get_story_file_ctx(void *ctx)\r
358 {\r
359   struct babel_handler *bh=(struct babel_handler *) ctx;\r
360   if (bh->blorb_mode) return bh->story_file_blorbed;\r
361   return bh->story_file;\r
362 }\r
363 void *babel_get_story_file()\r
364 {\r
365  return babel_get_story_file_ctx(&default_ctx);\r
366 }\r