summaryrefslogblamecommitdiffstats
path: root/lib/backup-files.c
blob: c6e0c6d94ae37a57ac5f13d1256ef04c2597809b (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15














                                                                   
 


                                                                            


  



                                                                       










                      







                                                          


           
                                                                                                



































                                                                         
                           
 
                                   
                                                      




                                 

                                

                         

 



                                 
 

































































                                                                        



                            




































                                                                

                                                           




                               

                                       
 




                                                                    

                                         
                 
 

                                                         
 


                                                       
                                         


                                                               
                 
 






                                                               



                      
/*
  File: backup-files.c

  Copyright (C) 2003 Andreas Gruenbacher <agruen@suse.de>
  SuSE Labs, SuSE Linux AG

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This program 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
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

/*
 * Create backup files of a list of files similar to GNU patch. A path
 * name prefix and suffix for the backup file can be specified with the
 * -B and -Z options.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

const char *progname;

enum { what_backup, what_restore, what_remove };

const char *opt_prefix="", *opt_suffix="", *opt_file=NULL;
int opt_silent=0, opt_what=what_backup;

#define LINE_LENGTH 1024


void
usage(void)
{
	printf("Usage: %s [-B prefix] [-z suffix] [-f {filelist|-}] [-s] [-r|-x] filename ...\n"
	       "\n"
	       "\tCreate hard linked backup copies of a list of files\n"
	       "\tread from standard input.\n"
	       "\n"
	       "\t-r\tRestore the backup\n"
	       "\t-x\tRemove backup files and empty parent directories\n"
	       "\t-B\tPath name prefix for backup files\n"
	       "\t-z\tPath name suffix for backup files\n"
	       "\t-s\tSilent operation; only print error messages\n\n",
	       progname);
}

void
create_parents(char *filename)
{
	struct stat st;
	char *f = strchr(filename, '/');

	if (f == NULL)
		return;
	*f = '\0';
	if (stat(f, &st) != 0) {
		while (f != NULL) {
			*f = '\0';
			mkdir(filename, 0777);
			*f = '/';
			f = strchr(f+1, '/');
		}
	} else {
		*f = '/';
	}
}

void
remove_parents(char *filename)
{
	char *f, *g = NULL;

	f = strrchr(filename, '/');
	while ((f = strrchr(filename, '/')) != NULL) {
		if (g != NULL)
			*g = '/';
		g = f;
		*f= '\0';
		
		rmdir(filename);
	}
	if (g != NULL)
		*g = '/';
}

int
process_file(char *file)
{
	char backup[LINE_LENGTH];

	if (strlen(opt_prefix) + strlen(file) +
	    strlen(opt_suffix) >= sizeof(backup)) {
		perror("Line buffer too small\n");
		return 1;
	}

	snprintf(backup, sizeof(backup), "%s%s%s",
		 opt_prefix, file, opt_suffix);

	if (opt_what == what_backup) {
		int fd;
		create_parents(backup);

		unlink(backup);
		if (link(file, backup) == 0) {
			if (!opt_silent)
				printf("Copying %s\n", file);
		} else if ((fd = creat(backup, 0666)) != -1) {
			close(fd);
			if (!opt_silent)
				printf("New file %s\n", file);
		} else {
			perror(backup);
			return 1;
		}
		return 0;
	} else if (opt_what == what_restore) {
		struct stat st;
		create_parents(file);

		if (stat(backup, &st) != 0) {
			perror(backup);
			return 1;
		}
		if (st.st_size == 0) {
			if (unlink(file) == 0 || errno == ENOENT) {
				if (!opt_silent)
					printf("Removing %s\n", file);
				unlink(backup);
				remove_parents(backup);
			} else {
				perror(file);
				return 1;
			}
		} else {
			unlink(file);
			if (link(backup, file) == 0) {
				if (!opt_silent)
					printf("Restoring %s\n", file);
				unlink(backup);
				remove_parents(backup);
			} else {
				fprintf(stderr, "Could not restore "
					      "file `%s' to `%s': %s\n",
					backup, file, strerror(errno));
				return 1;
			}
		}
		return 0;
	} else if (opt_what == what_remove) {
		unlink(backup);
		remove_parents(backup);
		return 0;
	} else
		return 1;
}

int
main(int argc, char *argv[])
{
	int opt, status=0;
	
	progname = argv[0];

	while ((opt = getopt(argc, argv, "rxB:z:f:sh")) != -1) {
		switch(opt) {
			case 'r':
				opt_what = what_restore;
				break;

			case 'x':
				opt_what = what_remove;
				break;

			case 'B':
				opt_prefix = optarg;
				break;
				
			case 'f':
				opt_file = optarg;
				break;

			case 'z':
				opt_suffix = optarg;
				break;

			case 's':
				opt_silent = 1;
				break;

			case 'h':
			default:
				usage();
				return 0;
		}
	}

	if ((*opt_prefix == '\0' && *opt_suffix == '\0') ||
	    (opt_file == NULL && optind == argc)) {
		usage();
		return 1;
	}

	if (opt_file != NULL) {
		FILE *file;
		char line[LINE_LENGTH];

		if (!strcmp(opt_file, "-")) {
			file = stdin;
		} else {
			if ((file = fopen(opt_file, "r")) == NULL) {
				perror(opt_file);
				return 1;
			}
		}

		while (fgets(line, sizeof(line), file)) {
			char *l = strchr(line, '\0');

			if (l > line && *(l-1) == '\n')
				*(l-1) = '\0';
			if (*line == '\0')
				continue;
				
			if ((status = process_file(line)) != 0)
				return status;
		}

		if (file != stdin) {
			fclose(file);
		}
	}
	for (; optind < argc; optind++) {
		if ((status = process_file(argv[optind])) != 0)
			return status;
	}

	return status;
}