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/>.
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"
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"
43 /* Return path to script file. */
44 static GString
*_get_fpath(const gchar
*path
, const gchar
*fname
)
49 s
= g_build_filename(path
, fname
, NULL
);
58 /* Return SHA1 for script file. */
59 static GString
*_file_sha1(const GString
*c
)
61 GString
*r
= g_string_new(NULL
);
64 gchar
*s
= g_compute_checksum_for_string(G_CHECKSUM_SHA1
, c
->str
, -1);
65 g_string_assign(r
, s
);
72 /* Return file contents in a GString. */
73 static GString
*_contents(GString
*fpath
)
76 g_file_get_contents(fpath
->str
, &c
, NULL
, NULL
);
79 GString
*s
= g_string_new(c
);
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";
108 s
= g_slist_prepend(NULL
, qs
);
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
)
121 g_critical("[%s] %s", __func__
, q
->status
.errmsg
->str
);
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)
134 g_message("[%s] libquvi: nothing matched the pattern `%s'",
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
);
154 /* New media script. */
155 static gpointer
_new_media_script(_quvi_t q
, const gchar
*path
,
162 fpath
= _get_fpath(path
, fname
);
163 c
= _contents(fpath
);
169 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
170 && _chk(c
->str
, "^function ident") == TRUE
171 && _chk(c
->str
, "^function parse") == 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
);
184 m_script_free(qs
, NULL
);
188 g_string_free(fpath
, TRUE
);
192 /* New subtitle export script. */
194 _new_subtitle_export_script(_quvi_t q
, const gchar
*path
, const gchar
*fname
)
200 fpath
= _get_fpath(path
, fname
);
201 c
= _contents(fpath
);
207 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
208 && _chk(c
->str
, "^function ident") == TRUE
209 && _chk(c
->str
, "^function export") == 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
);
222 m_script_free(qs
, NULL
);
226 g_string_free(fpath
, TRUE
);
230 /* New subtitle script. */
231 static gpointer
_new_subtitle_script(_quvi_t q
, const gchar
*path
,
238 fpath
= _get_fpath(path
, fname
);
239 c
= _contents(fpath
);
245 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
246 && _chk(c
->str
, "^function ident") == TRUE
247 && _chk(c
->str
, "^function parse") == 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
);
260 m_script_free(qs
, NULL
);
264 g_string_free(fpath
, TRUE
);
268 /* New playlist script. */
269 static gpointer
_new_playlist_script(_quvi_t q
, const gchar
*path
,
276 fpath
= _get_fpath(path
, fname
);
277 c
= _contents(fpath
);
283 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
284 && _chk(c
->str
, "^function ident") == TRUE
285 && _chk(c
->str
, "^function parse") == 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
);
298 m_script_free(qs
, NULL
);
302 g_string_free(fpath
, TRUE
);
306 /* New scan script. */
307 static gpointer
_new_scan_script(_quvi_t q
, const gchar
*path
,
314 fpath
= _get_fpath(path
, fname
);
315 c
= _contents(fpath
);
321 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
322 && _chk(c
->str
, "^function parse") == TRUE
);
325 qs
= _script_new(fpath
->str
, fname
, c
);
329 m_script_free(qs
, NULL
);
333 g_string_free(fpath
, TRUE
);
337 /* New utility script. */
338 static gpointer
_new_util_script(_quvi_t q
, const gchar
*path
,
345 fpath
= _get_fpath(path
, fname
);
346 c
= _contents(fpath
);
352 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
);
355 qs
= _script_new(fpath
->str
, fname
, c
);
359 m_script_free(qs
, NULL
);
363 g_string_free(fpath
, TRUE
);
367 /* Check for duplicate script. */
368 static gboolean
_chkdup_script(_quvi_t q
, gpointer script
, GSList
*l
)
373 a
= (_quvi_script_t
) script
;
378 b
= (_quvi_script_t
) curr
->data
;
380 if (g_string_equal(a
->sha1
, b
->sha1
) == TRUE
)
383 curr
= g_slist_next(curr
);
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
)
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
);
414 while ( (fname
= g_dir_read_name(dir
)) != NULL
)
416 if (_lua_files_only(fname
) != 0)
418 gpointer s
= cb_new(q
, path
, fname
);
421 /* Either file read failed or this is not a valid
423 if (show_script
!= NULL
&& strlen(show_script
) >0)
425 g_message("[%s] libquvi: rejected: %s [INVALID]",
431 /* Valid libquvi-script file. */
432 const gboolean r
= cb_chkdup(q
, s
, *dst
);
435 *dst
= g_slist_prepend(*dst
, s
);
442 if (show_script
!= NULL
&& strlen(show_script
) >0)
444 g_message("[%s] libquvi: %s: %s [%s]",
446 (r
== FALSE
) ? "accepted" : "rejected",
448 (r
== FALSE
) ? "OK" : "DUPLICATE");
457 *dst
= g_slist_reverse(*dst
);
459 return (*dst
!= NULL
);
464 GLOB_SUBTITLE_EXPORT_SCRIPTS
,
465 GLOB_SUBTITLE_SCRIPTS
,
466 GLOB_PLAYLIST_SCRIPTS
,
473 static const gchar
*glob_dir
[_GLOB_COUNT
] =
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
;
493 case GLOB_SUBTITLE_EXPORT_SCRIPTS
:
494 cb_new
= _new_subtitle_export_script
;
495 dst
= &q
->scripts
.subtitle_export
;
497 case GLOB_SUBTITLE_SCRIPTS
:
498 cb_new
= _new_subtitle_script
;
499 dst
= &q
->scripts
.subtitle
;
501 case GLOB_PLAYLIST_SCRIPTS
:
502 cb_new
= _new_playlist_script
;
503 dst
= &q
->scripts
.playlist
;
505 case GLOB_MEDIA_SCRIPTS
:
506 cb_new
= _new_media_script
;
507 dst
= &q
->scripts
.media
;
509 case GLOB_SCAN_SCRIPTS
:
510 cb_new
= _new_scan_script
;
511 dst
= &q
->scripts
.scan
;
513 case GLOB_UTIL_SCRIPTS
:
514 cb_new
= _new_util_script
;
515 dst
= &q
->scripts
.util
;
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)
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
);
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
);
555 _glob_scripts_dir(q
, path
, dst
, cb_new
, cb_free
, cb_chkdup
);
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
);
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
);
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
);
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
)
607 /* LIBQUVI_SCRIPTS_DIR */
609 if (scripts_dir
!= NULL
&& strlen(scripts_dir
) >0)
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
);
629 if (excl_scripts_dir
== TRUE
)
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
);
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
);
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
);
672 #endif /* SCRIPTSDIR */
677 QuviError
m_scan_scripts(_quvi_t q
)
683 const gchar
*s
= g_getenv("LIBQUVI_EXCLUSIVE_SCRIPTS_DIR");
685 excl_scripts_dir
= (s
!= NULL
&& strlen(s
) >0)
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;
700 while (++t
<_GLOB_COUNT
&& r
==QUVI_OK
)
702 r
= (_glob_scripts(q
,t
) == TRUE
) ? QUVI_OK
: e
;
708 /* vim: set ts=2 sw=2 tw=72 expandtab: */