diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-02-08 19:23:42 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-02-08 19:23:42 +0200 |
commit | 90ea6a2cf2e646441b2c2ea4bf480fe8e4152de7 (patch) | |
tree | 612823f0f3983e47cde17b549a8d85d5913cb0b3 | |
parent | 0c6bb13d6b5df94417f2878cd5a4aa4ddcd78a39 (diff) | |
download | idest-90ea6a2cf2e646441b2c2ea4bf480fe8e4152de7.tar.gz idest-90ea6a2cf2e646441b2c2ea4bf480fe8e4152de7.tar.bz2 |
Preparation for implementing set/delete operations.
* src/backup.c: New file.
* gnulib.modules: Request backupfile, dirname and save-cwd.
* src/Makefile.am (idest_SOURCES): Add backup.c
* src/cmdline.opt: New options --backup and --backup-directory.
* src/id3v2.c: Minor change.
* src/idest.h: Include backupfile.h
(backup_type, backup_dir): New externs.
* src/main.c: Minor change.
-rw-r--r-- | gnulib.modules | 3 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/backup.c | 220 | ||||
-rw-r--r-- | src/cmdline.opt | 13 | ||||
-rw-r--r-- | src/id3v2.c | 8 | ||||
-rw-r--r-- | src/idest.h | 6 | ||||
-rw-r--r-- | src/main.c | 12 |
7 files changed, 257 insertions, 6 deletions
diff --git a/gnulib.modules b/gnulib.modules index 3487fe9..4296821 100644 --- a/gnulib.modules +++ b/gnulib.modules @@ -2,9 +2,12 @@ # A module name per line. Empty lines and comments are ignored. argmatch +backupfile +dirname getopt gitlog-to-changelog error linked-list progname +save-cwd xalloc diff --git a/src/Makefile.am b/src/Makefile.am index 6f8066a..212e454 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,6 +16,7 @@ bin_PROGRAMS=idest idest_SOURCES=\ + backup.c\ idest.h\ id3v1.c\ id3v1.h\ diff --git a/src/backup.c b/src/backup.c new file mode 100644 index 0000000..495d6ac --- /dev/null +++ b/src/backup.c @@ -0,0 +1,220 @@ +/* This file is part of Idest. + Copyright (C) 2009 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/>. */ + +#include "idest.h" +#include <sys/stat.h> +#include <fcntl.h> +#include "save-cwd.h" +#include "dirname.h" +#include "backupfile.h" + +#define MKDIR_PERMISSIONS 0777 +#define CREAT_PERMISSIONS 0666 + +/* Concatenate BASE directory name, a single directory separator (/) and + NAME. Return in PBASELEN the length of BASE, not counting eventual trailing + slash. */ +char * +concat_dir(const char *base, const char *name, size_t *pbaselen) +{ + size_t len = strlen(base); + size_t size; + char *dir; + + while (len > 0 && base[len - 1] == '/') + len--; + + size = len + 1 + strlen(name); + dir = xmalloc(size + 1); + memcpy(dir, base, len); + dir[len++] = '/'; + strcpy(dir + len, name); + + if (pbaselen) + *pbaselen = len; + return dir; +} + +/* Create the directory DIR, eventually creating all intermediate directories + starting from DIR + BASELEN. */ +int +create_hierarchy(char *dir, size_t baselen) +{ + int rc; + struct stat st; + char *p; + + if (stat(dir, &st) == 0) { + if (!S_ISDIR(st.st_mode)) { + error(0, 0, "component %s is not a directory", dir); + return 1; + } + return 0; + } else if (errno != ENOENT) { + error(0, errno, "cannot stat file %s", dir); + return 1; + } + + p = strrchr(dir, '/'); + if (p) { + if (p - dir + 1 < baselen) { + error(0, 0, "base directory %s does not exist", dir); + return 1; + } + *p = 0; + } + + rc = create_hierarchy(dir, baselen); + if (rc == 0) { + if (p) + *p = '/'; + if (mkdir(dir, MKDIR_PERMISSIONS)) { + error(0, errno, "cannot create directory %s", dir); + rc = 1; + } + } + return rc; +} + +/* Create a directory BASE/NAME (with eventual intermediate directories in + NAME). */ +char * +create_directory(const char *base, const char *name) +{ + size_t baselen; + char *dir = concat_dir(base, name, &baselen); + + if (create_hierarchy(dir, baselen)) { + free(dir); + dir = NULL; + } + return dir; +} + + +/* Copy FILE to DST_FILE. */ +int +copy_file(const char *file, const char *dst_file) +{ + int in_fd, out_fd; + struct stat st; + char *buf = NULL; + size_t bufsize; + size_t fsize; + int rc; + + in_fd = open(file, O_RDONLY); + + if (in_fd == -1) { + error(0, errno, + "cannot open source file %s for reading", file); + return 1; + } + + if (fstat(in_fd, &st)) { + error(0, errno, "cannot stat source file %s", file); + close(in_fd); + return 1; + } + + out_fd = creat(dst_file, CREAT_PERMISSIONS); + if (out_fd == -1) { + error(0, errno, "cannot create destination file %s", file); + close(in_fd); + return 1; + } + + for (bufsize = fsize; + bufsize > 0 && (buf = malloc(bufsize)) == NULL; + bufsize /= 2); + if (bufsize == 0) + xalloc_die(); + + rc = 0; + fsize = st.st_size; + while (fsize > 0) { + size_t rest; + size_t rdbytes; + + rest = fsize > bufsize ? bufsize : fsize; + rdbytes = read(in_fd, buf, rest); + if (rdbytes == -1) { + error(0, errno, "unexpected error reading %s", file); + rc = 1; + break; + } + rest = write(out_fd, buf, rdbytes); + if (rest == -1) { + error(0, errno, "unexpected error writing to %s", + dst_file); + rc = 1; + break; + } else if (rest != rdbytes) { + error(0, 0, "short write on %s", dst_file); + rc = 1; + } + fsize -= rdbytes; + } + free(buf); + + close(in_fd); + close(out_fd); + if (rc) { + unlink(dst_file); + return 1; + } + return 0; +} + +/* Backup a file */ +int +backup_file(const char *file) +{ + int rc = 0; + char *adir; + char *new_name; + + if (backup_dir) { + if (file[0] == '/') + new_name = concat_dir(backup_dir, + last_component(file), NULL); + else + new_name = concat_dir(backup_dir, file, NULL); + if (access(new_name, F_OK) == 0) { + if (backup_type == no_backups) { + if (unlink(new_name)) + error(0, errno, + "cannot unlink backup file %s", + new_name); + /* Try to continue anyway */ + } else { + char *bfn = find_backup_file_name(new_name, + backup_type); + copy_file(new_name, bfn); + /* Continue no matter what the result of + copy_file was */ + } + } + } else if (backup_type == no_backups) + return 0; + else + new_name = find_backup_file_name(file, backup_type); + + rc = copy_file(file, new_name); + free(new_name); + return rc; +} + diff --git a/src/cmdline.opt b/src/cmdline.opt index 2df4cfd..8037afb 100644 --- a/src/cmdline.opt +++ b/src/cmdline.opt @@ -53,7 +53,6 @@ BEGIN ed_list_add_assignment(optarg, p); END - OPTION(id-version,V,VERSION, [<set ID3 version>]) BEGIN @@ -68,6 +67,18 @@ BEGIN latin1_output = 1; END +OPTION(backup,,CONTROL, + [<backup before removal, choose version CONTROL>]) +BEGIN + backup_type = xget_version ("--backup", optarg); +END + +OPTION(backup-directory,,DIR, + [<backup to given DIR>]) +BEGIN + backup_dir = optarg; +END + OPTIONS_END void diff --git a/src/id3v2.c b/src/id3v2.c index b8faa19..5ca9591 100644 --- a/src/id3v2.c +++ b/src/id3v2.c @@ -19,6 +19,14 @@ void set_id3v2(const char *name) { + struct id3_file *file; + + file = id3_file_open(name, ID3_FILE_MODE_READWRITE); + if (!file) + error(1, errno, "cannot open file %s", name); + /*FIXME...*/ + id3_file_close(file); + error(1, 0, "setting ID3 v2 frames is not yet implemented"); } diff --git a/src/idest.h b/src/idest.h index 9dc16d8..50effa8 100644 --- a/src/idest.h +++ b/src/idest.h @@ -24,6 +24,7 @@ #include <xalloc.h> #include <gl_linked_list.h> #include <argmatch.h> +#include <backupfile.h> #include <id3tag.h> #define _(s) s @@ -34,6 +35,8 @@ #define DEFAULT_ED_LIST "title,album,comment,artist,year,genre" extern int latin1_output; +extern enum backup_type backup_type; +extern char *backup_dir; void set_id3v1(const char *name, const struct id3v1_block *); void query_id3v1(const char *name); @@ -57,3 +60,6 @@ void ed_list_add_item(const char *id, gl_list_t list); void ed_list_print(void); void ed_list_add_assignment(const char *name, const char *value); + +/* backup.c */ +int backup_file(const char *file); @@ -18,13 +18,15 @@ unsigned version_option = 2; int latin1_output = 0; +enum backup_type backup_type; +char *backup_dir; struct ed_item { - char *name; - char id[5]; + char *name; /* Printable name */ + char id[5]; /* Item ID */ union { - gl_list_t vlist; - char *value; + gl_list_t vlist; /* List of strings, used with --query */ + char *value; /* New value, used with --set */ } v; }; @@ -336,7 +338,7 @@ set_id3(const char *name) set_id3v1(name, &v1_block); } else - error(1, 0, "setting ID3 v2 frames is not yet implemented"); + set_id3v2(name); } void |