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