1/* backupfile.c -- make Emacs style backup file names 2 Copyright (C) 1990-1997, 1998, 1999 Free Software Foundation, Inc. 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 2, or (at your option) 7 any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program; see the file COPYING. 16 If not, write to the Free Software Foundation, 17 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 18 19/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. 20 Some algorithms adapted from GNU Emacs. */ 21 22#if HAVE_CONFIG_H 23# include <config.h> 24#endif 25 26#include <argmatch.h> 27#include <backupfile.h> 28 29#include <stdio.h> 30#include <sys/types.h> 31#if HAVE_STRING_H 32# include <string.h> 33#else 34# include <strings.h> 35#endif 36 37#if HAVE_DIRENT_H 38# include <dirent.h> 39# define NLENGTH(direct) strlen ((direct)->d_name) 40#else 41# define dirent direct 42# define NLENGTH(direct) ((size_t) (direct)->d_namlen) 43# if HAVE_SYS_NDIR_H 44# include <sys/ndir.h> 45# endif 46# if HAVE_SYS_DIR_H 47# include <sys/dir.h> 48# endif 49# if HAVE_NDIR_H 50# include <ndir.h> 51# endif 52#endif 53 54#if CLOSEDIR_VOID 55/* Fake a return value. */ 56# define CLOSEDIR(d) (closedir (d), 0) 57#else 58# define CLOSEDIR(d) closedir (d) 59#endif 60 61#if STDC_HEADERS 62# include <stdlib.h> 63#else 64char *malloc (); 65#endif 66 67#ifndef HAVE_DECL_GETENV 68char *getenv (); 69#endif 70 71char *base_name PARAMS ((char const *)); 72 73#if HAVE_DIRENT_H || HAVE_NDIR_H || HAVE_SYS_DIR_H || HAVE_SYS_NDIR_H 74# define HAVE_DIR 1 75#else 76# define HAVE_DIR 0 77#endif 78 79#if HAVE_LIMITS_H 80# include <limits.h> 81#endif 82#ifndef CHAR_BIT 83# define CHAR_BIT 8 84#endif 85/* Upper bound on the string length of an integer converted to string. 86 302 / 1000 is ceil (log10 (2.0)). Subtract 1 for the sign bit; 87 add 1 for integer division truncation; add 1 more for a minus sign. */ 88#define INT_STRLEN_BOUND(t) ((sizeof (t) * CHAR_BIT - 1) * 302 / 1000 + 2) 89 90/* ISDIGIT differs from isdigit, as follows: 91 - Its arg may be any int or unsigned int; it need not be an unsigned char. 92 - It's guaranteed to evaluate its argument exactly once. 93 - It's typically faster. 94 Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that 95 only '0' through '9' are digits. Prefer ISDIGIT to isdigit unless 96 it's important to use the locale's definition of `digit' even when the 97 host does not conform to Posix. */ 98#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) 99 100#if D_INO_IN_DIRENT 101# define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0) 102#else 103# define REAL_DIR_ENTRY(dp) 1 104#endif 105 106/* The extension added to file names to produce a simple (as opposed 107 to numbered) backup file name. */ 108const char *simple_backup_suffix = "~"; 109 110static int max_backup_version PARAMS ((const char *, const char *)); 111static int version_number PARAMS ((const char *, const char *, size_t)); 112 113/* Return the name of the new backup file for file FILE, 114 allocated with malloc. Return 0 if out of memory. 115 FILE must not end with a '/' unless it is the root directory. 116 Do not call this function if backup_type == none. */ 117 118char * 119find_backup_file_name (const char *file, enum backup_type backup_type) 120{ 121 size_t backup_suffix_size_max; 122 size_t file_len = strlen (file); 123 size_t numbered_suffix_size_max = INT_STRLEN_BOUND (int) + 4; 124 char *s; 125 const char *suffix = simple_backup_suffix; 126 127 /* Allow room for simple or `.~N~' backups. */ 128 backup_suffix_size_max = strlen (simple_backup_suffix) + 1; 129 if (HAVE_DIR && backup_suffix_size_max < numbered_suffix_size_max) 130 backup_suffix_size_max = numbered_suffix_size_max; 131 132 s = malloc (file_len + backup_suffix_size_max + numbered_suffix_size_max); 133 if (s) 134 { 135 strcpy (s, file); 136 137#if HAVE_DIR 138 if (backup_type != simple) 139 { 140 int highest_backup; 141 size_t dir_len = base_name (s) - s; 142 143 strcpy (s + dir_len, "."); 144 highest_backup = max_backup_version (file + dir_len, s); 145 if (! (backup_type == numbered_existing && highest_backup == 0)) 146 { 147 char *numbered_suffix = s + (file_len + backup_suffix_size_max); 148 sprintf (numbered_suffix, ".~%d~", highest_backup + 1); 149 suffix = numbered_suffix; 150 } 151 strcpy (s, file); 152 } 153#endif /* HAVE_DIR */ 154 155 addext (s, suffix, '~'); 156 } 157 return s; 158} 159 160#if HAVE_DIR 161 162/* Return the number of the highest-numbered backup file for file 163 FILE in directory DIR. If there are no numbered backups 164 of FILE in DIR, or an error occurs reading DIR, return 0. 165 */ 166 167static int 168max_backup_version (const char *file, const char *dir) 169{ 170 DIR *dirp; 171 struct dirent *dp; 172 int highest_version; 173 int this_version; 174 size_t file_name_length; 175 176 dirp = opendir (dir); 177 if (!dirp) 178 return 0; 179 180 highest_version = 0; 181 file_name_length = strlen (file); 182 183 while ((dp = readdir (dirp)) != 0) 184 { 185 if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) < file_name_length + 4) 186 continue; 187 188 this_version = version_number (file, dp->d_name, file_name_length); 189 if (this_version > highest_version) 190 highest_version = this_version; 191 } 192 if (CLOSEDIR (dirp)) 193 return 0; 194 return highest_version; 195} 196 197/* If BACKUP is a numbered backup of BASE, return its version number; 198 otherwise return 0. BASE_LENGTH is the length of BASE. 199 */ 200 201static int 202version_number (const char *base, const char *backup, size_t base_length) 203{ 204 int version; 205 const char *p; 206 207 version = 0; 208 if (strncmp (base, backup, base_length) == 0 209 && backup[base_length] == '.' 210 && backup[base_length + 1] == '~') 211 { 212 for (p = &backup[base_length + 2]; ISDIGIT (*p); ++p) 213 version = version * 10 + *p - '0'; 214 if (p[0] != '~' || p[1]) 215 version = 0; 216 } 217 return version; 218} 219#endif /* HAVE_DIR */ 220 221static const char * const backup_args[] = 222{ 223 /* In a series of synonyms, present the most meaning full first, so 224 that argmatch_valid be more readable. */ 225 "none", "off", 226 "simple", "never", 227 "existing", "nil", 228 "numbered", "t", 229 0 230}; 231 232static const enum backup_type backup_types[] = 233{ 234 none, none, 235 simple, simple, 236 numbered_existing, numbered_existing, 237 numbered, numbered 238}; 239 240/* Return the type of backup specified by VERSION. 241 If VERSION is NULL or the empty string, return numbered_existing. 242 If VERSION is invalid or ambiguous, fail with a diagnostic appropriate 243 for the specified CONTEXT. Unambiguous abbreviations are accepted. */ 244 245enum backup_type 246get_version (const char *context, const char *version) 247{ 248 if (version == 0 || *version == 0) 249 return numbered_existing; 250 else 251 return XARGMATCH (context, version, backup_args, backup_types); 252} 253 254 255/* Return the type of backup specified by VERSION. 256 If VERSION is NULL, use the value of the envvar VERSION_CONTROL. 257 If the specified string is invalid or ambiguous, fail with a diagnostic 258 appropriate for the specified CONTEXT. 259 Unambiguous abbreviations are accepted. */ 260 261enum backup_type 262xget_version (const char *context, const char *version) 263{ 264 if (version && *version) 265 return get_version (context, version); 266 else 267 return get_version ("$VERSION_CONTROL", getenv ("VERSION_CONTROL")); 268} 269