1 /* backupfile.c -- make Emacs style backup file names 2 Copyright (C) 1990-1999, 2000-2003, 2005-2006 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 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 18 19 /* Written by David MacKenzie <djm@gnu.ai.mit.edu>. 20 Some algorithms adapted from GNU Emacs. */ 21 22 #include <config.h> 23 24 #include "argmatch.h" 25 #include "backupfile.h" 26 27 #include <stdio.h> 28 #include <sys/types.h> 29 #if HAVE_STRING_H 30 # include <string.h> 31 #else 32 # include <strings.h> 33 #endif 34 35 #if HAVE_DIRENT_H 36 # include <dirent.h> 37 #endif 38 39 #include <stdlib.h> 40 41 #include "basename.h" 42 43 #if HAVE_DIRENT_H 44 # define HAVE_DIR 1 45 #else 46 # define HAVE_DIR 0 47 #endif 48 49 #include <limits.h> 50 51 /* Upper bound on the string length of an integer converted to string. 52 302 / 1000 is ceil (log10 (2.0)). Subtract 1 for the sign bit; 53 add 1 for integer division truncation; add 1 more for a minus sign. */ 54 #define INT_STRLEN_BOUND(t) ((sizeof (t) * CHAR_BIT - 1) * 302 / 1000 + 2) 55 56 /* ISDIGIT differs from isdigit, as follows: 57 - Its arg may be any int or unsigned int; it need not be an unsigned char. 58 - It's guaranteed to evaluate its argument exactly once. 59 - It's typically faster. 60 Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that 61 only '0' through '9' are digits. Prefer ISDIGIT to isdigit unless 62 it's important to use the locale's definition of `digit' even when the 63 host does not conform to Posix. */ 64 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) 65 66 #if D_INO_IN_DIRENT 67 # define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0) 68 #else 69 # define REAL_DIR_ENTRY(dp) 1 70 #endif 71 72 /* The extension added to file names to produce a simple (as opposed 73 to numbered) backup file name. */ 74 const char *simple_backup_suffix = "~"; 75 76 #if HAVE_DIR 77 static int max_backup_version (const char *, const char *); 78 static int version_number (const char *, const char *, size_t); 79 #endif 80 81 /* Return the name of the new backup file for file FILE, 82 allocated with malloc. Return 0 if out of memory. 83 FILE must not end with a '/' unless it is the root directory. 84 Do not call this function if backup_type == none. */ 85 86 char * 87 find_backup_file_name (const char *file, enum backup_type backup_type) 88 { 89 size_t backup_suffix_size_max; 90 size_t file_len = strlen (file); 91 size_t numbered_suffix_size_max = INT_STRLEN_BOUND (int) + 4; 92 char *s; 93 const char *suffix = simple_backup_suffix; 94 95 /* Allow room for simple or `.~N~' backups. */ 96 backup_suffix_size_max = strlen (simple_backup_suffix) + 1; 97 if (HAVE_DIR && backup_suffix_size_max < numbered_suffix_size_max) 98 backup_suffix_size_max = numbered_suffix_size_max; 99 100 s = malloc (file_len + backup_suffix_size_max + numbered_suffix_size_max); 101 if (s) 102 { 103 strcpy (s, file); 104 105 #if HAVE_DIR 106 if (backup_type != simple) 107 { 108 int highest_backup; 109 size_t dir_len = basename (s) - s; 110 111 strcpy (s + dir_len, "."); 112 highest_backup = max_backup_version (file + dir_len, s); 113 if (! (backup_type == numbered_existing && highest_backup == 0)) 114 { 115 char *numbered_suffix = s + (file_len + backup_suffix_size_max); 116 sprintf (numbered_suffix, ".~%d~", highest_backup + 1); 117 suffix = numbered_suffix; 118 } 119 strcpy (s, file); 120 } 121 #endif /* HAVE_DIR */ 122 123 addext (s, suffix, '~'); 124 } 125 return s; 126 } 127 128 #if HAVE_DIR 129 130 /* Return the number of the highest-numbered backup file for file 131 FILE in directory DIR. If there are no numbered backups 132 of FILE in DIR, or an error occurs reading DIR, return 0. 133 */ 134 135 static int 136 max_backup_version (const char *file, const char *dir) 137 { 138 DIR *dirp; 139 struct dirent *dp; 140 int highest_version; 141 int this_version; 142 size_t file_name_length; 143 144 dirp = opendir (dir); 145 if (!dirp) 146 return 0; 147 148 highest_version = 0; 149 file_name_length = strlen (file); 150 151 while ((dp = readdir (dirp)) != 0) 152 { 153 if (!REAL_DIR_ENTRY (dp) || strlen (dp->d_name) < file_name_length + 4) 154 continue; 155 156 this_version = version_number (file, dp->d_name, file_name_length); 157 if (this_version > highest_version) 158 highest_version = this_version; 159 } 160 if (closedir (dirp)) 161 return 0; 162 return highest_version; 163 } 164 165 /* If BACKUP is a numbered backup of BASE, return its version number; 166 otherwise return 0. BASE_LENGTH is the length of BASE. 167 */ 168 169 static int 170 version_number (const char *base, const char *backup, size_t base_length) 171 { 172 int version; 173 const char *p; 174 175 version = 0; 176 if (strncmp (base, backup, base_length) == 0 177 && backup[base_length] == '.' 178 && backup[base_length + 1] == '~') 179 { 180 for (p = &backup[base_length + 2]; ISDIGIT (*p); ++p) 181 version = version * 10 + *p - '0'; 182 if (p[0] != '~' || p[1]) 183 version = 0; 184 } 185 return version; 186 } 187 #endif /* HAVE_DIR */ 188 189 static const char * const backup_args[] = 190 { 191 /* In a series of synonyms, present the most meaning full first, so 192 that argmatch_valid be more readable. */ 193 "none", "off", 194 "simple", "never", 195 "existing", "nil", 196 "numbered", "t", 197 0 198 }; 199 200 static const enum backup_type backup_types[] = 201 { 202 none, none, 203 simple, simple, 204 numbered_existing, numbered_existing, 205 numbered, numbered 206 }; 207 208 /* Return the type of backup specified by VERSION. 209 If VERSION is NULL or the empty string, return numbered_existing. 210 If VERSION is invalid or ambiguous, fail with a diagnostic appropriate 211 for the specified CONTEXT. Unambiguous abbreviations are accepted. */ 212 213 enum backup_type 214 get_version (const char *context, const char *version) 215 { 216 if (version == 0 || *version == 0) 217 return numbered_existing; 218 else 219 return XARGMATCH (context, version, backup_args, backup_types); 220 } 221 222 223 /* Return the type of backup specified by VERSION. 224 If VERSION is NULL, use the value of the envvar VERSION_CONTROL. 225 If the specified string is invalid or ambiguous, fail with a diagnostic 226 appropriate for the specified CONTEXT. 227 Unambiguous abbreviations are accepted. */ 228 229 enum backup_type 230 xget_version (const char *context, const char *version) 231 { 232 if (version && *version) 233 return get_version (context, version); 234 else 235 return get_version ("$VERSION_CONTROL", getenv ("VERSION_CONTROL")); 236 } 237