misc/scan_scripts.c: Make show_dir a static variable
[libquvi.git] / src / misc / scan_scripts.c
blob8699a3f7e7349346c07df0adeddd163dd1ffcaf5
1 /* libquvi
2 * Copyright (C) 2012,2013 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 static gboolean excl_scripts_dir;
88 static const gchar *scripts_dir;
89 static const gchar *show_dir;
90 const gchar *show_script;
92 typedef QuviError (*exec_script_ident_callback)(gpointer, GSList*);
93 typedef gpointer (*new_ident_callback)(_quvi_t, const gchar*);
94 typedef void (*free_ident_callback)(gpointer);
96 /* Parses the values returned by the ident function. */
97 static void _chk_script_ident(_quvi_t q, _quvi_script_t qs, gboolean *ok,
98 new_ident_callback cb_new,
99 exec_script_ident_callback cb_exec,
100 free_ident_callback cb_free)
102 static const gchar URL[] = "http://foo";
104 QuviError r;
105 gpointer p;
106 GSList *s;
108 s = g_slist_prepend(NULL, qs);
109 p = cb_new(q, URL);
110 r = cb_exec(p, s);
112 g_slist_free(s);
113 cb_free(p);
115 /* Script ident function should return "no support". If anything else
116 * is returned, there's something wrong with the script. */
117 if (r == QUVI_ERROR_NO_SUPPORT)
118 *ok = TRUE;
119 else
121 g_critical("[%s] %s", __func__, q->status.errmsg->str);
122 *ok = FALSE;
126 /* Check if a pattern matches in a string. */
127 static gboolean _chk(const gchar *s, const gchar *p)
129 const gboolean r = m_match(s, p);
130 if (show_script != NULL && strlen(show_script) >0)
132 if (r == FALSE)
134 g_message("[%s] libquvi: nothing matched the pattern `%s'",
135 __func__, p);
138 return (r);
141 /* New script */
142 static gpointer _script_new(const gchar *fpath, const gchar *fname, GString *c)
144 _quvi_script_t qs = g_new0(struct _quvi_script_s, 1);
145 qs->export.format = g_string_new(NULL);
146 qs->domains = g_string_new(NULL);
147 qs->fpath = g_string_new(fpath);
148 qs->fname = g_string_new(fname);
149 qs->sha1 = _file_sha1(c);
150 g_string_free(c, TRUE);
151 return (qs);
154 /* New media script. */
155 static gpointer _new_media_script(_quvi_t q, const gchar *path,
156 const gchar *fname)
158 _quvi_script_t qs;
159 GString *fpath;
160 GString *c;
162 fpath = _get_fpath(path, fname);
163 c = _contents(fpath);
164 qs = NULL;
166 if (c != NULL)
168 gboolean OK =
169 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
170 && _chk(c->str, "^function ident") == TRUE
171 && _chk(c->str, "^function parse") == TRUE);
173 if (OK == TRUE)
175 qs = _script_new(fpath->str, fname, c);
177 _chk_script_ident(q, qs, &OK, m_media_new,
178 l_exec_media_script_ident,
179 (free_ident_callback) m_media_free);
182 if (OK == FALSE)
184 m_script_free(qs, NULL);
185 qs = NULL;
188 g_string_free(fpath, TRUE);
189 return (qs);
192 /* New subtitle export script. */
193 static gpointer
194 _new_subtitle_export_script(_quvi_t q, const gchar *path, const gchar *fname)
196 _quvi_script_t qs;
197 GString *fpath;
198 GString *c;
200 fpath = _get_fpath(path, fname);
201 c = _contents(fpath);
202 qs = NULL;
204 if (c != NULL)
206 gboolean OK =
207 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
208 && _chk(c->str, "^function ident") == TRUE
209 && _chk(c->str, "^function export") == TRUE);
211 if (OK == TRUE)
213 qs = _script_new(fpath->str, fname, c);
215 _chk_script_ident(q, qs, &OK, m_subtitle_export_new,
216 l_exec_subtitle_export_script_ident,
217 (free_ident_callback) m_subtitle_export_free);
220 if (OK == FALSE)
222 m_script_free(qs, NULL);
223 qs = NULL;
226 g_string_free(fpath, TRUE);
227 return (qs);
230 /* New subtitle script. */
231 static gpointer _new_subtitle_script(_quvi_t q, const gchar *path,
232 const gchar *fname)
234 _quvi_script_t qs;
235 GString *fpath;
236 GString *c;
238 fpath = _get_fpath(path, fname);
239 c = _contents(fpath);
240 qs = NULL;
242 if (c != NULL)
244 gboolean OK =
245 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
246 && _chk(c->str, "^function ident") == TRUE
247 && _chk(c->str, "^function parse") == TRUE);
249 if (OK == TRUE)
251 qs = _script_new(fpath->str, fname, c);
253 _chk_script_ident(q, qs, &OK, m_subtitle_new,
254 l_exec_subtitle_script_ident,
255 (free_ident_callback) m_subtitle_free);
258 if (OK == FALSE)
260 m_script_free(qs, NULL);
261 qs = NULL;
264 g_string_free(fpath, TRUE);
265 return (qs);
268 /* New playlist script. */
269 static gpointer _new_playlist_script(_quvi_t q, const gchar *path,
270 const gchar *fname)
272 _quvi_script_t qs;
273 GString *fpath;
274 GString *c;
276 fpath = _get_fpath(path, fname);
277 c = _contents(fpath);
278 qs = NULL;
280 if (c != NULL)
282 gboolean OK =
283 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
284 && _chk(c->str, "^function ident") == TRUE
285 && _chk(c->str, "^function parse") == TRUE);
287 if (OK == TRUE)
289 qs = _script_new(fpath->str, fname, c);
291 _chk_script_ident(q, qs, &OK, m_playlist_new,
292 l_exec_playlist_script_ident,
293 (free_ident_callback) m_playlist_free);
296 if (OK == FALSE)
298 m_script_free(qs, NULL);
299 qs = NULL;
302 g_string_free(fpath, TRUE);
303 return (qs);
306 /* New scan script. */
307 static gpointer _new_scan_script(_quvi_t q, const gchar *path,
308 const gchar *fname)
310 _quvi_script_t qs;
311 GString *fpath;
312 GString *c;
314 fpath = _get_fpath(path, fname);
315 c = _contents(fpath);
316 qs = NULL;
318 if (c != NULL)
320 gboolean OK =
321 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
322 && _chk(c->str, "^function parse") == TRUE);
324 if (OK == TRUE)
325 qs = _script_new(fpath->str, fname, c);
327 if (OK == FALSE)
329 m_script_free(qs, NULL);
330 qs = NULL;
333 g_string_free(fpath, TRUE);
334 return (qs);
337 /* New utility script. */
338 static gpointer _new_util_script(_quvi_t q, const gchar *path,
339 const gchar *fname)
341 _quvi_script_t qs;
342 GString *fpath;
343 GString *c;
345 fpath = _get_fpath(path, fname);
346 c = _contents(fpath);
347 qs = NULL;
349 if (c != NULL)
351 const gboolean OK =
352 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE);
354 if (OK == TRUE)
355 qs = _script_new(fpath->str, fname, c);
357 if (OK == FALSE)
359 m_script_free(qs, NULL);
360 qs = NULL;
363 g_string_free(fpath, TRUE);
364 return (qs);
367 /* Check for duplicate script. */
368 static gboolean _chkdup_script(_quvi_t q, gpointer script, GSList *l)
370 _quvi_script_t a, b;
371 GSList *curr;
373 a = (_quvi_script_t) script;
374 curr = l;
376 while (curr != NULL)
378 b = (_quvi_script_t) curr->data;
380 if (g_string_equal(a->sha1, b->sha1) == TRUE)
381 return (TRUE);
383 curr = g_slist_next(curr);
385 return (FALSE);
388 /* Include '*.lua' files only. */
389 static gint _lua_files_only(const gchar *fpath)
391 const gchar *ext = strrchr(fpath, '.');
392 return (fpath[0] != '.' && ext != NULL && strcmp(ext, ".lua") == 0);
395 typedef gpointer (*new_script_callback)(_quvi_t, const gchar*, const gchar*);
396 typedef gboolean (*chkdup_script_callback)(_quvi_t, gpointer, GSList*);
397 typedef void (*free_script_callback)(gpointer, gpointer);
399 static gboolean _glob_scripts_dir(_quvi_t q, const gchar *path, GSList **dst,
400 new_script_callback cb_new,
401 free_script_callback cb_free,
402 chkdup_script_callback cb_chkdup)
404 const gchar *fname;
405 GDir *dir;
407 if (show_dir != NULL && strlen(show_dir) >0)
408 g_message("[%s] libquvi: %s", __func__, path);
410 dir = g_dir_open(path, 0, NULL);
411 if (dir == NULL)
412 return (FALSE);
414 while ( (fname = g_dir_read_name(dir)) != NULL)
416 if (_lua_files_only(fname) != 0)
418 gpointer s = cb_new(q, path, fname);
419 if (s == NULL)
421 /* Either file read failed or this is not a valid
422 * libquvi-script. */
423 if (show_script != NULL && strlen(show_script) >0)
425 g_message("[%s] libquvi: rejected: %s [INVALID]",
426 __func__, fname);
429 else
431 /* Valid libquvi-script file. */
432 const gboolean r = cb_chkdup(q, s, *dst);
434 if (r == FALSE)
435 *dst = g_slist_prepend(*dst, s);
436 else
438 cb_free(s, NULL);
439 s = NULL;
442 if (show_script != NULL && strlen(show_script) >0)
444 g_message("[%s] libquvi: %s: %s [%s]",
445 __func__,
446 (r == FALSE) ? "accepted" : "rejected",
447 fname,
448 (r == FALSE) ? "OK" : "DUPLICATE");
453 g_dir_close(dir);
454 dir = NULL;
456 if (*dst != NULL)
457 *dst = g_slist_reverse(*dst);
459 return (*dst != NULL);
462 typedef enum
464 GLOB_SUBTITLE_EXPORT_SCRIPTS,
465 GLOB_SUBTITLE_SCRIPTS,
466 GLOB_PLAYLIST_SCRIPTS,
467 GLOB_MEDIA_SCRIPTS,
468 GLOB_SCAN_SCRIPTS,
469 GLOB_UTIL_SCRIPTS,
470 _GLOB_COUNT
471 } GlobType;
473 static const gchar *glob_dir[_GLOB_COUNT] =
475 "subtitle/export/",
476 "subtitle/",
477 "playlist/",
478 "media/",
479 "scan/",
480 "util/"
483 static gboolean _glob_scripts(_quvi_t q, const GlobType t)
485 chkdup_script_callback cb_chkdup;
486 free_script_callback cb_free;
487 new_script_callback cb_new;
488 GSList **dst;
489 gchar *path;
491 switch (t)
493 case GLOB_SUBTITLE_EXPORT_SCRIPTS:
494 cb_new = _new_subtitle_export_script;
495 dst = &q->scripts.subtitle_export;
496 break;
497 case GLOB_SUBTITLE_SCRIPTS:
498 cb_new = _new_subtitle_script;
499 dst = &q->scripts.subtitle;
500 break;
501 case GLOB_PLAYLIST_SCRIPTS:
502 cb_new = _new_playlist_script;
503 dst = &q->scripts.playlist;
504 break;
505 case GLOB_MEDIA_SCRIPTS:
506 cb_new = _new_media_script;
507 dst = &q->scripts.media;
508 break;
509 case GLOB_SCAN_SCRIPTS:
510 cb_new = _new_scan_script;
511 dst = &q->scripts.scan;
512 break;
513 case GLOB_UTIL_SCRIPTS:
514 cb_new = _new_util_script;
515 dst = &q->scripts.util;
516 break;
517 default:
518 g_error("%s: %d: invalid mode", __func__, __LINE__);
521 cb_chkdup = _chkdup_script;
522 cb_free = m_script_free;
525 /* LIBQUVI_SCRIPTS_DIR */
527 if (scripts_dir != NULL && strlen(scripts_dir) >0)
529 gchar **r;
530 gint i;
532 r = g_strsplit(scripts_dir, G_SEARCHPATH_SEPARATOR_S, 0);
533 for (i=0; r[i] != NULL; ++i)
535 path = g_build_path(G_DIR_SEPARATOR_S, r[i], glob_dir[t], NULL);
536 _glob_scripts_dir(q, path, dst, cb_new, cb_free, cb_chkdup);
538 g_free(path);
539 path = NULL;
541 g_strfreev(r);
543 if (excl_scripts_dir == TRUE)
544 return ((*dst != NULL) ? TRUE:FALSE);
549 /* Current working directory. */
551 gchar *cwd = g_get_current_dir();
552 path = g_build_path(G_DIR_SEPARATOR_S, cwd, glob_dir[t], NULL);
553 g_free(cwd);
555 _glob_scripts_dir(q, path, dst, cb_new, cb_free, cb_chkdup);
556 g_free(path);
559 #ifdef SCRIPTSDIR
561 /* SCRIPTSDIR from config.h */
563 path = g_build_path(G_DIR_SEPARATOR_S,
564 SCRIPTSDIR, VERSION_MM, glob_dir[t], NULL);
566 _glob_scripts_dir(q, path, dst, cb_new, cb_free, cb_chkdup);
567 g_free(path);
569 /* SCRIPTSDIR: Without the VERSION_MM. */
571 path = g_build_path(G_DIR_SEPARATOR_S, SCRIPTSDIR, glob_dir[t], NULL);
572 _glob_scripts_dir(q, path, dst, cb_new, cb_free, cb_chkdup);
573 g_free(path);
575 #endif /* SCRIPTSDIR */
577 return ((*dst != NULL) ? TRUE:FALSE);
580 static gboolean _dir_exists(const gchar *path)
582 GDir *dir = g_dir_open(path, 0, NULL);
584 if (dir == NULL)
585 return (FALSE);
587 g_dir_close(dir);
588 dir = NULL;
590 return (TRUE);
593 extern void l_modify_pkgpath(_quvi_t, const gchar*);
595 #define Q_COMMON_DIR "common"
598 * Check for the "common" directory, if found, append the path to the
599 * Lua's package.path setting. We're not interested in the contents of
600 * this directory at this stage.
602 static void _chk_common_scripts(_quvi_t q)
604 gchar *path = NULL;
607 /* LIBQUVI_SCRIPTS_DIR */
609 if (scripts_dir != NULL && strlen(scripts_dir) >0)
611 gchar **r;
612 gint i;
614 r = g_strsplit(scripts_dir, G_SEARCHPATH_SEPARATOR_S, 0);
615 for (i=0; r[i] != NULL; ++i)
617 path = g_build_path(G_DIR_SEPARATOR_S,
618 scripts_dir, Q_COMMON_DIR, NULL);
620 if (_dir_exists(path) == TRUE)
621 l_modify_pkgpath(q, path);
623 g_free(path);
624 path = NULL;
626 g_strfreev(r);
627 r = NULL;
629 if (excl_scripts_dir == TRUE)
630 return;
635 /* Current working directory. */
637 gchar *cwd = g_get_current_dir();
638 path = g_build_path(G_DIR_SEPARATOR_S, cwd, Q_COMMON_DIR, NULL);
640 if (_dir_exists(path) == TRUE)
641 l_modify_pkgpath(q, path);
643 g_free(path);
644 path = NULL;
646 g_free(cwd);
647 cwd = NULL;
650 #ifdef SCRIPTSDIR
652 /* SCRIPTSDIR from config.h */
654 path = g_build_path(G_DIR_SEPARATOR_S,
655 SCRIPTSDIR, VERSION_MM, Q_COMMON_DIR, NULL);
657 if (_dir_exists(path) == TRUE)
658 l_modify_pkgpath(q, path);
660 g_free(path);
662 /* SCRIPTSDIR: Without the VERSION_MM. */
664 path = g_build_path(G_DIR_SEPARATOR_S, SCRIPTSDIR, Q_COMMON_DIR, NULL);
666 if (_dir_exists(path) == TRUE)
667 l_modify_pkgpath(q, path);
669 g_free(path);
670 path = NULL;
672 #endif /* SCRIPTSDIR */
675 #undef Q_COMMON_DIR
677 QuviError m_scan_scripts(_quvi_t q)
679 QuviError r, e;
680 GlobType t;
683 const gchar *s = g_getenv("LIBQUVI_EXCLUSIVE_SCRIPTS_DIR");
685 excl_scripts_dir = (s != NULL && strlen(s) >0)
686 ? TRUE
687 : FALSE;
690 scripts_dir = g_getenv("LIBQUVI_SCRIPTS_DIR");
691 show_script = g_getenv("LIBQUVI_SHOW_SCRIPT");
692 show_dir = g_getenv("LIBQUVI_SHOW_DIR");
694 _chk_common_scripts(q);
696 e = QUVI_ERROR_CALLBACK_ABORTED+1;
697 r = QUVI_OK;
698 t = -1;
700 while (++t <_GLOB_COUNT && r ==QUVI_OK)
702 r = (_glob_scripts(q,t) == TRUE) ? QUVI_OK : e;
703 ++e;
705 return (r);
708 /* vim: set ts=2 sw=2 tw=72 expandtab: */