summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2011-07-25 13:21:55 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2011-07-25 13:21:55 (GMT)
commit39e1ad85f7fb63156621112a28a876265d9fa1f0 (patch) (side-by-side diff)
tree3607cb92f570680bb6c2016018e373834edd8b3d
parentc6230a46e93e9fb1525d5a036074aa7b5ff8a76e (diff)
downloadidest-39e1ad85f7fb63156621112a28a876265d9fa1f0.tar.gz
idest-39e1ad85f7fb63156621112a28a876265d9fa1f0.tar.bz2
Implement --batch option (a generalization of --format).
* NEWS: Update * doc/idest.texi: Update. * scheme/Makefile.am (EXTRA_DIST,site_DATA): Add batch.scm (dist-hook): Exclude backup files. * scheme/batch.scm: New file. * scheme/idest/batch/help.scm: New file. * scheme/idest/format/help.scm: Use list-modules. * scheme/idest/format/shortlist.scm: Fix typo. * scheme/idest/list-modules.scm: New file (from scheme/idest/format/help.scm). * src/cmdline.opt: Remove the --function option. New option --batch. * src/guile.c (guile_function,guile_script): Remove. (load_closure): Remove struct. (load_handler,load_handler_path): data points to char **. (guile_load): Change signature: filename is superfluous, use argv[0] instead. All callers updated. * src/idest.h (ed_list,guile_script,guile_function): Remove. * src/main.c (dry_run_option,batch_name): New variables. (set_guile_argv): New function. (main): Handle batch_name and dry_run_option.
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--NEWS74
-rw-r--r--doc/idest.texi4
-rw-r--r--scheme/Makefile.am4
-rw-r--r--scheme/batch.scm46
-rw-r--r--scheme/idest/batch/help.scm25
-rw-r--r--scheme/idest/format/help.scm82
-rw-r--r--scheme/idest/format/shortlist.scm2
-rw-r--r--scheme/idest/list-modules.scm100
-rw-r--r--src/cmdline.opt17
-rw-r--r--src/guile.c55
-rw-r--r--src/idest.h3
-rw-r--r--src/main.c43
12 files changed, 297 insertions, 158 deletions
diff --git a/NEWS b/NEWS
index d0c9552..a5b4d89 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-IdEst -- history of user-visible changes. 2011-07-17
+IdEst -- history of user-visible changes. 2011-07-25
Copyright (C) 2009-2011 Sergey Poznyakoff
See the end of file for copying conditions.
@@ -64,6 +64,51 @@ only if the input file originally had no ID3 tags.
Prints verbose frame descriptions instead of their short names.
+* The `--script' option.
+
+The `--script' (`-S') option stops further argument processing and
+passes the rest of command line as argument to the script. The script
+can modify the arguments (e.g. by removing its command-line options).
+The modified arguments are then returned to idest and processed as
+a list of input file names.
+
+* Argument to the --script option is searched in the %load-path.
+
+This feature is disabled if the argument contains directory separators
+(/).
+
+* New option --dry-run
+
+The `--dry-run' (`-n') option can be used together with `--script'
+or `--batch' options. It instructs idest to print all the
+modifications the script produced without actually applying them
+to the file(s). Use it to check whether your scripts work in
+the expected way and correctly modify the frames. For example,
+to test the script `modify.scm' run
+
+ idest --dry-run --script modify.scm *.mp3
+
+* New option --format
+
+The `--format=NAME' (`-H NAME') option instructs idest to run a
+user-defined format NAME. This option stops further argument
+processing and passes the rest of command line as argument to
+the format script. To obtain a list of available formats along
+with short descriptions of them, run `idest --format=help'.
+
+See also `User-defined formats and batch scripts', below.
+
+* New option --batch
+
+The `--batch=NAME' (`-B NAME') option instructs idest to run a
+user-defined batch modification module NAME. This option stops
+further argument processing and passes the rest of command line as
+argument to the batch script. To obtain a list of available batch
+scripts along with short descriptions of them, run
+`idest --batch=help'.
+
+See also `User-defined formats and batch scripts', below.
+
* The --delete option takes optional argument
The argument, if supplied, is a comma-separated list of the
@@ -108,7 +153,7 @@ The full syntax is:
(See above for a description of the optional QUAL part). For example:
- idest --query Title%TIT2,Artist%TPE1,Author%TOLY,Composer%TCOM *.mp3
+ idest --query=Title%TIT2,Artist%TPE1,Author%TOLY,Composer%TCOM *.mp3
* Changes in Scheme representation of frame lists.
@@ -146,25 +191,22 @@ $HOME/.idest.scm and $GUILE_SITE/idest/idest.scm (where HOME is the
user home directory and GUILE_SITE is Guile site-wide directory).
The first of them which is found is loaded as a Scheme source file.
-* Argument to the --script option is searched in the %load-path.
-
-This feature is disabled if the argument contains directory separators
-(/).
-
* Guile scripts can access and modify command line arguments.
-* Test script `dry-run.scm'
-
-The test script `dry-run.scm' is installed in the program script
-directory. It allows you to check whether your scripts work in
-the expected way and correctly modify the frames. For example,
-to test the script `modify.scm' run
+See "The `--script' option" above.
- idest --script dry-run modify.scm *.mp3
+* User-defined formats and batch scripts.
-The program will print input file names and frames produced by
-`modify.scm' without actually touching your data.
+User-defined formats provide a convenient way to extend `idest'
+functionality. A format is a Guile script. It differs from the
+usual scripts in that it is written as a module and stored in
+directory "idest/format", somewhere in the %load-path. Formats are
+invoked using the `--format' (`-H') option (see above). Several
+formats are shipped with the idest.
+Batch scripts are similar to formats, except that they provide a way
+to modify tags. Batch scripts are stored in "idest/format" directory
+and are called via the `--batch' (`-B') option (see above).
Version 1.2, 2011-04-10
diff --git a/doc/idest.texi b/doc/idest.texi
index bd6bad9..fa73ad4 100644
--- a/doc/idest.texi
+++ b/doc/idest.texi
@@ -1146,10 +1146,6 @@ are deleted. @xref{Delete}.
Print verbose frame descriptions instead of short names.
@xref{describe}.
-@item -f
-@itemx --function=@var{name}
-Guile function to call. @xref{Scripting}.
-
@item -F @var{flist}
@itemx --filter=@var{flist}
Operate only on frames from @var{flist}. This option affects the
diff --git a/scheme/Makefile.am b/scheme/Makefile.am
index a71eb52..b8eefe7 100644
--- a/scheme/Makefile.am
+++ b/scheme/Makefile.am
@@ -15,17 +15,19 @@
# along with Idest. If not, see <http://www.gnu.org/licenses/>.
EXTRA_DIST=\
+ batch.scm\
dry-run.scm\
format.scm
sitedir = @GUILE_SITE@/$(PACKAGE)
site_DATA=\
+ batch.scm\
dry-run.scm\
format.scm
dist-hook:
- tar -C $(srcdir) -c -f - --exclude-vcs idest | \
+ tar -C $(srcdir) -c -f - --exclude-vcs --exclude-backups idest | \
tar -C $(distdir) -x -f -
versionsitedir = $(sitedir)/$(VERSION)
diff --git a/scheme/batch.scm b/scheme/batch.scm
new file mode 100644
index 0000000..5fa368c
--- a/dev/null
+++ b/scheme/batch.scm
@@ -0,0 +1,46 @@
+;; This file is part of Idest
+;; Copyright (C) 2011 Sergey Poznyakoff
+;; Idest is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+;;
+;; Idest is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with Idest. If not, see <http://www.gnu.org/licenses/>.
+
+(let ((cmd (command-line))
+ (mod-pathname #f)
+ (saved-load-hook %load-hook))
+
+ (set! %load-hook (lambda (filename)
+ (set! mod-pathname filename)
+ (set! %load-hook saved-load-hook)))
+
+ (let ((mod-name (list-ref cmd 1)))
+ (set-program-arguments (list-tail cmd 1))
+
+ (catch 'misc-error
+ (lambda ()
+ (set! idest-main
+ (module-ref
+ (resolve-module
+ (list 'idest 'batch (string->symbol mod-name)))
+ 'idest-main)))
+ (lambda (key port message args sys-error)
+ (with-output-to-port
+ (current-error-port)
+ (cond
+ ((not mod-pathname)
+ (format #t
+ "idest: no such batch: ~A~%" mod-name)
+ (exit 1))
+ (else
+ (apply format #t (string-append "idest: " message) args)
+ (newline)
+ (exit 1))))))
+ (set! idest-readonly #f)))
diff --git a/scheme/idest/batch/help.scm b/scheme/idest/batch/help.scm
new file mode 100644
index 0000000..48891ce
--- a/dev/null
+++ b/scheme/idest/batch/help.scm
@@ -0,0 +1,25 @@
+;; This file is part of Idest
+;; Copyright (C) 2011 Sergey Poznyakoff
+;;
+;; Idest is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+;;
+;; Idest is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with Idest. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (idest batch help))
+
+(use-modules (idest list-modules))
+
+(idest-list-modules 'batch)
+(exit 0)
+
+
+
diff --git a/scheme/idest/format/help.scm b/scheme/idest/format/help.scm
index 4f739e6..e55dda1 100644
--- a/scheme/idest/format/help.scm
+++ b/scheme/idest/format/help.scm
@@ -16,86 +16,10 @@
(define-module (idest format help))
-(use-modules (ice-9 getopt-long)
- (srfi srfi-1))
+(use-modules (idest list-modules))
-(define (strip-suffix name)
- (call-with-current-continuation
- (lambda (return)
- (for-each
- (lambda (suf)
- (if (and (not (string-null? suf)) (string-suffix? suf name))
- (return
- (substring name 0 (- (string-length name) (string-length suf))))))
- %load-extensions)
- (return #f))))
+(idest-list-modules 'format)
+(exit 0)
-(let* ((saved-load-hook %load-hook)
- (cmd (command-line))
- (progname (car cmd))
- ;; Collect a list of possible modules. List elements are conses:
- ;; (basename . dir)
- ;; where basename is the module name and dir is the directory where
- ;; it is found. Make sure only one entry for each basename exists.
- ;; Sort the list alphabetically on basename.
- (candidates
- (sort
- (fold
- (lambda (elt prev)
- (catch 'misc-error
- (lambda ()
- (let ((dir (string-append elt "/idest/format")))
- (if (and dir
- (file-exists? dir)
- (eq? (stat:type (stat dir)) 'directory))
- (let ((d (opendir dir)))
- (let loop ((file (readdir d)))
- (cond
- ((not (eof-object? file))
- (if (eq? (stat:type
- (stat (string-append dir "/" file)))
- 'regular)
- (let ((base (strip-suffix file)))
- (if (and base
- (not (assoc-ref prev base)))
- (set! prev (cons (cons base dir)
- prev)))))
- (loop (readdir d)))))))))
- (lambda (key . args)
- #f))
- prev)
- '()
- %load-path)
- (lambda (a b)
- (string<? (car a) (car b))))))
- ;; Try out each candidate and print ist name, directory and description
- ;; if it happens to be a valid idest format module.
- ;; Take care not to bail out on errors. Disable %load-hook as it migh
- ;; clobber the output.
- (set! %load-hook #f)
- (for-each
- (lambda (candidate)
- (catch 'misc-error
- (lambda ()
- (let ((mod (resolve-module
- (list 'idest 'format
- (string->symbol (car candidate))))))
- ; Check if it defines idest-main
- (module-ref mod 'idest-main)
- (format #t "~A (from ~A): ~A~%"
- (car candidate) (cdr candidate)
- (catch #t
- (lambda ()
- (module-ref mod 'description))
- (lambda (key . args)
- "no description")))))
- (lambda (key . args)
- #f)))
- candidates)
- (newline)
- (set! %load-hook saved-load-hook)
- (exit 0))
-
-
diff --git a/scheme/idest/format/shortlist.scm b/scheme/idest/format/shortlist.scm
index 4dd418b..a3d5bed 100644
--- a/scheme/idest/format/shortlist.scm
+++ b/scheme/idest/format/shortlist.scm
@@ -8,7 +8,7 @@
(define-module (idest format shortlist))
(define-public description
- "display title, artist name and year, on a signle line")
+ "display title, artist name and year, on a single line")
(define (get-frame code frames)
(or (assoc-ref
diff --git a/scheme/idest/list-modules.scm b/scheme/idest/list-modules.scm
new file mode 100644
index 0000000..bd538ea
--- a/dev/null
+++ b/scheme/idest/list-modules.scm
@@ -0,0 +1,100 @@
+;; This file is part of Idest
+;; Copyright (C) 2011 Sergey Poznyakoff
+;;
+;; Idest is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+;;
+;; Idest is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with Idest. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (idest list-modules))
+
+(use-modules (ice-9 getopt-long)
+ (srfi srfi-1))
+
+(define (strip-suffix name)
+ (call-with-current-continuation
+ (lambda (return)
+ (for-each
+ (lambda (suf)
+ (if (and (not (string-null? suf)) (string-suffix? suf name))
+ (return
+ (substring name 0 (- (string-length name) (string-length suf))))))
+ %load-extensions)
+ (return #f))))
+
+(define-public (idest-list-modules type)
+ (let ((saved-load-hook %load-hook)
+ ;; Collect a list of possible modules. List elements are conses:
+ ;; (basename . dir)
+ ;; where basename is the module name and dir is the directory where
+ ;; it is found. Make sure only one entry for each basename exists.
+ ;; Sort the list alphabetically on basename.
+ (candidates
+ (sort
+ (fold
+ (lambda (elt prev)
+ (catch 'misc-error
+ (lambda ()
+ (let ((dir (string-append elt "/idest/"
+ (symbol->string type))))
+ (if (and dir
+ (file-exists? dir)
+ (eq? (stat:type (stat dir)) 'directory))
+ (let ((d (opendir dir)))
+ (let loop ((file (readdir d)))
+ (cond
+ ((not (eof-object? file))
+ (if (eq? (stat:type
+ (stat (string-append dir "/" file)))
+ 'regular)
+ (let ((base (strip-suffix file)))
+ (if (and base
+ (not (assoc-ref prev base)))
+ (set! prev (cons (cons base dir)
+ prev)))))
+ (loop (readdir d)))))))))
+ (lambda (key . args)
+ #f))
+ prev)
+ '()
+ %load-path)
+ (lambda (a b)
+ (string<? (car a) (car b))))))
+
+ ;; Try out each candidate and print ist name, directory and description
+ ;; if it happens to be a valid idest format module.
+ ;; Take care not to bail out on errors. Disable %load-hook as it migh
+ ;; clobber the output.
+ (set! %load-hook #f)
+ (for-each
+ (lambda (candidate)
+ (catch 'misc-error
+ (lambda ()
+ (let ((mod (resolve-module
+ (list 'idest type
+ (string->symbol (car candidate))))))
+ ; Check if it defines idest-main
+ (module-ref mod 'idest-main)
+ (format #t "~A (from ~A): ~A~%"
+ (car candidate) (cdr candidate)
+ (catch #t
+ (lambda ()
+ (module-ref mod 'description))
+ (lambda (key . args)
+ "no description")))))
+ (lambda (key . args)
+ #f)))
+ candidates)
+ (newline)
+ (set! %load-hook saved-load-hook)))
+
+
+
diff --git a/src/cmdline.opt b/src/cmdline.opt
index f2d891d..0043164 100644
--- a/src/cmdline.opt
+++ b/src/cmdline.opt
@@ -184,8 +184,8 @@ GROUP([<Scripting options>])
OPTION(script,S,FILE,
[<read Guile script from FILE; this stops further argument processing>])
BEGIN
- guile_script = optarg;
- guile_argv = argv + optind; /* Save rest of arguments */
+ set_guile_argv(argc - optind, argv + optind); /* Save rest of arguments */
+ *--guile_argv = optarg;
optind = argc; /* Stop argument processing */
END
@@ -196,10 +196,17 @@ BEGIN
stop = 1; /* Stop argument processing */
END
-OPTION(function,f,NAME,
- [<guile function to call>])
+OPTION(batch,B,NAME,
+ [<apply batch modification module NAME; this stops further argument processing>])
BEGIN
- guile_function = optarg;
+ batch_name = optarg;
+ stop = 1; /* Stop argument processing */
+END
+
+OPTION(dry-run,n,,
+ [<run the script, print modified frames but do not write them to the file>])
+BEGIN
+ dry_run_option = 1;
END
OPTION(trace,,[LEVEL],
diff --git a/src/guile.c b/src/guile.c
index b75b674..2de2fe2 100644
--- a/src/guile.c
+++ b/src/guile.c
@@ -22,8 +22,6 @@
int guile_inited = 0;
int guile_debug = 1;
-char *guile_script;
-char *guile_function;
char **guile_argv;
SCM_GLOBAL_VARIABLE_INIT(sym_idest_main, "idest-main", SCM_EOL);
@@ -81,49 +79,31 @@ guile_safe_exec(SCM (*handler)(void *data), void *data, SCM *result)
return 0;
}
-struct load_closure {
- char *filename;
- int argc;
- char **argv;
-};
-
static SCM
load_handler(void *data)
{
- struct load_closure *lp = data;
+ char **argv = data;
- scm_set_program_arguments(lp->argc, lp->argv, lp->filename);
- scm_primitive_load(scm_from_locale_string(lp->filename));
+ scm_set_program_arguments(-1, argv, NULL);
+ scm_primitive_load(scm_from_locale_string(argv[0]));
return SCM_UNDEFINED;
}
static SCM
load_handler_path(void *data)
{
- struct load_closure *lp = data;
-
- scm_set_program_arguments(lp->argc, lp->argv, lp->filename);
- scm_primitive_load_path(scm_from_locale_string(lp->filename));
+ char **argv = data;
+
+ scm_set_program_arguments(-1, argv, NULL);
+ scm_primitive_load_path(scm_from_locale_string(argv[0]));
return SCM_UNDEFINED;
}
static int
-guile_load(char *filename, int use_path, char **argv)
+guile_load(char **argv, int use_path)
{
- struct load_closure lc;
- if (argv) {
- lc.argc = -1;
- lc.argv = argv;
- } else {
- char *s_argv[2];
- s_argv[0] = filename;
- s_argv[1] = NULL;
- lc.argc = 1;
- lc.argv = s_argv;
- }
- lc.filename = filename;
if (guile_safe_exec(use_path ? load_handler_path : load_handler,
- &lc, NULL))
+ argv, NULL))
exit(1);
}
@@ -639,7 +619,7 @@ load_startup_file()
for (i = 0; argv[i]; i++) {
if (access(argv[i], R_OK) == 0) {
- if (guile_load(argv[i], 0, argv + i + 1))
+ if (guile_load(argv + i, 0))
error(1, 0, "cannot load startup script %s",
argv[i]);
break;
@@ -658,7 +638,7 @@ guile_init(int *pargc, char ***pargv)
int argc, i;
char **argv;
- if (!guile_script)
+ if (!guile_argv)
return;
scm_init_guile();
@@ -678,7 +658,7 @@ guile_init(int *pargc, char ***pargv)
load_startup_file();
- guile_load(guile_script, !strchr(guile_script, '/'), guile_argv);
+ guile_load(guile_argv, !strchr(guile_argv[0], '/'));
/* Read command line arguments */
args = scm_program_arguments();
@@ -696,13 +676,8 @@ guile_init(int *pargc, char ***pargv)
*pargv = argv;
proc = SCM_VARIABLE_REF(sym_idest_main);
- if (proc == SCM_EOL) {
- if (guile_function) {
- proc = SCM_VARIABLE_REF(scm_c_lookup(guile_function));
- SCM_VARIABLE_SET(sym_idest_main, proc);
- } else
- error(1, 0, "idest-main not defined");
- }
+ if (proc == SCM_EOL)
+ error(1, 0, "idest-main not defined");
if (scm_procedure_p(proc) != SCM_BOOL_T)
error(1, 0,
@@ -716,7 +691,7 @@ guile_init(int *pargc, char ***pargv)
else
error(1, 0,
"script %s set non-boolean value of idest-readonly",
- guile_script);
+ guile_argv[0]);
guile_inited = 1;
}
#else
diff --git a/src/idest.h b/src/idest.h
index 83b1d9d..b23a02f 100644
--- a/src/idest.h
+++ b/src/idest.h
@@ -75,13 +75,10 @@ extern char *source_file;
extern struct id3_tag *source_tag;
extern enum backup_type backup_type;
extern char *backup_dir;
-extern gl_list_t ed_list;
extern unsigned convert_version;
extern unsigned version_option;
extern unsigned default_version_option;
extern int guile_debug;
-extern char *guile_script;
-extern char *guile_function;
extern char **guile_argv;
/* idop.c */
diff --git a/src/main.c b/src/main.c
index 8b07063..f32be10 100644
--- a/src/main.c
+++ b/src/main.c
@@ -24,12 +24,14 @@ enum backup_type backup_type = no_backups;
char *backup_dir;
int verbose_option = 0;
int describe_option = 0;
+int dry_run_option = 0;
int all_frames = 0;
char *source_file;
struct id3_tag *source_tag;
char *format_name;
+char *batch_name;
struct item_info {
@@ -164,6 +166,20 @@ const char *mode_option_str[] = {
"--list-frames"
};
+char **guile_argv_ptr;
+
+void
+set_guile_argv(int argc, char **av)
+{
+ int i;
+
+ guile_argv_ptr = xcalloc(argc + 4, sizeof(guile_argv_ptr[0]));
+ guile_argv = guile_argv_ptr + 3;
+ for (i = 0; i < argc; i++)
+ guile_argv[i] = av[i];
+ guile_argv[i] = NULL;
+}
+
#include "cmdline.h"
int
@@ -181,21 +197,30 @@ main(int argc, char **argv)
argv += optind;
if (format_name) {
- int i;
-
if (mode_set)
error(1, 0, "--format cannot be used with %s",
mode_option_str[mode]);
- if (guile_script)
+ if (guile_argv)
error(1, 0, "--format cannot be used with --script");
-
- guile_script = "format";
- guile_argv = xcalloc(argc + 2, sizeof(guile_argv[0]));
- guile_argv[0] = format_name;
- for (i = 0; i <= argc; i++)
- guile_argv[i + 1] = argv[i];
+ if (batch_name)
+ error(1, 0, "--format cannot be used with --batch");
+ set_guile_argv(argc, argv);
+ *--guile_argv = format_name;
+ *--guile_argv = "format";
+ } else if (batch_name) {
+ if (mode_set)
+ error(1, 0, "--batch cannot be used with %s",
+ mode_option_str[mode]);
+ if (guile_argv)
+ error(1, 0, "--batch cannot be used with --script");
+ set_guile_argv(argc, argv);
+ *--guile_argv = batch_name;
+ *--guile_argv = "batch";
}
+ if (dry_run_option)
+ *--guile_argv = "dry-run";
+
guile_init(&argc, &argv);
if (mode == MODE_LIST) {

Return to:

Send suggestions and report system problems to the System administrator.