--- vorbisgain-0.36/vorbisgain.h.bak 2005-02-06 19:40:19.000000000 +0200 +++ vorbisgain-0.36/vorbisgain.h 2007-07-05 00:18:47.178541639 +0300 @@ -37,6 +37,7 @@ typedef struct settings #endif int skip; /**< Skip non-vorbis files */ int show_progress; + unsigned int do_fsync:1; } SETTINGS; --- vorbisgain-0.36/vorbisgain.c.bak 2005-02-06 19:41:19.000000000 +0200 +++ vorbisgain-0.36/vorbisgain.c 2007-07-05 00:19:47.085332070 +0300 @@ -235,7 +235,7 @@ int convert_files(FILE_LIST* file_list, { if (write_gains(file->filename, file->track_peak, file->track_gain, album_peak, file->album_gain, - !settings->quiet, 0) < 0) + !settings->quiet, 0, settings->do_fsync) < 0) { return -1; } @@ -288,7 +288,7 @@ int clean_files(FILE_LIST* file_list, SE else { if (write_gains(file->filename, NO_PEAK, NO_GAIN, - NO_PEAK, NO_GAIN, !settings->quiet, 1) < 0) + NO_PEAK, NO_GAIN, !settings->quiet, 1, settings->do_fsync) < 0) { return -1; } @@ -425,7 +425,8 @@ int process_files(FILE_LIST* file_list, if (!settings->album) { if (!settings->display_only && write_gains(file->filename, - file->track_peak, file->track_gain, NO_PEAK, NO_GAIN, 0, 0) < 0) + file->track_peak, file->track_gain, NO_PEAK, NO_GAIN, + 0, 0, settings->do_fsync) < 0) { return -1; } @@ -466,7 +467,7 @@ int process_files(FILE_LIST* file_list, if (write_gains(file->filename, file->track_peak, file->track_gain, album_peak, album_gain, - !settings->quiet, 0) < 0) + !settings->quiet, 0, settings->do_fsync) < 0) { return -1; } @@ -494,6 +495,7 @@ static void print_help(void) fprintf(stderr, _(" -C, --convert Convert VorbisGain tags from old to new style\n")); fprintf(stderr, _(" -d, --display-only Display results only. No files are modified\n")); fprintf(stderr, _(" -f, --fast Don't recalculate tagged files\n")); + fprintf(stderr, _(" -F, --fsync Fsync every written file\n")); fprintf(stderr, _(" -h, --help Print this help text\n")); fprintf(stderr, _(" -n, --no-progress Don't show progress, just print results\n")); fprintf(stderr, _(" -q, --quiet Don't print any output (except errors)\n")); @@ -512,7 +514,7 @@ static void print_help(void) } -const static struct option long_options[] = +static const struct option long_options[] = { {"album", 0, NULL, 'a'}, {"album-gain", 1, NULL, 'g'}, @@ -520,6 +522,7 @@ const static struct option long_options[ {"convert", 0, NULL, 'C'}, {"display-only", 0, NULL, 'd'}, {"fast", 0, NULL, 'f'}, + {"fsync", 0, NULL, 'F'}, {"help", 0, NULL, 'h'}, {"no-progress", 0, NULL, 'n'}, {"quiet", 0, NULL, 'q'}, @@ -533,9 +536,9 @@ const static struct option long_options[ #ifdef ENABLE_RECURSIVE -#define ARG_STRING "acCdfg:hnqrst:v" +#define ARG_STRING "acCdfFg:hnqrst:v" #else -#define ARG_STRING "acCdfg:hnqst:v" +#define ARG_STRING "acCdfFg:hnqst:v" #endif @@ -619,6 +622,10 @@ int main(int argc, char** argv) settings.fast = 1; break; + case 'F': + settings.do_fsync = 1; + break; + case 'n': settings.show_progress = 0; break; --- vorbisgain-0.36/vorbisgain.1.bak 2005-02-06 19:40:19.000000000 +0200 +++ vorbisgain-0.36/vorbisgain.1 2007-07-25 08:37:11.336255795 +0300 @@ -83,6 +83,9 @@ needed (the album gain and peak tags are .I -a has been specified). +.IP "-F, --fsync" +Fsync temporary file before overwriting original input file. + .IP "-n, --no-progress" Only display results, but don't show progress in percentages and times. This can be useful if the output is piped into other programs. --- vorbisgain-0.36/vorbis.c.bak 2005-02-06 19:40:19.000000000 +0200 +++ vorbisgain-0.36/vorbis.c 2007-07-25 09:13:03.764887699 +0300 @@ -10,13 +10,18 @@ #include "config.h" #endif +#include +#include #include #include #include #include #include +#include +#include #include #include + #include "gain_analysis.h" #include "i18n.h" #include "misc.h" @@ -56,7 +61,7 @@ #define PROGRESS_FORMAT_SIZE 8 #define MIN_FILENAME_SIZE 5 #define MIN_MIDDLE_TRUNCATE_SIZE 20 -#define TEMP_NAME "vorbisgain.tmp" +#define TEMP_NAME ".vorbisgain.XXXXXX" /** @@ -557,10 +562,10 @@ int get_gain(const char* filename, float * message has been printed). */ int write_gains(const char *filename, float track_peak, float track_gain, - float album_peak, float album_gain, int verbose, int remove_tags) + float album_peak, float album_gain, int verbose, int remove_tags, int do_fsync) { struct stat stat_buf; - struct utimbuf utime_buf; + struct timeval tvvals[2]; vcedit_state* state = NULL; vorbis_comment* vc; FILE* infile = NULL; @@ -572,6 +577,7 @@ int write_gains(const char *filename, fl int result = -1; int delete_temp = 0; int i; + int fd; infile = fopen(filename, "rb"); @@ -688,7 +694,7 @@ int write_gains(const char *filename, fl * than necessary (and not always needed). Lets keep it simple though (at * the expense of a few bytes)... */ - temp_name = malloc(strlen(filename) + sizeof(TEMP_NAME)); + temp_name = malloc(strlen(filename) + sizeof(TEMP_NAME) + 1); if (temp_name == NULL) { @@ -697,10 +703,15 @@ int write_gains(const char *filename, fl } strcpy(temp_name, filename); - strcpy((char *) last_path(temp_name), TEMP_NAME); - - outfile = fopen(temp_name, "wb"); + strcat(temp_name, TEMP_NAME); + fd = mkstemp(temp_name); + if (fd == -1) + { + file_error(_("Couldn't open '%s' for output: "), temp_name); + goto exit; + } + outfile = fdopen(fd, "w+"); if (outfile == NULL) { file_error(_("Couldn't open '%s' for output: "), temp_name); @@ -726,53 +737,74 @@ int write_gains(const char *filename, fl vcedit_clear(state); state = NULL; + + if (fstat(fileno(infile), &stat_buf) != 0) + { + file_error(_("Couldn't get information about old file '%s': "), filename); + goto exit; + } fclose(infile); infile = NULL; - /* Only bother to check for close failure on the output file */ - i = fclose(outfile); - - if (i != 0) + if (ferror(outfile) || fflush(outfile) == EOF) { - file_error(_("Couldn't write replay gain tags for '%s': "), filename); + file_error(_("Couldn't flush buffers for '%s': "), filename); + fclose(outfile); outfile = NULL; delete_temp = 1; goto exit; } - outfile = NULL; + /* TRY to copy mode and modification time... */ + if (fchmod(fileno(outfile), stat_buf.st_mode) != 0) + { + file_error(_("Note: Couldn't set mode for file '%s': "), temp_name); + } - if (stat(filename, &stat_buf) != 0) + /* Compiles at least with glibc-2.6 */ + tvvals[0].tv_sec = stat_buf.st_atim.tv_sec; + tvvals[0].tv_usec = stat_buf.st_atim.tv_nsec / 1000; + tvvals[1].tv_sec = stat_buf.st_mtim.tv_sec; + tvvals[1].tv_usec = stat_buf.st_mtim.tv_nsec / 1000; + if (utimes(temp_name, tvvals) != 0) { - file_error(_("Couldn't get information about old file '%s': "), filename); - goto exit; + file_error(_("Note: Couldn't set time for file '%s': "), temp_name); } - if (remove(filename) != 0) + if (do_fsync && fsync(fileno(outfile)) == -1) { - file_error(_("Couldn't delete old file '%s': "), filename); + file_error(_("Couldn't fsync '%s': "), filename); + fclose(outfile); + outfile = NULL; + delete_temp = 1; goto exit; } - if (rename(temp_name, filename) != 0) + /* Only bother to check for close failure on the output file */ + i = fclose(outfile); + + if (i != 0) { - file_error(_("Couldn't rename '%s' to '%s': "), temp_name, filename); + file_error(_("Couldn't write replay gain tags for '%s': "), filename); + outfile = NULL; + delete_temp = 1; goto exit; } - /* TRY to copy mode and modification time... */ + outfile = NULL; - if (chmod(filename, stat_buf.st_mode) != 0) +#ifdef WIN32 + if (remove(filename) != 0) { - file_error(_("Note: Couldn't set mode for file '%s': "), filename); + file_error(_("Couldn't delete old file '%s': "), filename); + goto exit; } +#endif - utime_buf.actime = stat_buf.st_atime; - utime_buf.modtime = stat_buf.st_mtime; - - if (utime(filename, &utime_buf) != 0) + if (rename(temp_name, filename) != 0) { - file_error(_("Note: Couldn't set time for file '%s': "), filename); + file_error(_("Couldn't rename '%s' to '%s': "), temp_name, filename); + goto exit; } result = 0; @@ -798,17 +830,17 @@ exit: fclose(infile); } - if (infile != NULL) + if (outfile != NULL) { - fclose(infile); + fclose(outfile); } if (delete_temp) { - if (remove(TEMP_NAME) != 0) + if (remove(temp_name) != 0) { file_error(_("Note: Couldn't remove temporary file '%s': "), - TEMP_NAME); + temp_name); } }