1 /* backupfile.c -- make Emacs style backup file names 2 Copyright (C) 1990 Free Software Foundation, Inc. 3 4 This program is free software; you can redistribute it and/or modify 5 it without restriction. 6 7 This program is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ 10 11 /* David MacKenzie <djm@ai.mit.edu>. 12 Some algorithms adapted from GNU Emacs. */ 13 14 #include <stdio.h> 15 #include <ctype.h> 16 #include <sys/types.h> 17 #include "backupfile.h" 18 #include "config.h" 19 char *index (); 20 char *rindex (); 21 char *malloc (); 22 23 #ifdef DIRENT 24 #include <dirent.h> 25 #ifdef direct 26 #undef direct 27 #endif 28 #define direct dirent 29 #define NLENGTH(direct) (strlen((direct)->d_name)) 30 #else /* !DIRENT */ 31 #define NLENGTH(direct) ((direct)->d_namlen) 32 #ifdef USG 33 #ifdef SYSNDIR 34 #include <sys/ndir.h> 35 #else /* !SYSNDIR */ 36 #include <ndir.h> 37 #endif /* !SYSNDIR */ 38 #else /* !USG */ 39 #ifdef SYSDIR 40 #include <sys/dir.h> 41 #endif /* SYSDIR */ 42 #endif /* !USG */ 43 #endif /* !DIRENT */ 44 45 #ifndef isascii 46 #define ISDIGIT(c) (isdigit ((unsigned char) (c))) 47 #else 48 #define ISDIGIT(c) (isascii (c) && isdigit (c)) 49 #endif 50 51 #if defined (HAVE_UNISTD_H) 52 #include <unistd.h> 53 #endif 54 55 #if defined (_POSIX_VERSION) 56 /* POSIX does not require that the d_ino field be present, and some 57 systems do not provide it. */ 58 #define REAL_DIR_ENTRY(dp) 1 59 #else 60 #define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0) 61 #endif 62 63 /* Which type of backup file names are generated. */ 64 enum backup_type backup_type = none; 65 66 /* The extension added to file names to produce a simple (as opposed 67 to numbered) backup file name. */ 68 char *simple_backup_suffix = "~"; 69 70 char *basename (); 71 char *dirname (); 72 static char *concat (); 73 char *find_backup_file_name (); 74 static char *make_version_name (); 75 static int max_backup_version (); 76 static int version_number (); 77 78 #ifndef NODIR 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 84 char * 85 find_backup_file_name (file) 86 char *file; 87 { 88 char *dir; 89 char *base_versions; 90 int highest_backup; 91 92 if (backup_type == simple) 93 return concat (file, simple_backup_suffix); 94 base_versions = concat (basename (file), ".~"); 95 if (base_versions == 0) 96 return 0; 97 dir = dirname (file); 98 if (dir == 0) 99 { 100 free (base_versions); 101 return 0; 102 } 103 highest_backup = max_backup_version (base_versions, dir); 104 free (base_versions); 105 free (dir); 106 if (backup_type == numbered_existing && highest_backup == 0) 107 return concat (file, simple_backup_suffix); 108 return make_version_name (file, highest_backup + 1); 109 } 110 111 /* Return the number of the highest-numbered backup file for file 112 FILE in directory DIR. If there are no numbered backups 113 of FILE in DIR, or an error occurs reading DIR, return 0. 114 FILE should already have ".~" appended to it. */ 115 116 static int 117 max_backup_version (file, dir) 118 char *file, *dir; 119 { 120 DIR *dirp; 121 struct direct *dp; 122 int highest_version; 123 int this_version; 124 int file_name_length; 125 126 dirp = opendir (dir); 127 if (!dirp) 128 return 0; 129 130 highest_version = 0; 131 file_name_length = strlen (file); 132 133 while ((dp = readdir (dirp)) != 0) 134 { 135 if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) <= file_name_length) 136 continue; 137 138 this_version = version_number (file, dp->d_name, file_name_length); 139 if (this_version > highest_version) 140 highest_version = this_version; 141 } 142 closedir (dirp); 143 return highest_version; 144 } 145 146 /* Return a string, allocated with malloc, containing 147 "FILE.~VERSION~". Return 0 if out of memory. */ 148 149 static char * 150 make_version_name (file, version) 151 char *file; 152 int version; 153 { 154 char *backup_name; 155 156 backup_name = malloc (strlen (file) + 16); 157 if (backup_name == 0) 158 return 0; 159 sprintf (backup_name, "%s.~%d~", file, version); 160 return backup_name; 161 } 162 163 /* If BACKUP is a numbered backup of BASE, return its version number; 164 otherwise return 0. BASE_LENGTH is the length of BASE. 165 BASE should already have ".~" appended to it. */ 166 167 static int 168 version_number (base, backup, base_length) 169 char *base; 170 char *backup; 171 int base_length; 172 { 173 int version; 174 char *p; 175 176 version = 0; 177 if (!strncmp (base, backup, base_length) && ISDIGIT (backup[base_length])) 178 { 179 for (p = &backup[base_length]; ISDIGIT (*p); ++p) 180 version = version * 10 + *p - '0'; 181 if (p[0] != '~' || p[1]) 182 version = 0; 183 } 184 return version; 185 } 186 187 /* Return the newly-allocated concatenation of STR1 and STR2. 188 If out of memory, return 0. */ 189 190 static char * 191 concat (str1, str2) 192 char *str1, *str2; 193 { 194 char *newstr; 195 char str1_length = strlen (str1); 196 197 newstr = malloc (str1_length + strlen (str2) + 1); 198 if (newstr == 0) 199 return 0; 200 strcpy (newstr, str1); 201 strcpy (newstr + str1_length, str2); 202 return newstr; 203 } 204 205 /* Return NAME with any leading path stripped off. */ 206 207 char * 208 basename (name) 209 char *name; 210 { 211 char *base; 212 213 base = rindex (name, '/'); 214 return base ? base + 1 : name; 215 } 216 217 /* Return the leading directories part of PATH, 218 allocated with malloc. If out of memory, return 0. 219 Assumes that trailing slashes have already been 220 removed. */ 221 222 char * 223 dirname (path) 224 char *path; 225 { 226 char *newpath; 227 char *slash; 228 int length; /* Length of result, not including NUL. */ 229 230 slash = rindex (path, '/'); 231 if (slash == 0) 232 { 233 /* File is in the current directory. */ 234 path = "."; 235 length = 1; 236 } 237 else 238 { 239 /* Remove any trailing slashes from result. */ 240 while (slash > path && *slash == '/') 241 --slash; 242 243 length = slash - path + 1; 244 } 245 newpath = malloc (length + 1); 246 if (newpath == 0) 247 return 0; 248 strncpy (newpath, path, length); 249 newpath[length] = 0; 250 return newpath; 251 } 252 253 /* If ARG is an unambiguous match for an element of the 254 null-terminated array OPTLIST, return the index in OPTLIST 255 of the matched element, else -1 if it does not match any element 256 or -2 if it is ambiguous (is a prefix of more than one element). */ 257 258 int 259 argmatch (arg, optlist) 260 char *arg; 261 char **optlist; 262 { 263 int i; /* Temporary index in OPTLIST. */ 264 int arglen; /* Length of ARG. */ 265 int matchind = -1; /* Index of first nonexact match. */ 266 int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */ 267 268 arglen = strlen (arg); 269 270 /* Test all elements for either exact match or abbreviated matches. */ 271 for (i = 0; optlist[i]; i++) 272 { 273 if (!strncmp (optlist[i], arg, arglen)) 274 { 275 if (strlen (optlist[i]) == arglen) 276 /* Exact match found. */ 277 return i; 278 else if (matchind == -1) 279 /* First nonexact match found. */ 280 matchind = i; 281 else 282 /* Second nonexact match found. */ 283 ambiguous = 1; 284 } 285 } 286 if (ambiguous) 287 return -2; 288 else 289 return matchind; 290 } 291 292 /* Error reporting for argmatch. 293 KIND is a description of the type of entity that was being matched. 294 VALUE is the invalid value that was given. 295 PROBLEM is the return value from argmatch. */ 296 297 void 298 invalid_arg (kind, value, problem) 299 char *kind; 300 char *value; 301 int problem; 302 { 303 fprintf (stderr, "patch: "); 304 if (problem == -1) 305 fprintf (stderr, "invalid"); 306 else /* Assume -2. */ 307 fprintf (stderr, "ambiguous"); 308 fprintf (stderr, " %s `%s'\n", kind, value); 309 } 310 311 static char *backup_args[] = 312 { 313 "never", "simple", "nil", "existing", "t", "numbered", 0 314 }; 315 316 static enum backup_type backup_types[] = 317 { 318 simple, simple, numbered_existing, numbered_existing, numbered, numbered 319 }; 320 321 /* Return the type of backup indicated by VERSION. 322 Unique abbreviations are accepted. */ 323 324 enum backup_type 325 get_version (version) 326 char *version; 327 { 328 int i; 329 330 if (version == 0 || *version == 0) 331 return numbered_existing; 332 i = argmatch (version, backup_args); 333 if (i >= 0) 334 return backup_types[i]; 335 invalid_arg ("version control type", version, i); 336 exit (1); 337 } 338 #endif /* NODIR */ 339