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 | }
|
---|