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