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