Annotation of src/usr.bin/patch/backupfile.c, Revision 1.1
1.1 ! deraadt 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 */