diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-07-25 16:21:55 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-07-25 16:21:55 +0300 |
commit | 39e1ad85f7fb63156621112a28a876265d9fa1f0 (patch) | |
tree | 3607cb92f570680bb6c2016018e373834edd8b3d | |
parent | c6230a46e93e9fb1525d5a036074aa7b5ff8a76e (diff) | |
download | idest-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.
-rw-r--r-- | NEWS | 74 | ||||
-rw-r--r-- | doc/idest.texi | 4 | ||||
-rw-r--r-- | scheme/Makefile.am | 4 | ||||
-rw-r--r-- | scheme/batch.scm | 46 | ||||
-rw-r--r-- | scheme/idest/batch/help.scm | 25 | ||||
-rw-r--r-- | scheme/idest/format/help.scm | 82 | ||||
-rw-r--r-- | scheme/idest/format/shortlist.scm | 2 | ||||
-rw-r--r-- | scheme/idest/list-modules.scm | 100 | ||||
-rw-r--r-- | src/cmdline.opt | 17 | ||||
-rw-r--r-- | src/guile.c | 55 | ||||
-rw-r--r-- | src/idest.h | 3 | ||||
-rw-r--r-- | src/main.c | 43 |
12 files changed, 297 insertions, 158 deletions
@@ -1,2 +1,2 @@ -IdEst -- history of user-visible changes. 2011-07-17 +IdEst -- history of user-visible changes. 2011-07-25 Copyright (C) 2009-2011 Sergey Poznyakoff @@ -66,2 +66,47 @@ 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 @@ -110,3 +155,3 @@ The full syntax is: - idest --query Title%TIT2,Artist%TPE1,Author%TOLY,Composer%TCOM *.mp3 + idest --query=Title%TIT2,Artist%TPE1,Author%TOLY,Composer%TCOM *.mp3 @@ -148,21 +193,18 @@ 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). diff --git a/doc/idest.texi b/doc/idest.texi index bd6bad9..fa73ad4 100644 --- a/doc/idest.texi +++ b/doc/idest.texi @@ -1148,6 +1148,2 @@ Print verbose frame descriptions instead of short names. -@item -f -@itemx --function=@var{name} -Guile function to call. @xref{Scripting}. - @item -F @var{flist} diff --git a/scheme/Makefile.am b/scheme/Makefile.am index a71eb52..b8eefe7 100644 --- a/scheme/Makefile.am +++ b/scheme/Makefile.am @@ -17,2 +17,3 @@ EXTRA_DIST=\ + batch.scm\ dry-run.scm\ @@ -23,2 +24,3 @@ sitedir = @GUILE_SITE@/$(PACKAGE) site_DATA=\ + batch.scm\ dry-run.scm\ @@ -27,3 +29,3 @@ site_DATA=\ 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 - diff --git a/scheme/batch.scm b/scheme/batch.scm new file mode 100644 index 0000000..5fa368c --- /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 --- /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 @@ -18,84 +18,8 @@ -(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 @@ -10,3 +10,3 @@ (define-public description - "display title, artist name and year, on a signle line") + "display title, artist name and year, on a single line") diff --git a/scheme/idest/list-modules.scm b/scheme/idest/list-modules.scm new file mode 100644 index 0000000..bd538ea --- /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 @@ -186,4 +186,4 @@ OPTION(script,S,FILE, 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 */ @@ -198,6 +198,13 @@ 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 diff --git a/src/guile.c b/src/guile.c index b75b674..2de2fe2 100644 --- a/src/guile.c +++ b/src/guile.c @@ -24,4 +24,2 @@ int guile_inited = 0; int guile_debug = 1; -char *guile_script; -char *guile_function; char **guile_argv; @@ -83,8 +81,2 @@ guile_safe_exec(SCM (*handler)(void *data), void *data, SCM *result) -struct load_closure { - char *filename; - int argc; - char **argv; -}; - static SCM @@ -92,6 +84,6 @@ 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; @@ -102,6 +94,6 @@ 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; @@ -110,18 +102,6 @@ load_handler_path(void *data) 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); @@ -641,3 +621,3 @@ load_startup_file() 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", @@ -660,3 +640,3 @@ guile_init(int *pargc, char ***pargv) - if (!guile_script) + if (!guile_argv) return; @@ -680,3 +660,3 @@ guile_init(int *pargc, char ***pargv) - guile_load(guile_script, !strchr(guile_script, '/'), guile_argv); + guile_load(guile_argv, !strchr(guile_argv[0], '/')); @@ -698,9 +678,4 @@ guile_init(int *pargc, char ***pargv) 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"); @@ -718,3 +693,3 @@ guile_init(int *pargc, char ***pargv) "script %s set non-boolean value of idest-readonly", - guile_script); + guile_argv[0]); guile_inited = 1; diff --git a/src/idest.h b/src/idest.h index 83b1d9d..b23a02f 100644 --- a/src/idest.h +++ b/src/idest.h @@ -77,3 +77,2 @@ extern enum backup_type backup_type; extern char *backup_dir; -extern gl_list_t ed_list; extern unsigned convert_version; @@ -82,4 +81,2 @@ extern unsigned default_version_option; extern int guile_debug; -extern char *guile_script; -extern char *guile_function; extern char **guile_argv; @@ -26,2 +26,3 @@ int verbose_option = 0; int describe_option = 0; +int dry_run_option = 0; @@ -32,2 +33,3 @@ struct id3_tag *source_tag; char *format_name; +char *batch_name; @@ -166,2 +168,16 @@ const char *mode_option_str[] = { +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" @@ -183,4 +199,2 @@ main(int argc, char **argv) if (format_name) { - int i; - if (mode_set) @@ -188,12 +202,23 @@ main(int argc, char **argv) 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); |