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