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
28 #include "_quvi_media_s.h"
29 #include "_quvi_playlist_s.h"
30 #include "_quvi_script_s.h"
32 #include "misc/script_free.h"
33 #include "misc/playlist.h"
34 #include "misc/media.h"
38 /* Return path to script file. */
39 static GString
*_get_fpath(const gchar
*path
, const gchar
*fname
)
44 s
= g_build_filename(path
, fname
, NULL
);
53 /* Return SHA1 for script file. */
54 static GString
*_file_sha1(const GString
*c
)
56 GString
*r
= g_string_new(NULL
);
59 gchar
*s
= g_compute_checksum_for_string(G_CHECKSUM_SHA1
, c
->str
, -1);
60 g_string_assign(r
, s
);
67 /* Return file contents in a GString. */
68 static GString
*_contents(GString
*fpath
)
71 g_file_get_contents(fpath
->str
, &c
, NULL
, NULL
);
74 GString
*s
= g_string_new(c
);
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";
104 s
= g_slist_prepend(s
, qs
);
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
)
119 g_critical("[%s] %s", __func__
, q
->status
.errmsg
->str
);
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)
131 g_message("[%s] no match: `%s'", __func__
, p
);
137 static gpointer
script_new(const gchar
*fpath
, const gchar
*fname
,
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
);
148 /* New media script. */
149 static gpointer
_new_media_script(_quvi_t q
, const gchar
*path
,
156 fpath
= _get_fpath(path
, fname
);
157 c
= _contents(fpath
);
163 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
164 && _chk(c
->str
, "^function ident") == TRUE
165 && _chk(c
->str
, "^function parse") == 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
,
178 g_string_free(c
, TRUE
);
183 m_script_free(qs
, NULL
);
190 g_string_free(fpath
, TRUE
);
197 /* New playlist script. */
198 static gpointer
_new_playlist_script(_quvi_t q
, const gchar
*path
,
205 fpath
= _get_fpath(path
, fname
);
206 c
= _contents(fpath
);
212 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
213 && _chk(c
->str
, "^function ident") == TRUE
214 && _chk(c
->str
, "^function parse") == 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
);
232 m_script_free(qs
, NULL
);
239 g_string_free(fpath
, TRUE
);
246 /* New scan script. */
247 static gpointer
_new_scan_script(_quvi_t q
, const gchar
*path
,
254 fpath
= _get_fpath(path
, fname
);
255 c
= _contents(fpath
);
261 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
262 && _chk(c
->str
, "^function parse") == TRUE
);
265 qs
= script_new(fpath
->str
, fname
, c
);
267 g_string_free(c
, TRUE
);
272 m_script_free(qs
, NULL
);
279 g_string_free(fpath
, TRUE
);
286 /* New utility script. */
287 static gpointer
_new_util_script(_quvi_t q
, const gchar
*path
,
294 fpath
= _get_fpath(path
, fname
);
295 c
= _contents(fpath
);
301 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
);
304 qs
= script_new(fpath
->str
, fname
, c
);
306 g_string_free(c
, TRUE
);
311 m_script_free(qs
, NULL
);
318 g_string_free(fpath
, TRUE
);
325 /* Check for duplicate script. */
326 static gboolean
_chkdup_script(_quvi_t q
, gpointer script
, GSList
*l
)
331 a
= (_quvi_script_t
) script
;
336 b
= (_quvi_script_t
) curr
->data
;
338 if (g_string_equal(a
->sha1
, b
->sha1
) == TRUE
)
341 curr
= g_slist_next(curr
);
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
)
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
);
380 while ((fname
= g_dir_read_name(dir
)) != NULL
)
382 if (_lua_files_only(fname
) != 0)
384 gpointer s
= cb_new(q
, path
, fname
);
387 /* Either file read failed or this is not a valid
389 if (show_script
!= NULL
&& strlen(show_script
) >0)
391 g_message("libquvi: %s: rejected: %s [INVALID]",
397 /* Valid libquvi-script file. */
398 const gboolean r
= cb_chkdup(q
, s
, *dst
);
401 *dst
= g_slist_prepend(*dst
, s
);
408 if (show_script
!= NULL
&& strlen(show_script
) >0)
410 g_message("libquvi: %s: %s: %s [%s]",
412 (r
== FALSE
) ? "accepted" : "rejected",
414 (r
== FALSE
) ? "OK" : "DUPLICATE");
423 *dst
= g_slist_sort(*dst
, _sort
);
425 return (*dst
!= NULL
);
430 GLOB_PLAYLIST_SCRIPTS
,
436 static const gchar
*dir
[] =
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
;
457 case GLOB_PLAYLIST_SCRIPTS
:
458 cb_new
= _new_playlist_script
;
460 case GLOB_MEDIA_SCRIPTS
:
461 cb_new
= _new_media_script
;
463 case GLOB_SCAN_SCRIPTS
:
464 cb_new
= _new_scan_script
;
466 case GLOB_UTIL_SCRIPTS
:
467 cb_new
= _new_util_script
;
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)
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
);
501 /* Current working directory. */
503 gchar
*cwd
= g_get_current_dir();
504 path
= g_build_path(G_DIR_SEPARATOR_S
, cwd
, dir
[t
], NULL
);
509 _glob_scripts_dir(q
, path
, dst
, cb_new
, cb_free
, cb_chkdup
);
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
);
525 #endif /* SCRIPTSDIR */
527 return (*dst
!= NULL
);
530 static gboolean
dir_exists(const gchar
*path
)
532 GDir
*dir
= g_dir_open(path
, 0, NULL
);
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
)
557 /* LIBQUVI_SCRIPTS_DIR (excl.) */
559 if (scripts_dir
!= NULL
&& strlen(scripts_dir
) >0)
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
);
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
);
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
);
609 #endif /* SCRIPTSDIR */
614 QuviError
m_scan_scripts(_quvi_t q
)
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
)
626 : QUVI_ERROR_NO_UTIL_SCRIPTS
;
630 rc
= _glob_scripts(q
, GLOB_MEDIA_SCRIPTS
, &q
->scripts
.media
)
632 : QUVI_ERROR_NO_MEDIA_SCRIPTS
;
637 rc
= _glob_scripts(q
, GLOB_PLAYLIST_SCRIPTS
, &q
->scripts
.playlist
)
639 : QUVI_ERROR_NO_PLAYLIST_SCRIPTS
;
644 rc
= _glob_scripts(q
, GLOB_SCAN_SCRIPTS
, &q
->scripts
.scan
)
646 : QUVI_ERROR_NO_SCAN_SCRIPTS
;
652 /* vim: set ts=2 sw=2 tw=72 expandtab: */