[3444] | 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 | }
|
---|