1/* backupfile.c -- make Emacs style backup file names 2 3 Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 4 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software 5 Foundation, Inc. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2, or (at your option) 10 any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; see the file COPYING. 19 If not, write to the Free Software Foundation, 20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 21 22/* Written by Paul Eggert and David MacKenzie. 23 Some algorithms adapted from GNU Emacs. */ 24 25#include <config.h> 26 27#include "backupfile.h" 28 29#include "argmatch.h" 30#include "dirname.h" 31#include "xalloc.h" 32 33#include <errno.h> 34#include <stdbool.h> 35#include <stdlib.h> 36#include <string.h> 37 38#include <limits.h> 39 40#include <unistd.h> 41 42#include <dirent.h> 43#ifndef _D_EXACT_NAMLEN 44# define _D_EXACT_NAMLEN(dp) strlen ((dp)->d_name) 45#endif 46#if D_INO_IN_DIRENT 47# define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0) 48#else 49# define REAL_DIR_ENTRY(dp) 1 50#endif 51 52#if ! (HAVE_PATHCONF && defined _PC_NAME_MAX) 53# define pathconf(file, option) (errno = -1) 54#endif 55 56#ifndef _POSIX_NAME_MAX 57# define _POSIX_NAME_MAX 14 58#endif 59#ifndef SIZE_MAX 60# define SIZE_MAX ((size_t) -1) 61#endif 62 63#if defined _XOPEN_NAME_MAX 64# define NAME_MAX_MINIMUM _XOPEN_NAME_MAX 65#else 66# define NAME_MAX_MINIMUM _POSIX_NAME_MAX 67#endif 68 69#ifndef HAVE_DOS_FILE_NAMES 70# define HAVE_DOS_FILE_NAMES 0 71#endif 72#ifndef HAVE_LONG_FILE_NAMES 73# define HAVE_LONG_FILE_NAMES 0 74#endif 75 76/* ISDIGIT differs from isdigit, as follows: 77 - Its arg may be any int or unsigned int; it need not be an unsigned char 78 or EOF. 79 - It's typically faster. 80 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to 81 ISDIGIT unless it's important to use the locale's definition 82 of `digit' even when the host does not conform to POSIX. */ 83#define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9) 84 85/* The results of opendir() in this file are not used with dirfd and fchdir, 86 therefore save some unnecessary work in fchdir.c. */ 87#undef opendir 88#undef closedir 89 90/* The extension added to file names to produce a simple (as opposed 91 to numbered) backup file name. */ 92char const *simple_backup_suffix = "~"; 93 94 95/* If FILE (which was of length FILELEN before an extension was 96 appended to it) is too long, replace the extension with the single 97 char E. If the result is still too long, remove the char just 98 before E. */ 99 100static void 101check_extension (char *file, size_t filelen, char e) 102{ 103 char *base = last_component (file); 104 size_t baselen = base_len (base); 105 size_t baselen_max = HAVE_LONG_FILE_NAMES ? 255 : NAME_MAX_MINIMUM; 106 107 if (HAVE_DOS_FILE_NAMES || NAME_MAX_MINIMUM < baselen) 108 { 109 /* The new base name is long enough to require a pathconf check. */ 110 long name_max; 111 112 /* Temporarily modify the buffer into its parent directory name, 113 invoke pathconf on the directory, and then restore the buffer. */ 114 char tmp[sizeof "."]; 115 memcpy (tmp, base, sizeof "."); 116 strcpy (base, "."); 117 errno = 0; 118 name_max = pathconf (file, _PC_NAME_MAX); 119 if (0 <= name_max || errno == 0) 120 { 121 long size = baselen_max = name_max; 122 if (name_max != size) 123 baselen_max = SIZE_MAX; 124 } 125 memcpy (base, tmp, sizeof "."); 126 } 127 128 if (HAVE_DOS_FILE_NAMES && baselen_max <= 12) 129 { 130 /* Live within DOS's 8.3 limit. */ 131 char *dot = strchr (base, '.'); 132 if (!dot) 133 baselen_max = 8; 134 else 135 { 136 char const *second_dot = strchr (dot + 1, '.'); 137 baselen_max = (second_dot 138 ? second_dot - base 139 : dot + 1 - base + 3); 140 } 141 } 142 143 if (baselen_max < baselen) 144 { 145 baselen = file + filelen - base; 146 if (baselen_max <= baselen) 147 baselen = baselen_max - 1; 148 base[baselen] = e; 149 base[baselen + 1] = '\0'; 150 } 151} 152 153/* Returned values for NUMBERED_BACKUP. */ 154 155enum numbered_backup_result 156 { 157 /* The new backup name is the same length as an existing backup 158 name, so it's valid for that directory. */ 159 BACKUP_IS_SAME_LENGTH, 160 161 /* Some backup names already exist, but the returned name is longer 162 than any of them, and its length should be checked. */ 163 BACKUP_IS_LONGER, 164 165 /* There are no existing backup names. The new name's length 166 should be checked. */ 167 BACKUP_IS_NEW 168 }; 169 170/* *BUFFER contains a file name. Store into *BUFFER the next backup 171 name for the named file, with a version number greater than all the 172 existing numbered backups. Reallocate *BUFFER as necessary; its 173 initial allocated size is BUFFER_SIZE, which must be at least 4 174 bytes longer than the file name to make room for the initially 175 appended ".~1". FILELEN is the length of the original file name. 176 The returned value indicates what kind of backup was found. If an 177 I/O or other read error occurs, use the highest backup number that 178 was found. */ 179 180static enum numbered_backup_result 181numbered_backup (char **buffer, size_t buffer_size, size_t filelen) 182{ 183 enum numbered_backup_result result = BACKUP_IS_NEW; 184 DIR *dirp; 185 struct dirent *dp; 186 char *buf = *buffer; 187 size_t versionlenmax = 1; 188 char *base = last_component (buf); 189 size_t base_offset = base - buf; 190 size_t baselen = base_len (base); 191 192 /* Temporarily modify the buffer into its parent directory name, 193 open the directory, and then restore the buffer. */ 194 char tmp[sizeof "."]; 195 memcpy (tmp, base, sizeof "."); 196 strcpy (base, "."); 197 dirp = opendir (buf); 198 memcpy (base, tmp, sizeof "."); 199 strcpy (base + baselen, ".~1~"); 200 201 if (!dirp) 202 return result; 203 204 while ((dp = readdir (dirp)) != NULL) 205 { 206 char const *p; 207 char *q; 208 bool all_9s; 209 size_t versionlen; 210 size_t new_buflen; 211 212 if (! REAL_DIR_ENTRY (dp) || _D_EXACT_NAMLEN (dp) < baselen + 4) 213 continue; 214 215 if (memcmp (buf + base_offset, dp->d_name, baselen + 2) != 0) 216 continue; 217 218 p = dp->d_name + baselen + 2; 219 220 /* Check whether this file has a version number and if so, 221 whether it is larger. Use string operations rather than 222 integer arithmetic, to avoid problems with integer overflow. */ 223 224 if (! ('1' <= *p && *p <= '9')) 225 continue; 226 all_9s = (*p == '9'); 227 for (versionlen = 1; ISDIGIT (p[versionlen]); versionlen++) 228 all_9s &= (p[versionlen] == '9'); 229 230 if (! (p[versionlen] == '~' && !p[versionlen + 1] 231 && (versionlenmax < versionlen 232 || (versionlenmax == versionlen 233 && memcmp (buf + filelen + 2, p, versionlen) <= 0)))) 234 continue; 235 236 /* This directory has the largest version number seen so far. 237 Append this highest numbered extension to the file name, 238 prepending '0' to the number if it is all 9s. */ 239 240 versionlenmax = all_9s + versionlen; 241 result = (all_9s ? BACKUP_IS_LONGER : BACKUP_IS_SAME_LENGTH); 242 new_buflen = filelen + 2 + versionlenmax + 1; 243 if (buffer_size <= new_buflen) 244 { 245 buf = xnrealloc (buf, 2, new_buflen); 246 buffer_size = new_buflen * 2; 247 } 248 q = buf + filelen; 249 *q++ = '.'; 250 *q++ = '~'; 251 *q = '0'; 252 q += all_9s; 253 memcpy (q, p, versionlen + 2); 254 255 /* Add 1 to the version number. */ 256 257 q += versionlen; 258 while (*--q == '9') 259 *q = '0'; 260 ++*q; 261 } 262 263 closedir (dirp); 264 *buffer = buf; 265 return result; 266} 267 268/* Return the name of the new backup file for the existing file FILE, 269 allocated with malloc. Report an error and fail if out of memory. 270 Do not call this function if backup_type == no_backups. */ 271 272char * 273find_backup_file_name (char const *file, enum backup_type backup_type) 274{ 275 size_t filelen = strlen (file); 276 char *s; 277 size_t ssize; 278 bool simple = true; 279 280 /* Allow room for simple or ".~N~" backups. The guess must be at 281 least sizeof ".~1~", but otherwise will be adjusted as needed. */ 282 size_t simple_backup_suffix_size = strlen (simple_backup_suffix) + 1; 283 size_t backup_suffix_size_guess = simple_backup_suffix_size; 284 enum { GUESS = sizeof ".~12345~" }; 285 if (backup_suffix_size_guess < GUESS) 286 backup_suffix_size_guess = GUESS; 287 288 ssize = filelen + backup_suffix_size_guess + 1; 289 s = xmalloc (ssize); 290 memcpy (s, file, filelen + 1); 291 292 if (backup_type != simple_backups) 293 switch (numbered_backup (&s, ssize, filelen)) 294 { 295 case BACKUP_IS_SAME_LENGTH: 296 return s; 297 298 case BACKUP_IS_LONGER: 299 simple = false; 300 break; 301 302 case BACKUP_IS_NEW: 303 simple = (backup_type == numbered_existing_backups); 304 break; 305 } 306 307 if (simple) 308 memcpy (s + filelen, simple_backup_suffix, simple_backup_suffix_size); 309 check_extension (s, filelen, '~'); 310 return s; 311} 312 313static char const * const backup_args[] = 314{ 315 /* In a series of synonyms, present the most meaningful first, so 316 that argmatch_valid be more readable. */ 317 "none", "off", 318 "simple", "never", 319 "existing", "nil", 320 "numbered", "t", 321 NULL 322}; 323 324static const enum backup_type backup_types[] = 325{ 326 no_backups, no_backups, 327 simple_backups, simple_backups, 328 numbered_existing_backups, numbered_existing_backups, 329 numbered_backups, numbered_backups 330}; 331 332/* Ensure that these two vectors have the same number of elements, 333 not counting the final NULL in the first one. */ 334ARGMATCH_VERIFY (backup_args, backup_types); 335 336/* Return the type of backup specified by VERSION. 337 If VERSION is NULL or the empty string, return numbered_existing_backups. 338 If VERSION is invalid or ambiguous, fail with a diagnostic appropriate 339 for the specified CONTEXT. Unambiguous abbreviations are accepted. */ 340 341enum backup_type 342get_version (char const *context, char const *version) 343{ 344 if (version == 0 || *version == 0) 345 return numbered_existing_backups; 346 else 347 return XARGMATCH (context, version, backup_args, backup_types); 348} 349 350 351/* Return the type of backup specified by VERSION. 352 If VERSION is NULL, use the value of the envvar VERSION_CONTROL. 353 If the specified string is invalid or ambiguous, fail with a diagnostic 354 appropriate for the specified CONTEXT. 355 Unambiguous abbreviations are accepted. */ 356 357enum backup_type 358xget_version (char const *context, char const *version) 359{ 360 if (version && *version) 361 return get_version (context, version); 362 else 363 return get_version ("$VERSION_CONTROL", getenv ("VERSION_CONTROL")); 364} 365