source: trunk/essentials/app-arch/tar/lib/backupfile.c

Last change on this file was 3342, checked in by bird, 18 years ago

tar 1.16.1

File size: 10.3 KB
Line 
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 extension added to file names to produce a simple (as opposed
86 to numbered) backup file name. */
87char const *simple_backup_suffix = "~";
88
89
90/* If FILE (which was of length FILELEN before an extension was
91 appended to it) is too long, replace the extension with the single
92 char E. If the result is still too long, remove the char just
93 before E. */
94
95static void
96check_extension (char *file, size_t filelen, char e)
97{
98 char *base = last_component (file);
99 size_t baselen = base_len (base);
100 size_t baselen_max = HAVE_LONG_FILE_NAMES ? 255 : NAME_MAX_MINIMUM;
101
102 if (HAVE_DOS_FILE_NAMES || NAME_MAX_MINIMUM < baselen)
103 {
104 /* The new base name is long enough to require a pathconf check. */
105 long name_max;
106
107 /* Temporarily modify the buffer into its parent directory name,
108 invoke pathconf on the directory, and then restore the buffer. */
109 char tmp[sizeof "."];
110 memcpy (tmp, base, sizeof ".");
111 strcpy (base, ".");
112 errno = 0;
113 name_max = pathconf (file, _PC_NAME_MAX);
114 if (0 <= name_max || errno == 0)
115 {
116 long size = baselen_max = name_max;
117 if (name_max != size)
118 baselen_max = SIZE_MAX;
119 }
120 memcpy (base, tmp, sizeof ".");
121 }
122
123 if (HAVE_DOS_FILE_NAMES && baselen_max <= 12)
124 {
125 /* Live within DOS's 8.3 limit. */
126 char *dot = strchr (base, '.');
127 if (!dot)
128 baselen_max = 8;
129 else
130 {
131 char const *second_dot = strchr (dot + 1, '.');
132 baselen_max = (second_dot
133 ? second_dot - base
134 : dot + 1 - base + 3);
135 }
136 }
137
138 if (baselen_max < baselen)
139 {
140 baselen = file + filelen - base;
141 if (baselen_max <= baselen)
142 baselen = baselen_max - 1;
143 base[baselen] = e;
144 base[baselen + 1] = '\0';
145 }
146}
147
148/* Returned values for NUMBERED_BACKUP. */
149
150enum numbered_backup_result
151 {
152 /* The new backup name is the same length as an existing backup
153 name, so it's valid for that directory. */
154 BACKUP_IS_SAME_LENGTH,
155
156 /* Some backup names already exist, but the returned name is longer
157 than any of them, and its length should be checked. */
158 BACKUP_IS_LONGER,
159
160 /* There are no existing backup names. The new name's length
161 should be checked. */
162 BACKUP_IS_NEW
163 };
164
165/* *BUFFER contains a file name. Store into *BUFFER the next backup
166 name for the named file, with a version number greater than all the
167 existing numbered backups. Reallocate *BUFFER as necessary; its
168 initial allocated size is BUFFER_SIZE, which must be at least 4
169 bytes longer than the file name to make room for the initially
170 appended ".~1". FILELEN is the length of the original file name.
171 The returned value indicates what kind of backup was found. If an
172 I/O or other read error occurs, use the highest backup number that
173 was found. */
174
175static enum numbered_backup_result
176numbered_backup (char **buffer, size_t buffer_size, size_t filelen)
177{
178 enum numbered_backup_result result = BACKUP_IS_NEW;
179 DIR *dirp;
180 struct dirent *dp;
181 char *buf = *buffer;
182 size_t versionlenmax = 1;
183 char *base = last_component (buf);
184 size_t base_offset = base - buf;
185 size_t baselen = base_len (base);
186
187 /* Temporarily modify the buffer into its parent directory name,
188 open the directory, and then restore the buffer. */
189 char tmp[sizeof "."];
190 memcpy (tmp, base, sizeof ".");
191 strcpy (base, ".");
192 dirp = opendir (buf);
193 memcpy (base, tmp, sizeof ".");
194 strcpy (base + baselen, ".~1~");
195
196 if (!dirp)
197 return result;
198
199 while ((dp = readdir (dirp)) != NULL)
200 {
201 char const *p;
202 char *q;
203 bool all_9s;
204 size_t versionlen;
205 size_t new_buflen;
206
207 if (! REAL_DIR_ENTRY (dp) || _D_EXACT_NAMLEN (dp) < baselen + 4)
208 continue;
209
210 if (memcmp (buf + base_offset, dp->d_name, baselen + 2) != 0)
211 continue;
212
213 p = dp->d_name + baselen + 2;
214
215 /* Check whether this file has a version number and if so,
216 whether it is larger. Use string operations rather than
217 integer arithmetic, to avoid problems with integer overflow. */
218
219 if (! ('1' <= *p && *p <= '9'))
220 continue;
221 all_9s = (*p == '9');
222 for (versionlen = 1; ISDIGIT (p[versionlen]); versionlen++)
223 all_9s &= (p[versionlen] == '9');
224
225 if (! (p[versionlen] == '~' && !p[versionlen + 1]
226 && (versionlenmax < versionlen
227 || (versionlenmax == versionlen
228 && memcmp (buf + filelen + 2, p, versionlen) <= 0))))
229 continue;
230
231 /* This directory has the largest version number seen so far.
232 Append this highest numbered extension to the file name,
233 prepending '0' to the number if it is all 9s. */
234
235 versionlenmax = all_9s + versionlen;
236 result = (all_9s ? BACKUP_IS_LONGER : BACKUP_IS_SAME_LENGTH);
237 new_buflen = filelen + 2 + versionlenmax + 1;
238 if (buffer_size <= new_buflen)
239 {
240 buf = xnrealloc (buf, 2, new_buflen);
241 buffer_size = new_buflen * 2;
242 }
243 q = buf + filelen;
244 *q++ = '.';
245 *q++ = '~';
246 *q = '0';
247 q += all_9s;
248 memcpy (q, p, versionlen + 2);
249
250 /* Add 1 to the version number. */
251
252 q += versionlen;
253 while (*--q == '9')
254 *q = '0';
255 ++*q;
256 }
257
258 closedir (dirp);
259 *buffer = buf;
260 return result;
261}
262
263/* Return the name of the new backup file for the existing file FILE,
264 allocated with malloc. Report an error and fail if out of memory.
265 Do not call this function if backup_type == no_backups. */
266
267char *
268find_backup_file_name (char const *file, enum backup_type backup_type)
269{
270 size_t filelen = strlen (file);
271 char *s;
272 size_t ssize;
273 bool simple = true;
274
275 /* Allow room for simple or ".~N~" backups. The guess must be at
276 least sizeof ".~1~", but otherwise will be adjusted as needed. */
277 size_t simple_backup_suffix_size = strlen (simple_backup_suffix) + 1;
278 size_t backup_suffix_size_guess = simple_backup_suffix_size;
279 enum { GUESS = sizeof ".~12345~" };
280 if (backup_suffix_size_guess < GUESS)
281 backup_suffix_size_guess = GUESS;
282
283 ssize = filelen + backup_suffix_size_guess + 1;
284 s = xmalloc (ssize);
285 memcpy (s, file, filelen + 1);
286
287 if (backup_type != simple_backups)
288 switch (numbered_backup (&s, ssize, filelen))
289 {
290 case BACKUP_IS_SAME_LENGTH:
291 return s;
292
293 case BACKUP_IS_LONGER:
294 simple = false;
295 break;
296
297 case BACKUP_IS_NEW:
298 simple = (backup_type == numbered_existing_backups);
299 break;
300 }
301
302 if (simple)
303 memcpy (s + filelen, simple_backup_suffix, simple_backup_suffix_size);
304 check_extension (s, filelen, '~');
305 return s;
306}
307
308static char const * const backup_args[] =
309{
310 /* In a series of synonyms, present the most meaningful first, so
311 that argmatch_valid be more readable. */
312 "none", "off",
313 "simple", "never",
314 "existing", "nil",
315 "numbered", "t",
316 NULL
317};
318
319static const enum backup_type backup_types[] =
320{
321 no_backups, no_backups,
322 simple_backups, simple_backups,
323 numbered_existing_backups, numbered_existing_backups,
324 numbered_backups, numbered_backups
325};
326
327/* Ensure that these two vectors have the same number of elements,
328 not counting the final NULL in the first one. */
329ARGMATCH_VERIFY (backup_args, backup_types);
330
331/* Return the type of backup specified by VERSION.
332 If VERSION is NULL or the empty string, return numbered_existing_backups.
333 If VERSION is invalid or ambiguous, fail with a diagnostic appropriate
334 for the specified CONTEXT. Unambiguous abbreviations are accepted. */
335
336enum backup_type
337get_version (char const *context, char const *version)
338{
339 if (version == 0 || *version == 0)
340 return numbered_existing_backups;
341 else
342 return XARGMATCH (context, version, backup_args, backup_types);
343}
344
345
346/* Return the type of backup specified by VERSION.
347 If VERSION is NULL, use the value of the envvar VERSION_CONTROL.
348 If the specified string is invalid or ambiguous, fail with a diagnostic
349 appropriate for the specified CONTEXT.
350 Unambiguous abbreviations are accepted. */
351
352enum backup_type
353xget_version (char const *context, char const *version)
354{
355 if (version && *version)
356 return get_version (context, version);
357 else
358 return get_version ("$VERSION_CONTROL", getenv ("VERSION_CONTROL"));
359}
Note: See TracBrowser for help on using the repository browser.