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