_glob_scripts: LIBQUVI_SCRIPTS_DIR: Ignore _glob_scripts_dir return value
[libquvi.git] / src / misc / scan_scripts.c
blob12f1d4e59fa945df0d662e56f411fbbd63463f92
1 /* libquvi
2 * Copyright (C) 2012 Toni Gundogdu <legatvs@gmail.com>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
20 #include "config.h"
22 #include <string.h>
23 #include <glib.h>
25 #include "quvi.h"
26 /* -- */
27 #include "_quvi_s.h"
28 #include "_quvi_media_s.h"
29 #include "_quvi_playlist_s.h"
30 #include "_quvi_script_s.h"
31 /* -- */
32 #include "misc/script_free.h"
33 #include "misc/playlist.h"
34 #include "misc/media.h"
35 #include "misc/re.h"
36 #include "lua/exec.h"
38 /* Return path to script file. */
39 static GString *_get_fpath(const gchar *path, const gchar *fname)
41 GString *r;
42 gchar *s;
44 s = g_build_filename(path, fname, NULL);
45 r = g_string_new(s);
47 g_free(s);
48 s = NULL;
50 return (r);
53 /* Return SHA1 for script file. */
54 static GString *_file_sha1(const GString *c)
56 GString *r = g_string_new(NULL);
57 if (c != NULL)
59 gchar *s = g_compute_checksum_for_string(G_CHECKSUM_SHA1, c->str, -1);
60 g_string_assign(r, s);
61 g_free(s);
62 s = NULL;
64 return (r);
67 /* Return file contents in a GString. */
68 static GString *_contents(GString *fpath)
70 gchar *c = NULL;
71 g_file_get_contents(fpath->str, &c, NULL, NULL);
72 if (c != NULL)
74 GString *s = g_string_new(c);
75 g_free(c);
76 c = NULL;
77 return (s);
79 return (NULL);
82 static const gchar *scripts_dir = NULL;
83 static const gchar *show_script = NULL;
84 static const gchar *show_dir = NULL;
86 typedef QuviError (*exec_script_ident_callback)(gpointer, GSList*);
87 typedef gpointer (*new_ident_callback)(_quvi_t, const gchar*);
88 typedef void (*free_ident_callback)(gpointer);
90 /* Parses the values returned by the ident function. */
91 static void _chk_script_ident(_quvi_t q, _quvi_script_t qs, gboolean *ok,
92 new_ident_callback cb_new,
93 exec_script_ident_callback cb_exec,
94 free_ident_callback cb_free)
96 static const gchar URL[] = "http://foo";
98 QuviError rc;
99 gpointer p;
100 GSList *s;
102 p = cb_new(q, URL);
103 s = NULL;
104 s = g_slist_prepend(s, qs);
105 rc = cb_exec(p, s);
107 cb_free(p);
108 p = NULL;
110 g_slist_free(s);
111 s = NULL;
113 /* Script ident function should return "no support". If anything else
114 * is returned, there's something wrong with the script. */
115 if (rc == QUVI_ERROR_NO_SUPPORT)
116 *ok = TRUE;
117 else
119 g_critical("[%s] %s", __func__, q->status.errmsg->str);
120 *ok = FALSE;
124 /* Check if a pattern matches in a string. */
125 static gboolean _chk(const gchar *s, const gchar *p)
127 const gboolean r = m_match(s, p);
128 if (show_script != NULL && strlen(show_script) >0)
130 if (r == FALSE)
131 g_message("[%s] no match: `%s'", __func__, p);
133 return (r);
136 /* New script */
137 static gpointer script_new(const gchar *fpath, const gchar *fname,
138 const GString *c)
140 _quvi_script_t qs = g_new0(struct _quvi_script_s, 1);
141 qs->domains = g_string_new(NULL);
142 qs->fpath = g_string_new(fpath);
143 qs->fname = g_string_new(fname);
144 qs->sha1 = _file_sha1(c);
145 return (qs);
148 /* New media script. */
149 static gpointer _new_media_script(_quvi_t q, const gchar *path,
150 const gchar *fname)
152 _quvi_script_t qs;
153 GString *fpath;
154 GString *c;
156 fpath = _get_fpath(path, fname);
157 c = _contents(fpath);
158 qs = NULL;
160 if (c != NULL)
162 gboolean OK =
163 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
164 && _chk(c->str, "^function ident") == TRUE
165 && _chk(c->str, "^function parse") == TRUE);
167 if (OK == TRUE)
169 typedef free_ident_callback fic;
171 qs = script_new(fpath->str, fname, c);
173 _chk_script_ident(q, qs, &OK, m_media_new,
174 l_exec_media_script_ident,
175 (fic) m_media_free);
178 g_string_free(c, TRUE);
179 c = NULL;
181 if (OK == FALSE)
183 m_script_free(qs, NULL);
184 qs = NULL;
188 if (fpath != NULL)
190 g_string_free(fpath, TRUE);
191 fpath = NULL;
194 return (qs);
197 /* New playlist script. */
198 static gpointer _new_playlist_script(_quvi_t q, const gchar *path,
199 const gchar *fname)
201 _quvi_script_t qs;
202 GString *fpath;
203 GString *c;
205 fpath = _get_fpath(path, fname);
206 c = _contents(fpath);
207 qs = NULL;
209 if (c != NULL)
211 gboolean OK =
212 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
213 && _chk(c->str, "^function ident") == TRUE
214 && _chk(c->str, "^function parse") == TRUE);
216 if (OK == TRUE)
218 typedef free_ident_callback fic;
220 qs = script_new(fpath->str, fname, c);
222 _chk_script_ident(q, qs, &OK, m_playlist_new,
223 l_exec_playlist_script_ident,
224 (fic) m_playlist_free);
227 g_string_free(c, TRUE);
228 c = NULL;
230 if (OK == FALSE)
232 m_script_free(qs, NULL);
233 qs = NULL;
237 if (fpath != NULL)
239 g_string_free(fpath, TRUE);
240 fpath = NULL;
243 return (qs);
246 /* New scan script. */
247 static gpointer _new_scan_script(_quvi_t q, const gchar *path,
248 const gchar *fname)
250 _quvi_script_t qs;
251 GString *fpath;
252 GString *c;
254 fpath = _get_fpath(path, fname);
255 c = _contents(fpath);
256 qs = NULL;
258 if (c != NULL)
260 gboolean OK =
261 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
262 && _chk(c->str, "^function parse") == TRUE);
264 if (OK == TRUE)
265 qs = script_new(fpath->str, fname, c);
267 g_string_free(c, TRUE);
268 c = NULL;
270 if (OK == FALSE)
272 m_script_free(qs, NULL);
273 qs = NULL;
277 if (fpath != NULL)
279 g_string_free(fpath, TRUE);
280 fpath = NULL;
283 return (qs);
286 /* New utility script. */
287 static gpointer _new_util_script(_quvi_t q, const gchar *path,
288 const gchar *fname)
290 _quvi_script_t qs;
291 GString *fpath;
292 GString *c;
294 fpath = _get_fpath(path, fname);
295 c = _contents(fpath);
296 qs = NULL;
298 if (c != NULL)
300 const gboolean OK =
301 (_chk(c->str, "^\\-\\-\\s+libquvi\\-scripts") == TRUE);
303 if (OK == TRUE)
304 qs = script_new(fpath->str, fname, c);
306 g_string_free(c, TRUE);
307 c = NULL;
309 if (OK == FALSE)
311 m_script_free(qs, NULL);
312 qs = NULL;
316 if (fpath != NULL)
318 g_string_free(fpath, TRUE);
319 fpath = NULL;
322 return (qs);
325 /* Check for duplicate script. */
326 static gboolean _chkdup_script(_quvi_t q, gpointer script, GSList *l)
328 _quvi_script_t a, b;
329 GSList *curr;
331 a = (_quvi_script_t) script;
332 curr = l;
334 while (curr != NULL)
336 b = (_quvi_script_t) curr->data;
338 if (g_string_equal(a->sha1, b->sha1) == TRUE)
339 return (TRUE);
341 curr = g_slist_next(curr);
343 return (FALSE);
346 /* Include '*.lua' files only. */
347 static gint _lua_files_only(const gchar *fpath)
349 const gchar *ext = strrchr(fpath, '.');
350 return (fpath[0] != '.' && ext != NULL && strcmp(ext, ".lua") == 0);
353 /* Sort scripts alphabetically by filepath. */
354 static gint _sort(gconstpointer a, gconstpointer b)
356 const _quvi_script_t qsa = (_quvi_script_t) a;
357 const _quvi_script_t qsb = (_quvi_script_t) b;
358 return (g_strcmp0(qsa->fpath->str, qsb->fpath->str) >0);
361 typedef gpointer (*new_script_callback)(_quvi_t, const gchar*, const gchar*);
362 typedef gboolean (*chkdup_script_callback)(_quvi_t, gpointer, GSList*);
363 typedef void (*free_script_callback)(gpointer, gpointer);
365 static gboolean _glob_scripts_dir(_quvi_t q, const gchar *path, GSList **dst,
366 new_script_callback cb_new,
367 free_script_callback cb_free,
368 chkdup_script_callback cb_chkdup)
370 const gchar *fname;
371 GDir *dir;
373 if (show_dir != NULL && strlen(show_dir) >0)
374 g_message("libquvi: %s: %s", __func__, path);
376 dir = g_dir_open(path, 0, NULL);
377 if (dir == NULL)
378 return (FALSE);
380 while ((fname = g_dir_read_name(dir)) != NULL)
382 if (_lua_files_only(fname) != 0)
384 gpointer s = cb_new(q, path, fname);
385 if (s == NULL)
387 /* Either file read failed or this is not a valid
388 * libquvi-script. */
389 if (show_script != NULL && strlen(show_script) >0)
391 g_message("libquvi: %s: rejected: %s [INVALID]",
392 __func__, fname);
395 else
397 /* Valid libquvi-script file. */
398 const gboolean r = cb_chkdup(q, s, *dst);
400 if (r == FALSE)
401 *dst = g_slist_prepend(*dst, s);
402 else
404 cb_free(s, NULL);
405 s = NULL;
408 if (show_script != NULL && strlen(show_script) >0)
410 g_message("libquvi: %s: %s: %s [%s]",
411 __func__,
412 (r == FALSE) ? "accepted" : "rejected",
413 fname,
414 (r == FALSE) ? "OK" : "DUPLICATE");
419 g_dir_close(dir);
420 dir = NULL;
422 if (*dst != NULL)
423 *dst = g_slist_sort(*dst, _sort);
425 return (*dst != NULL);
428 typedef enum
430 GLOB_PLAYLIST_SCRIPTS,
431 GLOB_MEDIA_SCRIPTS,
432 GLOB_SCAN_SCRIPTS,
433 GLOB_UTIL_SCRIPTS
434 } GlobType;
436 static const gchar *dir[] =
438 "playlist/",
439 "media/",
440 "scan/",
441 "util/",
442 NULL
445 static gboolean _glob_scripts(_quvi_t q, const GlobType t, GSList **dst)
447 chkdup_script_callback cb_chkdup;
448 free_script_callback cb_free;
449 new_script_callback cb_new;
450 gchar *path;
452 cb_new = NULL;
453 *dst = NULL;
455 switch (t)
457 case GLOB_PLAYLIST_SCRIPTS:
458 cb_new = _new_playlist_script;
459 break;
460 case GLOB_MEDIA_SCRIPTS:
461 cb_new = _new_media_script;
462 break;
463 case GLOB_SCAN_SCRIPTS:
464 cb_new = _new_scan_script;
465 break;
466 case GLOB_UTIL_SCRIPTS:
467 cb_new = _new_util_script;
468 break;
469 default:
470 g_error("%s: %d: invalid mode", __func__, __LINE__);
473 cb_chkdup = _chkdup_script;
474 cb_free = m_script_free;
477 /* LIBQUVI_SCRIPTS_DIR */
479 if (scripts_dir != NULL && strlen(scripts_dir) >0)
481 gchar **r;
482 gint i;
484 r = g_strsplit(scripts_dir, G_DIR_SEPARATOR_S, 0);
486 for (i=0; r[i] != NULL; ++i)
488 path = g_build_path(G_DIR_SEPARATOR_S, r[i], dir[t], NULL);
489 _glob_scripts_dir(q, path, dst, cb_new, cb_free, cb_chkdup);
491 g_free(path);
492 path = NULL;
495 g_strfreev(r);
496 r = NULL;
501 /* Current working directory. */
503 gchar *cwd = g_get_current_dir();
504 path = g_build_path(G_DIR_SEPARATOR_S, cwd, dir[t], NULL);
506 g_free(cwd);
507 cwd = NULL;
509 _glob_scripts_dir(q, path, dst, cb_new, cb_free, cb_chkdup);
511 g_free(path);
512 path = NULL;
515 #ifdef SCRIPTSDIR
517 /* SCRIPTSDIR from config.h */
519 path = g_build_path(G_DIR_SEPARATOR_S, SCRIPTSDIR, dir[t], NULL);
520 _glob_scripts_dir(q, path, dst, cb_new, cb_free, cb_chkdup);
522 g_free(path);
523 path = NULL;
525 #endif /* SCRIPTSDIR */
527 return (*dst != NULL);
530 static gboolean dir_exists(const gchar *path)
532 GDir *dir = g_dir_open(path, 0, NULL);
534 if (dir == NULL)
535 return (FALSE);
537 g_dir_close(dir);
538 dir = NULL;
540 return (TRUE);
543 extern void l_modify_pkgpath(_quvi_t, const gchar*);
545 #define Q_COMMON_DIR "common"
548 * Check for the "common" directory, if found, append the path to the
549 * Lua's package.path setting. We're not interested in the contents of
550 * this directory at this stage.
552 static void chk_common_scripts(_quvi_t q)
554 gchar *path = NULL;
557 /* LIBQUVI_SCRIPTS_DIR (excl.) */
559 if (scripts_dir != NULL && strlen(scripts_dir) >0)
561 gchar **r;
562 gint i;
564 r = g_strsplit(scripts_dir, G_DIR_SEPARATOR_S, 0);
565 for (i=0; r[i] != NULL; ++i)
567 path = g_build_path(G_DIR_SEPARATOR_S,
568 scripts_dir, Q_COMMON_DIR, NULL);
570 if (dir_exists(path) == TRUE)
571 l_modify_pkgpath(q, path);
573 g_free(path);
574 path = NULL;
576 g_strfreev(r);
577 r = NULL;
582 /* Current working directory. */
584 gchar *cwd = g_get_current_dir();
585 path = g_build_path(G_DIR_SEPARATOR_S, cwd, Q_COMMON_DIR, NULL);
587 if (dir_exists(path) == TRUE)
588 l_modify_pkgpath(q, path);
590 g_free(path);
591 path = NULL;
593 g_free(cwd);
594 cwd = NULL;
597 #ifdef SCRIPTSDIR
599 /* SCRIPTSDIR from config.h */
601 path = g_build_path(G_DIR_SEPARATOR_S, SCRIPTSDIR, Q_COMMON_DIR, NULL);
603 if (dir_exists(path) == TRUE)
604 l_modify_pkgpath(q, path);
606 g_free(path);
607 path = NULL;
609 #endif /* SCRIPTSDIR */
612 #undef Q_COMMON_DIR
614 QuviError m_scan_scripts(_quvi_t q)
616 QuviError rc;
618 scripts_dir = g_getenv("LIBQUVI_SCRIPTS_DIR");
619 show_script = g_getenv("LIBQUVI_SHOW_SCRIPT");
620 show_dir = g_getenv("LIBQUVI_SHOW_DIR");
622 chk_common_scripts(q);
624 rc = _glob_scripts(q, GLOB_UTIL_SCRIPTS, &q->scripts.util)
625 ? QUVI_OK
626 : QUVI_ERROR_NO_UTIL_SCRIPTS;
628 if (rc == QUVI_OK)
630 rc = _glob_scripts(q, GLOB_MEDIA_SCRIPTS, &q->scripts.media)
631 ? QUVI_OK
632 : QUVI_ERROR_NO_MEDIA_SCRIPTS;
635 if (rc == QUVI_OK)
637 rc = _glob_scripts(q, GLOB_PLAYLIST_SCRIPTS, &q->scripts.playlist)
638 ? QUVI_OK
639 : QUVI_ERROR_NO_PLAYLIST_SCRIPTS;
642 if (rc == QUVI_OK)
644 rc = _glob_scripts(q, GLOB_SCAN_SCRIPTS, &q->scripts.scan)
645 ? QUVI_OK
646 : QUVI_ERROR_NO_SCAN_SCRIPTS;
649 return (rc);
652 /* vim: set ts=2 sw=2 tw=72 expandtab: */