backupfile.c revision 118136
1299118Sbr/* backupfile.c -- make Emacs style backup file names 2299118Sbr Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc. 3299118Sbr 4299118Sbr This program is free software; you can redistribute it and/or modify 5299118Sbr it under the terms of the GNU General Public License as published by 6299118Sbr the Free Software Foundation; either version 2, or (at your option) 7299118Sbr any later version. 8299118Sbr 9299118Sbr This program is distributed in the hope that it will be useful, 10299118Sbr but WITHOUT ANY WARRANTY; without even the implied warranty of 11299118Sbr MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12299118Sbr GNU General Public License for more details. 13299118Sbr 14299118Sbr You should have received a copy of the GNU General Public License 15299118Sbr along with this program; if not, write to the Free Software 16299118Sbr Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ 17299118Sbr 18299118Sbr/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. 19299118Sbr Some algorithms adapted from GNU Emacs. 20299118Sbr */ 21299118Sbr 22299118Sbr#include <sys/cdefs.h> 23299118Sbr__FBSDID("$FreeBSD: head/gnu/usr.bin/patch/backupfile.c 118136 2003-07-29 00:31:07Z jwd $"); 24299118Sbr 25299118Sbr#include "config.h" 26299118Sbr#include <stdio.h> 27299118Sbr#include <ctype.h> 28299118Sbr#include <sys/types.h> 29299118Sbr#include "backupfile.h" 30299118Sbr#ifdef STDC_HEADERS 31299118Sbr#include <string.h> 32299118Sbr#include <stdlib.h> 33299118Sbr#else 34299118Sbrchar *malloc (); 35299118Sbr#endif 36299118Sbr 37299118Sbr#if defined (HAVE_UNISTD_H) 38299118Sbr#include <unistd.h> 39299118Sbr#endif 40299118Sbr 41299118Sbr#if defined(DIRENT) || defined(_POSIX_VERSION) 42299118Sbr#include <dirent.h> 43299118Sbr#define NLENGTH(direct) (strlen((direct)->d_name)) 44299118Sbr#else /* not (DIRENT or _POSIX_VERSION) */ 45299118Sbr#define dirent direct 46299118Sbr#define NLENGTH(direct) ((direct)->d_namlen) 47299118Sbr#ifdef SYSNDIR 48299118Sbr#include <sys/ndir.h> 49299118Sbr#endif /* SYSNDIR */ 50299118Sbr#ifdef SYSDIR 51299118Sbr#include <sys/dir.h> 52299118Sbr#endif /* SYSDIR */ 53299118Sbr#ifdef NDIR 54299118Sbr#include <ndir.h> 55299118Sbr#endif /* NDIR */ 56299118Sbr#endif /* DIRENT or _POSIX_VERSION */ 57299118Sbr 58299118Sbr#ifndef isascii 59299118Sbr#define ISDIGIT(c) (isdigit ((unsigned char) (c))) 60299118Sbr#else 61299118Sbr#define ISDIGIT(c) (isascii (c) && isdigit (c)) 62299118Sbr#endif 63299118Sbr 64299118Sbr#if defined (_POSIX_VERSION) 65299118Sbr/* POSIX does not require that the d_ino field be present, and some 66299118Sbr systems do not provide it. */ 67299118Sbr#define REAL_DIR_ENTRY(dp) 1 68299118Sbr#else 69299118Sbr#define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0) 70299118Sbr#endif 71299118Sbr 72299118Sbr/* Which type of backup file names are generated. */ 73299118Sbrenum backup_type backup_type = none; 74299118Sbr 75299118Sbr/* The extension added to file names to produce a simple (as opposed 76299118Sbr to numbered) backup file name. */ 77299118Sbrchar *simple_backup_suffix = "~"; 78299118Sbr 79299118Sbrint argmatch(char *_arg, char **_optlist); 80299118Sbrconst char *basename(const char *_name); 81299118Sbrchar *dirname(const char *_path); 82299118Sbrstatic char *concat(const char *_str1, const char *_str2); 83299118Sbrchar *find_backup_file_name(char *_file); 84299118Sbrstatic char *make_version_name (char *_file, int _version); 85299118Sbrstatic int max_backup_version(char *_file, char *_dir); 86299118Sbrstatic int version_number(char *base, char *backup, int base_length); 87299118Sbrvoid invalid_arg(const char *_kind, char *_value, int _problem); 88299118Sbr 89299118Sbr/* Return NAME with any leading path stripped off. */ 90299118Sbr 91299118Sbrconst char * 92299118Sbrbasename(const char *name) 93299118Sbr{ 94299118Sbr const char *r = name, *p = name; 95299118Sbr 96299118Sbr while (*p) 97299118Sbr if (*p++ == '/') 98299118Sbr r = p; 99299118Sbr return r; 100299118Sbr} 101299118Sbr 102299118Sbr#ifndef NODIR 103299118Sbr/* Return the name of the new backup file for file FILE, 104299118Sbr allocated with malloc. Return 0 if out of memory. 105299118Sbr FILE must not end with a '/' unless it is the root directory. 106299118Sbr Do not call this function if backup_type == none. */ 107299118Sbr 108299118Sbrchar * 109299118Sbrfind_backup_file_name(char *file) 110299118Sbr{ 111299118Sbr char *dir; 112299118Sbr char *base_versions; 113299118Sbr int highest_backup; 114299118Sbr 115299118Sbr if (backup_type == simple) 116299118Sbr { 117299118Sbr char *s = malloc (strlen (file) + strlen (simple_backup_suffix) + 1); 118299118Sbr strcpy (s, file); 119299118Sbr addext (s, simple_backup_suffix, '~'); 120299118Sbr return s; 121299118Sbr } 122299118Sbr base_versions = concat (basename (file), ".~"); 123299118Sbr if (base_versions == 0) 124299118Sbr return 0; 125299118Sbr dir = dirname (file); 126299118Sbr if (dir == 0) 127299118Sbr { 128299118Sbr free (base_versions); 129299118Sbr return 0; 130299118Sbr } 131299118Sbr highest_backup = max_backup_version (base_versions, dir); 132299118Sbr free (base_versions); 133299118Sbr free (dir); 134299118Sbr if (backup_type == numbered_existing && highest_backup == 0) 135299118Sbr return concat (file, simple_backup_suffix); 136299118Sbr return make_version_name (file, highest_backup + 1); 137299118Sbr} 138299118Sbr 139299118Sbr/* Return the number of the highest-numbered backup file for file 140299118Sbr FILE in directory DIR. If there are no numbered backups 141299118Sbr of FILE in DIR, or an error occurs reading DIR, return 0. 142299118Sbr FILE should already have ".~" appended to it. */ 143299118Sbr 144299118Sbrstatic int 145299118Sbrmax_backup_version(char *file, char *dir) 146299118Sbr{ 147299118Sbr DIR *dirp; 148299118Sbr struct dirent *dp; 149299118Sbr int highest_version; 150299118Sbr int this_version; 151299118Sbr int file_name_length; 152299118Sbr 153299118Sbr dirp = opendir (dir); 154299118Sbr if (!dirp) 155299118Sbr return 0; 156299118Sbr 157299118Sbr highest_version = 0; 158299118Sbr file_name_length = strlen (file); 159299118Sbr 160299118Sbr while ((dp = readdir (dirp)) != 0) 161299118Sbr { 162299118Sbr if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) <= file_name_length) 163299118Sbr continue; 164299118Sbr 165 this_version = version_number (file, dp->d_name, file_name_length); 166 if (this_version > highest_version) 167 highest_version = this_version; 168 } 169 closedir (dirp); 170 return highest_version; 171} 172 173/* Return a string, allocated with malloc, containing 174 "FILE.~VERSION~". Return 0 if out of memory. */ 175 176static char * 177make_version_name(char *file, int version) 178{ 179 char *backup_name; 180 181 backup_name = malloc (strlen (file) + 16); 182 if (backup_name == 0) 183 return 0; 184 sprintf (backup_name, "%s.~%d~", file, version); 185 return backup_name; 186} 187 188/* If BACKUP is a numbered backup of BASE, return its version number; 189 otherwise return 0. BASE_LENGTH is the length of BASE. 190 BASE should already have ".~" appended to it. */ 191 192static int 193version_number(char *base, char *backup, int base_length) 194{ 195 int version; 196 char *p; 197 198 version = 0; 199 if (!strncmp (base, backup, base_length) && ISDIGIT (backup[base_length])) 200 { 201 for (p = &backup[base_length]; ISDIGIT (*p); ++p) 202 version = version * 10 + *p - '0'; 203 if (p[0] != '~' || p[1]) 204 version = 0; 205 } 206 return version; 207} 208 209/* Return the newly-allocated concatenation of STR1 and STR2. 210 If out of memory, return 0. */ 211 212static char * 213concat(const char *str1, const char *str2) 214{ 215 char *newstr; 216 int str1_length = strlen (str1); 217 218 newstr = malloc (str1_length + strlen (str2) + 1); 219 if (newstr == 0) 220 return 0; 221 strcpy (newstr, str1); 222 strcpy (newstr + str1_length, str2); 223 return newstr; 224} 225 226/* Return the leading directories part of PATH, 227 allocated with malloc. If out of memory, return 0. 228 Assumes that trailing slashes have already been 229 removed. */ 230 231char * 232dirname(const char *path) 233{ 234 char *newpath; 235 const char *slash; 236 int length; /* Length of result, not including NUL. */ 237 238 slash = basename (path); 239 if (slash == path) 240 { 241 /* File is in the current directory. */ 242 path = "."; 243 length = 1; 244 } 245 else 246 { 247 /* Remove any trailing slashes from result. */ 248 while (*--slash == '/' && slash > path) 249 ; 250 251 length = slash - path + 1; 252 } 253 newpath = malloc (length + 1); 254 if (newpath == 0) 255 return 0; 256 strncpy (newpath, path, length); 257 newpath[length] = 0; 258 return newpath; 259} 260 261/* If ARG is an unambiguous match for an element of the 262 null-terminated array OPTLIST, return the index in OPTLIST 263 of the matched element, else -1 if it does not match any element 264 or -2 if it is ambiguous (is a prefix of more than one element). */ 265 266int 267argmatch(char *arg, char **optlist) 268{ 269 int i; /* Temporary index in OPTLIST. */ 270 int arglen; /* Length of ARG. */ 271 int matchind = -1; /* Index of first nonexact match. */ 272 int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */ 273 274 arglen = strlen (arg); 275 276 /* Test all elements for either exact match or abbreviated matches. */ 277 for (i = 0; optlist[i]; i++) 278 { 279 if (!strncmp (optlist[i], arg, arglen)) 280 { 281 if (strlen (optlist[i]) == arglen) 282 /* Exact match found. */ 283 return i; 284 else if (matchind == -1) 285 /* First nonexact match found. */ 286 matchind = i; 287 else 288 /* Second nonexact match found. */ 289 ambiguous = 1; 290 } 291 } 292 if (ambiguous) 293 return -2; 294 else 295 return matchind; 296} 297 298/* Error reporting for argmatch. 299 KIND is a description of the type of entity that was being matched. 300 VALUE is the invalid value that was given. 301 PROBLEM is the return value from argmatch. */ 302 303void 304invalid_arg(const char *kind, char *value, 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 314static const char *backup_args[] = 315{ 316 "never", "simple", "nil", "existing", "t", "numbered", 0 317}; 318 319static 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 327enum backup_type 328get_version(char *version) 329{ 330 int i; 331 332 if (version == 0 || *version == 0) 333 return numbered_existing; 334 i = argmatch (version, backup_args); 335 if (i >= 0) 336 return backup_types[i]; 337 invalid_arg ("version control type", version, i); 338 exit (1); 339} 340#endif /* NODIR */ 341 342/* Append to FILENAME the extension EXT, unless the result would be too long, 343 in which case just append the character E. */ 344 345void 346addext(char *filename, char *ext, int e) 347{ 348 char *s = basename (filename); 349 int slen = strlen (s), extlen = strlen (ext); 350 long slen_max = -1; 351 352#if HAVE_PATHCONF && defined (_PC_NAME_MAX) 353#ifndef _POSIX_NAME_MAX 354#define _POSIX_NAME_MAX 14 355#endif 356 if (slen + extlen <= _POSIX_NAME_MAX) 357 /* The file name is so short there's no need to call pathconf. */ 358 slen_max = _POSIX_NAME_MAX; 359 else if (s == filename) 360 slen_max = pathconf (".", _PC_NAME_MAX); 361 else 362 { 363 char c = *s; 364 *s = 0; 365 slen_max = pathconf (filename, _PC_NAME_MAX); 366 *s = c; 367 } 368#endif 369 if (slen_max == -1) { 370#ifdef HAVE_LONG_FILE_NAMES 371 slen_max = 255; 372#else 373 slen_max = 14; 374#endif 375 } 376 if (slen + extlen <= slen_max) 377 strcpy (s + slen, ext); 378 else 379 { 380 if (slen_max <= slen) { 381 /* Try to preserve difference between .h .c etc. */ 382 if (slen == slen_max && s[slen - 2] == '.') 383 s[slen - 2] = s[slen - 1]; 384 385 slen = slen_max - 1; 386 } 387 s[slen] = e; 388 s[slen + 1] = 0; 389 } 390} 391