FIX: examples/supports.c: type_n: Return correct type
[libquvi.git] / src / misc / scan_scripts.c
blobb1d2d3f18d3f5c37402df164e7ead3dceed20dc1
1 /* libquvi
2 * Copyright (C) 2012 Toni Gundogdu <legatvs@gmail.com>
4 * This file is part of libquvi <http://quvi.sourceforge.net/>.
6 * This library is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU Affero General Public
8 * License as published by the Free Software Foundation, either
9 * version 3 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
16 * You should have received a copy of the GNU Affero General
17 * Public License along with this library. If not, see
18 * <http://www.gnu.org/licenses/>.
21 #include "config.h"
23 #include <string.h>
24 #include <glib.h>
26 #include "quvi.h"
27 /* -- */
28 #include "_quvi_s.h"
29 #include "_quvi_media_s.h"
30 #include "_quvi_subtitle_export_s.h"
31 #include "_quvi_subtitle_s.h"
32 #include "_quvi_playlist_s.h"
33 #include "_quvi_script_s.h"
34 /* -- */
35 #include "misc/script_free.h"
36 #include "misc/subtitle_export.h"
37 #include "misc/subtitle.h"
38 #include "misc/playlist.h"
39 #include "misc/media.h"
40 #include "misc/re.h"
41 #include "lua/exec.h"
43 /* Return path to script file. */
44 static GString *_get_fpath(const gchar *path, const gchar *fname)
46 GString *r;
47 gchar *s;
49 s = g_build_filename(path, fname, NULL);
50 r = g_string_new(s);
52 g_free(s);
53 s = NULL;
55 return (r);
58 /* Return SHA1 for script file. */
59 static GString *_file_sha1(const GString *c)
61 GString *r = g_string_new(NULL);
62 if (c != NULL)
64 gchar *s = g_compute_checksum_for_string(G_CHECKSUM_SHA1, c->str, -1);
65 g_string_assign(r, s);
66 g_free(s);
67 s = NULL;
69 return (r);
72 /* Return file contents in a GString. */
73 static GString *_contents(GString *fpath)
75 gchar *c = NULL;
76 g_file_get_contents(fpath->str, &c, NULL, NULL);
77 if (c != NULL)
79 GString *s = g_string_new(c);
80 g_free(c);
81 c = NULL;
82 return (s);
84 return (NULL);
87 const gchar *scripts_dir;
88 const gchar *show_script;
89 const gchar *show_dir;
91 typedef QuviError (*exec_script_ident_callback)(gpointer, GSList*);
92 typedef gpointer (*new_ident_callback)(_quvi_t, const gchar*);
93 typedef void (*free_ident_callback)(gpointer);
95 /* Parses the values returned by the ident function. */
96 static void _chk_script_ident(_quvi_t q, _quvi_script_t qs, gboolean *ok,
97 new_ident_callback cb_new,
98 exec_script_ident_callback cb_exec,
99 free_ident_callback cb_free)
101 static const gchar URL[] = "http://foo";
103 QuviError r;
104 gpointer p;
105 GSList *s;
107 s = g_slist_prepend(NULL, qs);
108 p = cb_new(q, URL);
109 r = cb_exec(p, s);
111 g_slist_free(s);
112 cb_free(p);
114 /* Script ident function should return "no support". If anything else
115 * is returned, there's something wrong with the script. */
116 if (r == QUVI_ERROR_NO_SUPPORT)
117 *ok = TRUE;
118 else
120 g_critical("[%s] %s", __func__, q->status.errmsg->str);
121 *ok = FALSE;
125 /* Check if a pattern matches in a string. */
126 static gboolean _chk(const gchar *s, const gchar *p)
128 const gboolean r = m_match(s, p);
129 if (show_script != NULL && strlen(show_script) >0)
131 if (r == FALSE)
133 g_message("[%s] libquvi: nothing matched the pattern `%s'",
134 __func__, p);
137 return (r);
140 /* New script */
141 static gpointer _script_new(const gchar *fpath, const gchar *fname, GString *c)
143 _quvi_script_t qs = g_new0(struct _quvi_script_s, 1);
144 qs->export.format = g_string_new(NULL);
145 qs->domains = g_string_new(NULL);
146 qs->fpath = g_string_new(fpath);
147 qs->fname = g_string_new(fname);
148 qs->sha1 = _file_sha1(c);
149 g_string_free(c, TRUE);
150 return (qs);
153 /* New media script. */
154 static gpointer _new_media_script(_quvi_t q, const gchar *path,
155 const gchar *fname)
157 _quvi_script_t qs;
158 GString *fpath;
159 GString *c;
161 fpath = _get_fpath(path, fname);
162 c = _contents(fpath);
163 qs = NULL;
165 if (c != NULL)
167 gboolean OK =
168 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
169 && _chk(c->str, "^function ident") == TRUE
170 && _chk(c->str, "^function parse") == TRUE);
172 if (OK == TRUE)
174 qs = _script_new(fpath->str, fname, c);
176 _chk_script_ident(q, qs, &OK, m_media_new,
177 l_exec_media_script_ident,
178 (free_ident_callback) m_media_free);
181 if (OK == FALSE)
183 m_script_free(qs, NULL);
184 qs = NULL;
187 g_string_free(fpath, TRUE);
188 return (qs);
191 /* New subtitle export script. */
192 static gpointer
193 _new_subtitle_export_script(_quvi_t q, const gchar *path, const gchar *fname)
195 _quvi_script_t qs;
196 GString *fpath;
197 GString *c;
199 fpath = _get_fpath(path, fname);
200 c = _contents(fpath);
201 qs = NULL;
203 if (c != NULL)
205 gboolean OK =
206 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
207 && _chk(c->str, "^function ident") == TRUE
208 && _chk(c->str, "^function export") == TRUE);
210 if (OK == TRUE)
212 qs = _script_new(fpath->str, fname, c);
214 _chk_script_ident(q, qs, &OK, m_subtitle_export_new,
215 l_exec_subtitle_export_script_ident,
216 (free_ident_callback) m_subtitle_export_free);
219 if (OK == FALSE)
221 m_script_free(qs, NULL);
222 qs = NULL;
225 g_string_free(fpath, TRUE);
226 return (qs);
229 /* New subtitle script. */
230 static gpointer _new_subtitle_script(_quvi_t q, const gchar *path,
231 const gchar *fname)
233 _quvi_script_t qs;
234 GString *fpath;
235 GString *c;
237 fpath = _get_fpath(path, fname);
238 c = _contents(fpath);
239 qs = NULL;
241 if (c != NULL)
243 gboolean OK =
244 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
245 && _chk(c->str, "^function ident") == TRUE
246 && _chk(c->str, "^function parse") == TRUE);
248 if (OK == TRUE)
250 qs = _script_new(fpath->str, fname, c);
252 _chk_script_ident(q, qs, &OK, m_subtitle_new,
253 l_exec_subtitle_script_ident,
254 (free_ident_callback) m_subtitle_free);
257 if (OK == FALSE)
259 m_script_free(qs, NULL);
260 qs = NULL;
263 g_string_free(fpath, TRUE);
264 return (qs);
267 /* New playlist script. */
268 static gpointer _new_playlist_script(_quvi_t q, const gchar *path,
269 const gchar *fname)
271 _quvi_script_t qs;
272 GString *fpath;
273 GString *c;
275 fpath = _get_fpath(path, fname);
276 c = _contents(fpath);
277 qs = NULL;
279 if (c != NULL)
281 gboolean OK =
282 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
283 && _chk(c->str, "^function ident") == TRUE
284 && _chk(c->str, "^function parse") == TRUE);
286 if (OK == TRUE)
288 qs = _script_new(fpath->str, fname, c);
290 _chk_script_ident(q, qs, &OK, m_playlist_new,
291 l_exec_playlist_script_ident,
292 (free_ident_callback) m_playlist_free);
295 if (OK == FALSE)
297 m_script_free(qs, NULL);
298 qs = NULL;
301 g_string_free(fpath, TRUE);
302 return (qs);
305 /* New scan script. */
306 static gpointer _new_scan_script(_quvi_t q, const gchar *path,
307 const gchar *fname)
309 _quvi_script_t qs;
310 GString *fpath;
311 GString *c;
313 fpath = _get_fpath(path, fname);
314 c = _contents(fpath);
315 qs = NULL;
317 if (c != NULL)
319 gboolean OK =
320 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
321 && _chk(c->str, "^function parse") == TRUE);
323 if (OK == TRUE)
324 qs = _script_new(fpath->str, fname, c);
326 if (OK == FALSE)
328 m_script_free(qs, NULL);
329 qs = NULL;
332 g_string_free(fpath, TRUE);
333 return (qs);
336 /* New utility script. */
337 static gpointer _new_util_script(_quvi_t q, const gchar *path,
338 const gchar *fname)
340 _quvi_script_t qs;
341 GString *fpath;
342 GString *c;
344 fpath = _get_fpath(path, fname);
345 c = _contents(fpath);
346 qs = NULL;
348 if (c != NULL)
350 const gboolean OK =
351 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE);
353 if (OK == TRUE)
354 qs = _script_new(fpath->str, fname, c);
356 if (OK == FALSE)
358 m_script_free(qs, NULL);
359 qs = NULL;
362 g_string_free(fpath, TRUE);
363 return (qs);
366 /* Check for duplicate script. */
367 static gboolean _chkdup_script(_quvi_t q, gpointer script, GSList *l)
369 _quvi_script_t a, b;
370 GSList *curr;
372 a = (_quvi_script_t) script;
373 curr = l;
375 while (curr != NULL)
377 b = (_quvi_script_t) curr->data;
379 if (g_string_equal(a->sha1, b->sha1) == TRUE)
380 return (TRUE);
382 curr = g_slist_next(curr);
384 return (FALSE);
387 /* Include '*.lua' files only. */
388 static gint _lua_files_only(const gchar *fpath)
390 const gchar *ext = strrchr(fpath, '.');
391 return (fpath[0] != '.' && ext != NULL && strcmp(ext, ".lua") == 0);
394 typedef gpointer (*new_script_callback)(_quvi_t, const gchar*, const gchar*);
395 typedef gboolean (*chkdup_script_callback)(_quvi_t, gpointer, GSList*);
396 typedef void (*free_script_callback)(gpointer, gpointer);
398 static gboolean _glob_scripts_dir(_quvi_t q, const gchar *path, GSList **dst,
399 new_script_callback cb_new,
400 free_script_callback cb_free,
401 chkdup_script_callback cb_chkdup)
403 const gchar *fname;
404 GDir *dir;
406 if (show_dir != NULL && strlen(show_dir) >0)
407 g_message("[%s] libquvi: %s", __func__, path);
409 dir = g_dir_open(path, 0, NULL);
410 if (dir == NULL)
411 return (FALSE);
413 while ( (fname = g_dir_read_name(dir)) != NULL)
415 if (_lua_files_only(fname) != 0)
417 gpointer s = cb_new(q, path, fname);
418 if (s == NULL)
420 /* Either file read failed or this is not a valid
421 * libquvi-script. */
422 if (show_script != NULL && strlen(show_script) >0)
424 g_message("[%s] libquvi: rejected: %s [INVALID]",
425 __func__, fname);
428 else
430 /* Valid libquvi-script file. */
431 const gboolean r = cb_chkdup(q, s, *dst);
433 if (r == FALSE)
434 *dst = g_slist_prepend(*dst, s);
435 else
437 cb_free(s, NULL);
438 s = NULL;
441 if (show_script != NULL && strlen(show_script) >0)
443 g_message("[%s] libquvi: %s: %s [%s]",
444 __func__,
445 (r == FALSE) ? "accepted" : "rejected",
446 fname,
447 (r == FALSE) ? "OK" : "DUPLICATE");
452 g_dir_close(dir);
453 dir = NULL;
455 if (*dst != NULL)
456 *dst = g_slist_reverse(*dst);
458 return (*dst != NULL);
461 typedef enum
463 GLOB_SUBTITLE_EXPORT_SCRIPTS,
464 GLOB_SUBTITLE_SCRIPTS,
465 GLOB_PLAYLIST_SCRIPTS,
466 GLOB_MEDIA_SCRIPTS,
467 GLOB_SCAN_SCRIPTS,
468 GLOB_UTIL_SCRIPTS,
469 _GLOB_COUNT
470 } GlobType;
472 static const gchar *glob_dir[_GLOB_COUNT] =
474 "subtitle/export/",
475 "subtitle/",
476 "playlist/",
477 "media/",
478 "scan/",
479 "util/"
482 static gboolean _glob_scripts(_quvi_t q, const GlobType t)
484 chkdup_script_callback cb_chkdup;
485 free_script_callback cb_free;
486 new_script_callback cb_new;
487 GSList **dst;
488 gchar *path;
490 switch (t)
492 case GLOB_SUBTITLE_EXPORT_SCRIPTS:
493 cb_new = _new_subtitle_export_script;
494 dst = &q->scripts.subtitle_export;
495 break;
496 case GLOB_SUBTITLE_SCRIPTS:
497 cb_new = _new_subtitle_script;
498 dst = &q->scripts.subtitle;
499 break;
500 case GLOB_PLAYLIST_SCRIPTS:
501 cb_new = _new_playlist_script;
502 dst = &q->scripts.playlist;
503 break;
504 case GLOB_MEDIA_SCRIPTS:
505 cb_new = _new_media_script;
506 dst = &q->scripts.media;
507 break;
508 case GLOB_SCAN_SCRIPTS:
509 cb_new = _new_scan_script;
510 dst = &q->scripts.scan;
511 break;
512 case GLOB_UTIL_SCRIPTS:
513 cb_new = _new_util_script;
514 dst = &q->scripts.util;
515 break;
516 default:
517 g_error("%s: %d: invalid mode", __func__, __LINE__);
520 cb_chkdup = _chkdup_script;
521 cb_free = m_script_free;
524 /* LIBQUVI_SCRIPTS_DIR */
526 if (scripts_dir != NULL && strlen(scripts_dir) >0)
528 gchar **r;
529 gint i;
531 r = g_strsplit(scripts_dir, G_SEARCHPATH_SEPARATOR_S, 0);
532 for (i=0; r[i] != NULL; ++i)
534 path = g_build_path(G_DIR_SEPARATOR_S, r[i], glob_dir[t], NULL);
535 _glob_scripts_dir(q, path, dst, cb_new, cb_free, cb_chkdup);
537 g_free(path);
538 path = NULL;
540 g_strfreev(r);
545 /* Current working directory. */
547 gchar *cwd = g_get_current_dir();
548 path = g_build_path(G_DIR_SEPARATOR_S, cwd, glob_dir[t], NULL);
549 g_free(cwd);
551 _glob_scripts_dir(q, path, dst, cb_new, cb_free, cb_chkdup);
552 g_free(path);
555 #ifdef SCRIPTSDIR
557 /* SCRIPTSDIR from config.h */
559 path = g_build_path(G_DIR_SEPARATOR_S,
560 SCRIPTSDIR, VERSION_MM, glob_dir[t], NULL);
562 _glob_scripts_dir(q, path, dst, cb_new, cb_free, cb_chkdup);
563 g_free(path);
565 /* SCRIPTSDIR: Without the VERSION_MM. */
567 path = g_build_path(G_DIR_SEPARATOR_S, SCRIPTSDIR, glob_dir[t], NULL);
568 _glob_scripts_dir(q, path, dst, cb_new, cb_free, cb_chkdup);
569 g_free(path);
571 #endif /* SCRIPTSDIR */
573 return ((*dst != NULL) ? TRUE:FALSE);
576 static gboolean _dir_exists(const gchar *path)
578 GDir *dir = g_dir_open(path, 0, NULL);
580 if (dir == NULL)
581 return (FALSE);
583 g_dir_close(dir);
584 dir = NULL;
586 return (TRUE);
589 extern void l_modify_pkgpath(_quvi_t, const gchar*);
591 #define Q_COMMON_DIR "common"
594 * Check for the "common" directory, if found, append the path to the
595 * Lua's package.path setting. We're not interested in the contents of
596 * this directory at this stage.
598 static void _chk_common_scripts(_quvi_t q)
600 gchar *path = NULL;
603 /* LIBQUVI_SCRIPTS_DIR (excl.) */
605 if (scripts_dir != NULL && strlen(scripts_dir) >0)
607 gchar **r;
608 gint i;
610 r = g_strsplit(scripts_dir, G_SEARCHPATH_SEPARATOR_S, 0);
611 for (i=0; r[i] != NULL; ++i)
613 path = g_build_path(G_DIR_SEPARATOR_S,
614 scripts_dir, Q_COMMON_DIR, NULL);
616 if (_dir_exists(path) == TRUE)
617 l_modify_pkgpath(q, path);
619 g_free(path);
620 path = NULL;
622 g_strfreev(r);
623 r = NULL;
628 /* Current working directory. */
630 gchar *cwd = g_get_current_dir();
631 path = g_build_path(G_DIR_SEPARATOR_S, cwd, Q_COMMON_DIR, NULL);
633 if (_dir_exists(path) == TRUE)
634 l_modify_pkgpath(q, path);
636 g_free(path);
637 path = NULL;
639 g_free(cwd);
640 cwd = NULL;
643 #ifdef SCRIPTSDIR
645 /* SCRIPTSDIR from config.h */
647 path = g_build_path(G_DIR_SEPARATOR_S,
648 SCRIPTSDIR, VERSION_MM, Q_COMMON_DIR, NULL);
650 if (_dir_exists(path) == TRUE)
651 l_modify_pkgpath(q, path);
653 g_free(path);
655 /* SCRIPTSDIR: Without the VERSION_MM. */
657 path = g_build_path(G_DIR_SEPARATOR_S, SCRIPTSDIR, Q_COMMON_DIR, NULL);
659 if (_dir_exists(path) == TRUE)
660 l_modify_pkgpath(q, path);
662 g_free(path);
663 path = NULL;
665 #endif /* SCRIPTSDIR */
668 #undef Q_COMMON_DIR
670 QuviError m_scan_scripts(_quvi_t q)
672 QuviError r, e;
673 GlobType t;
675 scripts_dir = g_getenv("LIBQUVI_SCRIPTS_DIR");
676 show_script = g_getenv("LIBQUVI_SHOW_SCRIPT");
677 show_dir = g_getenv("LIBQUVI_SHOW_DIR");
679 _chk_common_scripts(q);
681 e = QUVI_ERROR_CALLBACK_ABORTED+1;
682 r = QUVI_OK;
683 t = -1;
685 while (++t <_GLOB_COUNT && r ==QUVI_OK)
687 r = (_glob_scripts(q,t) == TRUE) ? QUVI_OK : e;
688 ++e;
690 return (r);
693 /* vim: set ts=2 sw=2 tw=72 expandtab: */