/* Copyright (c) 1993,1994, Joseph Arceneaux. All rights reserved. * * This file is subject to the terms of the GNU General Public License as * published by the Free Software Foundation. A copy of this license is * included with this software distribution in the file COPYING. If you * do not have a copy, you may obtain a copy by writing to the Free * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * This software 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. */ /* GNU/Emacs style backups -- * This behaviour is controlled by two environment variables, * VERSION_CONTROL and SIMPLE_BACKUP_SUFFIX. * * VERSION_CONTROL determines what kinds of backups are made. If it's * value is "numbered", then the first modification of some file * "eraserhead.c" will yield a backup file "eraserhead.c.~1~", the * second modification will yield "eraserhead.c.~2~", and so on. It * does not matter if the version numbers are not a sequence; the next * version will be one greater than the highest in that directory. * * If the value of VERSION_CONTROL is "numbered_existing", then such * numbered backups will be made if there are already numbered backup * versions of the file. Otherwise, the backup name will be that of * the original file with "~" (tilde) appended. E.g., "eraserhead.c~". * * If the value of VERSION_CONTROL is "simple", then the backup name * will be that of the original file with "~" appended, regardless of * whether or not there exist numbered versions in the directory. * * For simple backups, the value of SIMPLE_BACKUP_SUFFIX will be used * rather than "~" if it is set. * * If VERSION_CONTROL is unset, "numbered_existing" is assumed. For * Emacs lovers, "nil" is equivalent to "numbered_existing" and "t" is * equivalent to "numbered". * * Finally, if VERSION_CONTROL is "none" or "never", backups are not * made. I suggest you avoid this behaviour. * * Added, october 1999 (by Chris F.A. Johnson): * * If VERSION_WIDTH is set, then it controls zero padding of a numbered * suffix. */ /* Written by jla, based on code from djm (see `patch') */ #include "sys.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef PRESERVE_MTIME #include #ifdef HAVE_UTIME_H #include #elif defined(HAVE_SYS_UTIME_H) #include #endif #endif #include #if defined (_WIN32) && !defined (__CYGWIN__) #include #else #include #endif #include #ifndef isascii #define ISDIGIT(c) (isdigit ((unsigned char) (c))) #else #define ISDIGIT(c) (isascii (c) && isdigit (c)) #endif #include #ifdef HAVE_DIRENT_H #include #define NAMLEN(dirent) strlen((dirent)->d_name) #else #define dirent direct #define NAMLEN(dirent) (dirent)->d_namlen #ifdef HAVE_SYS_NDIR_H #include #endif #ifdef HAVE_SYS_DIR_H #include #endif #ifdef HAVE_NDIR_H #include #endif #if !defined(HAVE_SYS_NDIR_H) && !defined(HAVE_SYS_DIR_H) && !defined(HAVE_NDIR_H) #define NODIR 1 #endif #endif #include "indent.h" #include "globs.h" #include "io.h" #include "backup.h" RCSTAG_CC ("$Id: backup.c,v 1.21 2002/08/04 17:08:41 david Exp $"); #ifndef NODIR #if defined (_POSIX_VERSION) /* Might be defined in unistd.h. */ /* POSIX does not require that the d_ino field be present, and some * systems do not provide it. */ #define REAL_DIR_ENTRY(dp) 1 #else #define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0) #endif #else /* NODIR */ #define generate_backup_filename(v,f) simple_backup_name((f)) #endif /* NODIR */ #ifndef BACKUP_SUFFIX_STR #define BACKUP_SUFFIX_STR "~" #endif #ifndef BACKUP_SUFFIX_CHAR #define BACKUP_SUFFIX_CHAR '~' #endif #ifndef BACKUP_SUFFIX_FORMAT #define BACKUP_SUFFIX_FORMAT "%s.~%0*d~" #endif /* Default backup file suffix to use */ static char * simple_backup_suffix = BACKUP_SUFFIX_STR; /* What kinds of backup files to make -- see * table `version_control_values' below. */ backup_mode_ty version_control = unknown; int version_width = 1; /* Construct a simple backup name for PATHNAME by appending the value of `simple_backup_suffix'. */ static char * simple_backup_name ( char *pathname) { char *backup_name; backup_name = xmalloc (strlen (pathname) + strlen (simple_backup_suffix) + 2); sprintf (backup_name, "%s%s", pathname, simple_backup_suffix); return backup_name; } #ifndef NODIR /* If DIRENTRY is a numbered backup version of file BASE, return * that number. BASE_LENGTH is the string length of BASE. */ static int version_number( char * base, char * direntry, int base_length) { int version; char * p = NULL; version = 0; if (!strncmp (base, direntry, base_length) && ISDIGIT (direntry[base_length + 2])) { for (p = &direntry[base_length + 2]; ISDIGIT (*p); ++p) { version = version * 10 + *p - '0'; } if (p[0] != BACKUP_SUFFIX_CHAR || p[1]) { version = 0; } } return version; } /* Return the highest version of file FILENAME in directory * DIRNAME. Return 0 if there are no numbered versions. */ static int highest_version ( char * filename, char * dirname) { DIR * dirp = NULL; struct dirent * dp = NULL; int highest_version; int this_version; int file_name_length; dirp = opendir (dirname); if (!dirp) { return 0; } highest_version = 0; file_name_length = strlen (filename); while ((dp = readdir (dirp)) != 0) { if (!REAL_DIR_ENTRY (dp) || NAMLEN (dp) <= file_name_length + 2) { continue; } this_version = version_number (filename, dp->d_name, file_name_length); if (this_version > highest_version) { highest_version = this_version; } } closedir (dirp); return highest_version; } /* Return the highest version number for file PATHNAME. If there are no backups, or only a simple backup, return 0. */ static int max_version ( char * pathname) { char * p; char * filename; int pathlen = strlen (pathname); int version; p = pathname + pathlen - 1; while ((p > pathname) && (*p != '/')) { p--; } if (*p == '/') { int dirlen = p - pathname; char *dirname; filename = p + 1; dirname = xmalloc (dirlen + 1); strncpy (dirname, pathname, (dirlen)); dirname[dirlen] = '\0'; version = highest_version (filename, dirname); free (dirname); return version; } filename = pathname; version = highest_version (filename, "."); return version; } /* Generate a backup filename for PATHNAME, dependent on the * value of VERSION_CONTROL. */ static char * generate_backup_filename ( backup_mode_ty version_control, char * pathname) { int last_numbered_version; char *backup_name; if (version_control == none) { return 0; } if (version_control == simple) { return simple_backup_name (pathname); } last_numbered_version = max_version (pathname); if ((version_control == numbered_existing) && (last_numbered_version == 0)) { return simple_backup_name (pathname); } last_numbered_version++; backup_name = xmalloc (strlen (pathname) + 16); if (!backup_name) { return 0; } sprintf (backup_name, BACKUP_SUFFIX_FORMAT, pathname, version_width, (int) last_numbered_version); return backup_name; } #endif /* !NODIR */ static version_control_values_ty values[] = { {none, "never"}, /* Don't make backups. */ {none, "none"}, /* Ditto */ {simple, "simple"}, /* Only simple backups */ {numbered_existing, "existing"}, /* Numbered if they already exist */ {numbered_existing, "nil"}, /* Ditto */ {numbered, "numbered"}, /* Numbered backups */ {numbered, "t"}, /* Ditto */ {unknown, 0} /* Initial, undefined value. */ }; /* Determine the value of `version_control' by looking in the environment variable "VERSION_CONTROL". Defaults to numbered_existing. */ backup_mode_ty version_control_value(void) { char * version = getenv("VERSION_CONTROL"); version_control_values_ty * v; backup_mode_ty ret = unknown; if ((version == NULL) || (*version == 0)) { ret = numbered_existing; } else { v = &values[0]; while (v->name) { if (strcmp(version, v->name) == 0) { ret = v->value; break; } else { v++; } } } return ret; } /* Initialize information used in determining backup filenames. */ void set_version_width (void) { char *v = getenv ("VERSION_WIDTH"); if (v && ISDIGIT (*v)) { version_width = atoi (v); } if (version_width > 16) { version_width = 16; } } void initialize_backups (void) { char *v = getenv ("SIMPLE_BACKUP_SUFFIX"); if (v && *v) { simple_backup_suffix = v; } #ifdef NODIR version_control = simple; #else /* !NODIR */ version_control = version_control_value (); if (version_control == unknown) { fprintf (stderr, _("indent: Strange version-control value\n")); fprintf (stderr, _("indent: Using numbered-existing\n")); version_control = numbered_existing; } #endif /* !NODIR */ set_version_width (); } /* Make a backup copy of FILE, taking into account version-control. See the description at the beginning of the file for details. */ void make_backup ( file_buffer_ty * file, const struct stat * file_stats) { FILE * bf; char * backup_filename; unsigned int size; if (version_control == none) { return; } backup_filename = generate_backup_filename (version_control, file->name); if (!backup_filename) { fprintf (stderr, _("indent: Can't make backup filename of %s\n"), file->name); exit (system_error); } bf = fopen (backup_filename, "w"); if (!bf) { fatal (_("Can't open backup file %s"), backup_filename); } size = fwrite (file->data, file->size, 1, bf); if (size != 1) { fatal (_("Can't write to backup file %s"), backup_filename); } fclose (bf); #ifdef PRESERVE_MTIME { struct utimbuf buf; buf.actime = time (NULL); buf.modtime = file_stats->st_mtime; if (utime (backup_filename, &buf) != 0) { WARNING (_("Can't preserve modification time on backup file %s"), backup_filename, 0); } } #endif free (backup_filename); }