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