Squash  0.7.0
context.c
1 /* Copyright (c) 2013 The Squash Authors
2  *
3  * Permission is hereby granted, free of charge, to any person
4  * obtaining a copy of this software and associated documentation
5  * files (the "Software"), to deal in the Software without
6  * restriction, including without limitation the rights to use, copy,
7  * modify, merge, publish, distribute, sublicense, and/or sell copies
8  * of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be
12  * included in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
18  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
19  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  *
23  * Authors:
24  * Evan Nemerson <evan@nemerson.com>
25  */
26 
27 #define _DEFAULT_SOURCE
28 #define _BSD_SOURCE
29 #define _POSIX_SOURCE
30 #define _POSIX_C_SOURCE 200809L
31 
32 #include <assert.h>
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <stddef.h>
39 #include <ctype.h>
40 #include <stdio.h>
41 #include <strings.h>
42 
43 #if !defined(_WIN32)
44  #include <dirent.h>
45 #else
46  #include <windows.h>
47  #include <tchar.h>
48  #include <strsafe.h>
49 #endif
50 
51 #include "internal.h"
52 #include "tinycthread/source/tinycthread.h"
53 
54 #if !defined(_WIN32)
55 #define SQUASH_STRTOK_R(str,delim,saveptr) strtok_r(str,delim,saveptr)
56 #define squash_strndup(s,n) strndup(s,n)
57 #else
58 static char* squash_strndup(const char* s, size_t n);
59 #define SQUASH_STRTOK_R(str,delim,saveptr) strtok_s(str,delim,saveptr)
60 #endif
61 
79 static SquashCodecRef*
80 squash_context_get_codec_ref (SquashContext* context, const char* codec) {
81  SquashCodec key_codec = { 0, };
82  SquashCodecRef key = { &key_codec, };
83 
84  assert (context != NULL);
85  assert (codec != NULL);
86 
87  key_codec.name = (char*) codec;
88 
89  return SQUASH_TREE_FIND (&(context->codecs), _SquashCodecRef, tree, &key);
90 }
91 
92 static SquashCodecRef*
93 squash_context_get_codec_ref_from_extension (SquashContext* context, const char* extension) {
94  SquashCodec key_codec = { 0, };
95  SquashCodecRef key = { &key_codec, };
96 
97  assert (context != NULL);
98  assert (extension != NULL);
99 
100  key_codec.extension = (char*) extension;
101 
102  return SQUASH_TREE_FIND (&(context->extensions), _SquashCodecRef, tree, &key);
103 }
104 
113 SquashCodec*
114 squash_context_get_codec (SquashContext* context, const char* codec) {
115  const char* sep_pos = strchr (codec, ':');
116  if (sep_pos != NULL) {
117  char* plugin_name = (char*) malloc ((sep_pos - codec) + 1);
118 
119  strncpy (plugin_name, codec, sep_pos - codec);
120  plugin_name[sep_pos - codec] = 0;
121  codec = sep_pos + 1;
122 
123  SquashPlugin* plugin = squash_context_get_plugin (context, plugin_name);
124  free (plugin_name);
125  if (plugin == NULL)
126  return NULL;
127 
128  return squash_plugin_get_codec (plugin, codec);
129  } else {
130  SquashCodecRef* codec_ref = squash_context_get_codec_ref (context, codec);
131  if (codec_ref != NULL) {
132  /* TODO: we should probably see if we can load the codec from a
133  different plugin if this fails. */
134  return (squash_codec_init (codec_ref->codec) == SQUASH_OK) ? codec_ref->codec : NULL;
135  } else {
136  return NULL;
137  }
138  }
139 }
140 
148 SquashCodec*
149 squash_get_codec (const char* codec) {
151 }
152 
160 SquashCodec*
161 squash_context_get_codec_from_extension (SquashContext* context, const char* extension) {
162  SquashCodecRef* codec_ref = squash_context_get_codec_ref_from_extension (context, extension);
163  if (codec_ref != NULL) {
164  return (squash_codec_init (codec_ref->codec) == SQUASH_OK) ? codec_ref->codec : NULL;
165  } else {
166  return NULL;
167  }
168 }
169 
176 SquashCodec*
177 squash_get_codec_from_extension (const char* extension) {
179 }
180 
189 SquashPlugin*
190 squash_context_get_plugin (SquashContext* context, const char* plugin) {
191  SquashPlugin plugin_dummy = { 0, };
192 
193  assert (context != NULL);
194  assert (plugin != NULL);
195 
196  plugin_dummy.name = (char*) plugin;
197 
198  return SQUASH_TREE_FIND (&(context->plugins), _SquashPlugin, tree, &plugin_dummy);
199 }
200 
208 SquashPlugin*
209 squash_get_plugin (const char* plugin) {
211 }
212 
213 static int
214 squash_codec_ref_compare (SquashCodecRef* a, SquashCodecRef* b) {
215  return squash_codec_compare (a->codec, b->codec);
216 }
217 
218 static int
219 squash_codec_ref_extension_compare (SquashCodecRef* a, SquashCodecRef* b) {
220  return squash_codec_extension_compare (a->codec, b->codec);
221 }
222 
223 static SquashPlugin*
224 squash_context_add_plugin (SquashContext* context, char* name, char* directory) {
225  SquashPlugin* plugin = NULL;
226  SquashPlugin plugin_dummy = { 0, };
227 
228  assert (context != NULL);
229  assert (name != NULL);
230  assert (directory != NULL);
231 
232  plugin_dummy.name = name;
233 
234  plugin = SQUASH_TREE_FIND (&(context->plugins), _SquashPlugin, tree, &plugin_dummy);
235  if (plugin == NULL) {
236  plugin = squash_plugin_new (name, directory, context);
237 
238  SQUASH_TREE_INSERT(&(context->plugins), _SquashPlugin, tree, plugin);
239  } else {
240  free (name);
241  free (directory);
242 
243  plugin = NULL;
244  }
245 
246  return plugin;
247 }
248 
262 void
263 squash_context_add_codec (SquashContext* context, SquashCodec* codec) {
264  SquashCodecRef* codec_ref;
265 
266  assert (context != NULL);
267  assert (codec != NULL);
268 
269  codec_ref = squash_context_get_codec_ref (context, codec->name);
270  if (codec_ref == NULL) {
271  /* Insert a new entry into context->codecs */
272  codec_ref = (SquashCodecRef*) malloc (sizeof (SquashCodecRef));
273  codec_ref->codec = codec;
274  SQUASH_TREE_ENTRY_INIT(codec_ref->tree);
275  SQUASH_TREE_INSERT (&(context->codecs), _SquashCodecRef, tree, codec_ref);
276  } else if (codec->priority > codec_ref->codec->priority) {
277  /* Switch the existing context codec's details to this one */
278  codec_ref->codec = codec;
279  }
280 
281  if (codec->extension != NULL) {
282  codec_ref = squash_context_get_codec_ref_from_extension (context, codec->extension);
283  if (codec_ref == NULL) {
284  codec_ref = (SquashCodecRef*) malloc (sizeof (SquashCodecRef));
285  codec_ref->codec = codec;
286  SQUASH_TREE_ENTRY_INIT(codec_ref->tree);
287  SQUASH_TREE_INSERT (&(context->extensions), _SquashCodecRef, tree, codec_ref);
288  } else if (codec->priority > codec_ref->codec->priority) {
289  codec_ref->codec = codec;
290  }
291  }
292 }
293 
297 typedef struct _SquashCodecsFileParser {
298  SquashPlugin* plugin;
299  SquashCodec* codec;
300 } SquashCodecsFileParser;
301 
302 static bool
303 squash_codecs_file_parser_callback (const char* section,
304  const char* key,
305  const char* value,
306  size_t value_length,
307  void* user_data) {
308  SquashCodecsFileParser* parser = (SquashCodecsFileParser*) user_data;
309 
310  if (key == NULL) {
311  if (parser->codec != NULL)
312  squash_plugin_add_codec (parser->plugin, parser->codec);
313  parser->codec = squash_codec_new (parser->plugin, section);
314  } else {
315  if (strcasecmp (key, "license") == 0) {
316  size_t n = 0;
317  if (parser->plugin->license != NULL) {
318  free (parser->plugin->license);
319  parser->plugin->license = NULL;
320  }
321 
322  char* licenses = strdup (value);
323  char* saveptr = NULL;
324  char* license = strtok_r (licenses, ";", &saveptr);
325 
326  while (license != NULL) {
327  SquashLicense license_value = squash_license_from_string (license);
328  if (license_value != SQUASH_LICENSE_UNKNOWN) {
329  parser->plugin->license = realloc (parser->plugin->license, sizeof (SquashLicense) * (n + 2));
330  parser->plugin->license[n++] = squash_license_from_string (license);
331  parser->plugin->license[n] = SQUASH_LICENSE_UNKNOWN;
332 
333  n++;
334  }
335 
336  license = strtok_r (NULL, ";", &saveptr);
337  };
338 
339  free (licenses);
340  } else if (strcasecmp (key, "priority") == 0) {
341  char* endptr = NULL;
342  long priority = strtol (value, &endptr, 0);
343  if (*endptr == '\0') {
344  squash_codec_set_priority (parser->codec, (unsigned int) priority);
345  }
346  } else if (strcasecmp (key, "extension") == 0) {
347  squash_codec_set_extension (parser->codec, value);
348  }
349  }
350 
351  return true;
352 }
353 
354 static void
355 squash_codecs_file_parser_init (SquashCodecsFileParser* parser, SquashPlugin* plugin) {
356  SquashCodecsFileParser _parser = { plugin, NULL };
357 
358  *parser = _parser;
359 }
360 
361 static SquashStatus
362 squash_codecs_file_parser_parse (SquashCodecsFileParser* parser, FILE* input) {
363  bool res = squash_ini_parse (input, squash_codecs_file_parser_callback, parser);
364 
365  if (res && parser->codec != NULL) {
366  squash_plugin_add_codec (parser->plugin, parser->codec);
367  return SQUASH_OK;
368  } else {
369  return squash_error (SQUASH_FAILED);
370  }
371 }
372 
373 #if defined(_WIN32)
374 static char*
375 squash_strndup(const char* s, size_t n) {
376  const char* eos = (const char*) memchr (s, '\0', n);
377  const size_t res_len = (eos == NULL) ? n : (size_t) (eos - s);
378  char* res = (char*) malloc (res_len + 1);
379  memcpy (res, s, res_len);
380  res[res_len] = '\0';
381 
382  return res;
383 }
384 #endif
385 
386 static void
387 squash_context_check_directory_for_plugin (SquashContext* context, const char* directory_name, const char* plugin_name) {
388  size_t directory_name_length = strlen (directory_name);
389  size_t plugin_name_length = strlen (plugin_name);
390 
391  size_t codecs_file_name_length = directory_name_length + (plugin_name_length * 2) + 10;
392  char* codecs_file_name = (char*) malloc (codecs_file_name_length + 1);
393  snprintf (codecs_file_name, codecs_file_name_length, "%s/%s/squash.ini",
394  directory_name, plugin_name);
395 
396  FILE* codecs_file = fopen (codecs_file_name, "r");
397 
398  if (codecs_file != NULL) {
399  size_t plugin_directory_name_length = directory_name_length + plugin_name_length + 1;
400  char* plugin_directory_name = (char*) malloc (plugin_directory_name_length + 1);
401  snprintf (plugin_directory_name, plugin_directory_name_length + 1, "%s/%s",
402  directory_name, plugin_name);
403 
404  SquashPlugin* plugin = squash_context_add_plugin (context, squash_strndup (plugin_name, 32), plugin_directory_name);
405  if (plugin != NULL) {
406  SquashCodecsFileParser parser;
407 
408  squash_codecs_file_parser_init (&parser, plugin);
409  squash_codecs_file_parser_parse (&parser, codecs_file);
410  }
411 
412  fclose (codecs_file);
413  }
414 
415  free (codecs_file_name);
416 }
417 
418 static void
419 squash_context_find_plugins_in_directory (SquashContext* context, const char* directory_name) {
420 #if !defined(_WIN32)
421  DIR* directory = opendir (directory_name);
422  struct dirent* result = NULL;
423  struct dirent* entry = NULL;
424 
425  if (directory == NULL) {
426  return;
427  } else {
428  long name_max = pathconf (directory_name, _PC_NAME_MAX);
429  if (name_max == -1)
430  name_max = 255;
431  entry = (struct dirent*) malloc (offsetof (struct dirent, d_name) + name_max + 1);
432  }
433 
434  while ( readdir_r (directory, entry, &result) == 0 && result != NULL ) {
435 #ifdef _DIRENT_HAVE_D_TYPE
436  if ( entry->d_type != DT_DIR &&
437  entry->d_type != DT_UNKNOWN &&
438  entry->d_type != DT_LNK )
439  continue;
440 #endif
441 
442  if (strcmp (entry->d_name, "..") == 0 ||
443  strcmp (entry->d_name, ".") == 0)
444  continue;
445 
446  squash_context_check_directory_for_plugin (context, directory_name, entry->d_name);
447  }
448 
449  free (entry);
450  closedir (directory);
451 #else
452  WIN32_FIND_DATA entry;
453  TCHAR* directory_query = NULL;
454  size_t directory_query_length;
455  HANDLE directory_handle = INVALID_HANDLE_VALUE;
456 
457  StringCchLength (directory_name, MAX_PATH, &directory_query_length);
458  directory_query_length += 3;
459 
460  if (directory_query_length > MAX_PATH) {
461  return;
462  }
463 
464  directory_query = (TCHAR*) malloc (directory_query_length * sizeof(TCHAR));
465 
466  StringCchCopy (directory_query, directory_query_length, directory_name);
467  StringCchCat (directory_query, directory_query_length, TEXT("\\*"));
468 
469  directory_handle = FindFirstFile (directory_query, &entry);
470 
471  if (INVALID_HANDLE_VALUE == directory_handle) {
472  return;
473  }
474 
475  do {
476  if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
477  if (strcmp (entry.cFileName, "..") == 0 ||
478  strcmp (entry.cFileName, ".") == 0)
479  continue;
480 
481  squash_context_check_directory_for_plugin (context, directory_name, entry.cFileName);
482  }
483  }
484  while (FindNextFile (directory_handle, &entry) != 0);
485 
486  FindClose(directory_handle);
487  free (directory_query);
488 #endif /* defined(_WIN32) */
489 }
490 
491 static void
492 squash_context_find_plugins (SquashContext* context) {
493  char* directories;
494 
495  assert (context != NULL);
496 
497  directories = getenv ("SQUASH_PLUGINS");
498 
499  if (directories != NULL) {
500  char* saveptr = NULL;
501  char* directory_name = NULL;
502 
503  directories = strdup (directories);
504 
505  for ( directory_name = SQUASH_STRTOK_R (directories, ":", &saveptr) ;
506  directory_name != NULL ;
507  directory_name = SQUASH_STRTOK_R (NULL, ":", &saveptr) ) {
508  squash_context_find_plugins_in_directory (context, directory_name);
509  }
510 
511  free (directories);
512  } else {
513  squash_context_find_plugins_in_directory (context, SQUASH_PLUGIN_DIR);
514  }
515 }
516 
524 void
525 squash_context_foreach_plugin (SquashContext* context, SquashPluginForeachFunc func, void* data) {
526  SQUASH_TREE_FORWARD_APPLY(&(context->plugins), _SquashPlugin, tree, func, data);
527 }
528 
529 static void
530 squash_context_foreach_codec_ref (SquashContext* context, void(*func)(SquashCodecRef*, void*), void* data) {
531  SQUASH_TREE_FORWARD_APPLY(&(context->codecs), _SquashCodecRef, tree, func, data);
532 }
533 
537 struct SquashContextForeachCodecRefCbData {
539  void* data;
540 };
541 
542 static void
543 squash_context_foreach_codec_ref_cb (SquashCodecRef* codec_ref, void* data) {
544  struct SquashContextForeachCodecRefCbData* cb_data = (struct SquashContextForeachCodecRefCbData*) data;
545 
546  cb_data->func (codec_ref->codec, cb_data->data);
547 }
548 
563 void
564 squash_context_foreach_codec (SquashContext* context, SquashCodecForeachFunc func, void* data) {
565  struct SquashContextForeachCodecRefCbData cb_data = { func, data };
566 
567  squash_context_foreach_codec_ref (context, squash_context_foreach_codec_ref_cb, &cb_data);
568 }
569 
577 void
580 }
581 
596 void
599 }
600 
601 static SquashContext*
602 squash_context_new (void) {
603  SquashContext* context = (SquashContext*) malloc (sizeof (SquashContext));
604  SquashContext _context = { { 0, }, };
605 
606  assert (context != NULL);
607 
608  *context = _context;
609  SQUASH_TREE_INIT(&(context->codecs), squash_codec_ref_compare);
610  SQUASH_TREE_INIT(&(context->plugins), squash_plugin_compare);
611  SQUASH_TREE_INIT(&(context->extensions), squash_codec_ref_extension_compare);
612 
613  squash_context_find_plugins (context);
614 
615  return context;
616 }
617 
618 static SquashContext* squash_context_default = NULL;
619 
620 static void
621 squash_context_create_default (void) {
622  assert (squash_context_default == NULL);
623 
624  squash_context_default = (SquashContext*) squash_context_new ();
625 }
626 
637 SquashContext*
639  static once_flag once = ONCE_FLAG_INIT;
640 
641  call_once (&once, squash_context_create_default);
642 
643  assert (squash_context_default != NULL);
644 
645  return squash_context_default;
646 }
647 
SquashPlugin * squash_context_get_plugin(SquashContext *context, const char *plugin)
Retrieve a SquashPlugin from a SquashContext.
Definition: context.c:190
SquashCodec * squash_get_codec(const char *codec)
Retrieve a SquashCodec.
Definition: context.c:149
Operation failed.
Definition: status.h:41
void(* SquashPluginForeachFunc)(SquashPlugin *plugin, void *data)
Squashlback to be invoked on each SquashPlugin in a set.
Definition: plugin.h:42
SquashCodec * squash_get_codec_from_extension(const char *extension)
Retrieve a codec based on an extension.
Definition: context.c:177
SquashContext * squash_context_get_default(void)
Retrieve the default SquashContext.
Definition: context.c:638
SquashStatus squash_codec_init(SquashCodec *codec)
Initialize a codec.
Definition: codec.c:347
SquashPlugin * squash_get_plugin(const char *plugin)
Retrieve a SquashPlugin.
Definition: context.c:209
SquashStatus
Status codes.
Definition: status.h:36
void squash_context_foreach_codec(SquashContext *context, SquashCodecForeachFunc func, void *data)
Execute a callback for every loaded codec.
Definition: context.c:564
SquashCodec * squash_context_get_codec(SquashContext *context, const char *codec)
Retrieve a SquashCodec from a SquashContext.
Definition: context.c:114
SquashStatus squash_error(SquashStatus status)
Emit an error.
Definition: status.c:173
void(* SquashCodecForeachFunc)(SquashCodec *codec, void *data)
Squashlback to be invoked on each SquashCodec in a set.
Definition: codec.h:97
SquashCodec * squash_context_get_codec_from_extension(SquashContext *context, const char *extension)
Retrieve a codec from a context based on an extension.
Definition: context.c:161
void squash_foreach_codec(SquashCodecForeachFunc func, void *data)
Execute a callback for every loaded codec in the default context.
Definition: context.c:597
SquashCodec * squash_plugin_get_codec(SquashPlugin *plugin, const char *codec)
Get a codec from a plugin by name.
Definition: plugin.c:198
void squash_context_foreach_plugin(SquashContext *context, SquashPluginForeachFunc func, void *data)
Execute a callback for every loaded plugin.
Definition: context.c:525
A plugin.
Operation completed successfully.
Definition: status.h:37
void squash_foreach_plugin(SquashPluginForeachFunc func, void *data)
Execute a callback for every loaded plugin in the default context.
Definition: context.c:578