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/>.
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 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";
107 s
= g_slist_prepend(NULL
, qs
);
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
)
120 g_critical("[%s] %s", __func__
, q
->status
.errmsg
->str
);
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)
133 g_message("[%s] libquvi: nothing matched the pattern `%s'",
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
);
153 /* New media script. */
154 static gpointer
_new_media_script(_quvi_t q
, const gchar
*path
,
161 fpath
= _get_fpath(path
, fname
);
162 c
= _contents(fpath
);
168 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
169 && _chk(c
->str
, "^function ident") == TRUE
170 && _chk(c
->str
, "^function parse") == 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
);
183 m_script_free(qs
, NULL
);
187 g_string_free(fpath
, TRUE
);
191 /* New subtitle export script. */
193 _new_subtitle_export_script(_quvi_t q
, const gchar
*path
, const gchar
*fname
)
199 fpath
= _get_fpath(path
, fname
);
200 c
= _contents(fpath
);
206 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
207 && _chk(c
->str
, "^function ident") == TRUE
208 && _chk(c
->str
, "^function export") == 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
);
221 m_script_free(qs
, NULL
);
225 g_string_free(fpath
, TRUE
);
229 /* New subtitle script. */
230 static gpointer
_new_subtitle_script(_quvi_t q
, const gchar
*path
,
237 fpath
= _get_fpath(path
, fname
);
238 c
= _contents(fpath
);
244 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
245 && _chk(c
->str
, "^function ident") == TRUE
246 && _chk(c
->str
, "^function parse") == 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
);
259 m_script_free(qs
, NULL
);
263 g_string_free(fpath
, TRUE
);
267 /* New playlist script. */
268 static gpointer
_new_playlist_script(_quvi_t q
, const gchar
*path
,
275 fpath
= _get_fpath(path
, fname
);
276 c
= _contents(fpath
);
282 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
283 && _chk(c
->str
, "^function ident") == TRUE
284 && _chk(c
->str
, "^function parse") == 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
);
297 m_script_free(qs
, NULL
);
301 g_string_free(fpath
, TRUE
);
305 /* New scan script. */
306 static gpointer
_new_scan_script(_quvi_t q
, const gchar
*path
,
313 fpath
= _get_fpath(path
, fname
);
314 c
= _contents(fpath
);
320 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
321 && _chk(c
->str
, "^function parse") == TRUE
);
324 qs
= _script_new(fpath
->str
, fname
, c
);
328 m_script_free(qs
, NULL
);
332 g_string_free(fpath
, TRUE
);
336 /* New utility script. */
337 static gpointer
_new_util_script(_quvi_t q
, const gchar
*path
,
344 fpath
= _get_fpath(path
, fname
);
345 c
= _contents(fpath
);
351 (_chk(c
->str
, "^\\-\\-\\s+libquvi\\-scripts") == TRUE
);
354 qs
= _script_new(fpath
->str
, fname
, c
);
358 m_script_free(qs
, NULL
);
362 g_string_free(fpath
, TRUE
);
366 /* Check for duplicate script. */
367 static gboolean
_chkdup_script(_quvi_t q
, gpointer script
, GSList
*l
)
372 a
= (_quvi_script_t
) script
;
377 b
= (_quvi_script_t
) curr
->data
;
379 if (g_string_equal(a
->sha1
, b
->sha1
) == TRUE
)
382 curr
= g_slist_next(curr
);
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
)
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
);
413 while ( (fname
= g_dir_read_name(dir
)) != NULL
)
415 if (_lua_files_only(fname
) != 0)
417 gpointer s
= cb_new(q
, path
, fname
);
420 /* Either file read failed or this is not a valid
422 if (show_script
!= NULL
&& strlen(show_script
) >0)
424 g_message("[%s] libquvi: rejected: %s [INVALID]",
430 /* Valid libquvi-script file. */
431 const gboolean r
= cb_chkdup(q
, s
, *dst
);
434 *dst
= g_slist_prepend(*dst
, s
);
441 if (show_script
!= NULL
&& strlen(show_script
) >0)
443 g_message("[%s] libquvi: %s: %s [%s]",
445 (r
== FALSE
) ? "accepted" : "rejected",
447 (r
== FALSE
) ? "OK" : "DUPLICATE");
456 *dst
= g_slist_reverse(*dst
);
458 return (*dst
!= NULL
);
463 GLOB_SUBTITLE_EXPORT_SCRIPTS
,
464 GLOB_SUBTITLE_SCRIPTS
,
465 GLOB_PLAYLIST_SCRIPTS
,
472 static const gchar
*glob_dir
[_GLOB_COUNT
] =
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
;
492 case GLOB_SUBTITLE_EXPORT_SCRIPTS
:
493 cb_new
= _new_subtitle_export_script
;
494 dst
= &q
->scripts
.subtitle_export
;
496 case GLOB_SUBTITLE_SCRIPTS
:
497 cb_new
= _new_subtitle_script
;
498 dst
= &q
->scripts
.subtitle
;
500 case GLOB_PLAYLIST_SCRIPTS
:
501 cb_new
= _new_playlist_script
;
502 dst
= &q
->scripts
.playlist
;
504 case GLOB_MEDIA_SCRIPTS
:
505 cb_new
= _new_media_script
;
506 dst
= &q
->scripts
.media
;
508 case GLOB_SCAN_SCRIPTS
:
509 cb_new
= _new_scan_script
;
510 dst
= &q
->scripts
.scan
;
512 case GLOB_UTIL_SCRIPTS
:
513 cb_new
= _new_util_script
;
514 dst
= &q
->scripts
.util
;
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)
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
);
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
);
551 _glob_scripts_dir(q
, path
, dst
, cb_new
, cb_free
, cb_chkdup
);
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
);
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
);
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
);
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
)
603 /* LIBQUVI_SCRIPTS_DIR (excl.) */
605 if (scripts_dir
!= NULL
&& strlen(scripts_dir
) >0)
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
);
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
);
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
);
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
);
665 #endif /* SCRIPTSDIR */
670 QuviError
m_scan_scripts(_quvi_t q
)
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;
685 while (++t
<_GLOB_COUNT
&& r
==QUVI_OK
)
687 r
= (_glob_scripts(q
,t
) == TRUE
) ? QUVI_OK
: e
;
693 /* vim: set ts=2 sw=2 tw=72 expandtab: */