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