Annotation of src/usr.bin/patch/backupfile.c, Revision 1.7
1.7 ! provos 1: /* $OpenBSD: backupfile.c,v 1.6 1999/01/03 05:33:48 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.7 ! provos 17: static char rcsid[] = "$OpenBSD: backupfile.c,v 1.6 1999/01/03 05:33:48 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>
1.6 millert 24: #include <libgen.h>
1.1 deraadt 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 (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: static char *concat ();
77: char *find_backup_file_name ();
78: static char *make_version_name ();
79: static int max_backup_version ();
80: static int version_number ();
81:
82: #ifndef NODIR
83: /* Return the name of the new backup file for file FILE,
84: allocated with malloc. Return 0 if out of memory.
85: FILE must not end with a '/' unless it is the root directory.
86: Do not call this function if backup_type == none. */
87:
88: char *
89: find_backup_file_name (file)
90: char *file;
91: {
92: char *dir;
93: char *base_versions;
94: int highest_backup;
95:
96: if (backup_type == simple)
97: return concat (file, simple_backup_suffix);
98: base_versions = concat (basename (file), ".~");
99: if (base_versions == 0)
100: return 0;
101: dir = dirname (file);
102: if (dir == 0)
103: {
104: free (base_versions);
105: return 0;
106: }
107: highest_backup = max_backup_version (base_versions, dir);
108: free (base_versions);
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;
1.7 ! provos 198: int str1_length = strlen (str1);
1.1 deraadt 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: /* If ARG is an unambiguous match for an element of the
209: null-terminated array OPTLIST, return the index in OPTLIST
210: of the matched element, else -1 if it does not match any element
211: or -2 if it is ambiguous (is a prefix of more than one element). */
212:
213: int
214: argmatch (arg, optlist)
215: char *arg;
216: char **optlist;
217: {
218: int i; /* Temporary index in OPTLIST. */
219: int arglen; /* Length of ARG. */
220: int matchind = -1; /* Index of first nonexact match. */
221: int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */
222:
223: arglen = strlen (arg);
224:
225: /* Test all elements for either exact match or abbreviated matches. */
226: for (i = 0; optlist[i]; i++)
227: {
228: if (!strncmp (optlist[i], arg, arglen))
229: {
230: if (strlen (optlist[i]) == arglen)
231: /* Exact match found. */
232: return i;
233: else if (matchind == -1)
234: /* First nonexact match found. */
235: matchind = i;
236: else
237: /* Second nonexact match found. */
238: ambiguous = 1;
239: }
240: }
241: if (ambiguous)
242: return -2;
243: else
244: return matchind;
245: }
246:
247: /* Error reporting for argmatch.
248: KIND is a description of the type of entity that was being matched.
249: VALUE is the invalid value that was given.
250: PROBLEM is the return value from argmatch. */
251:
252: void
253: invalid_arg (kind, value, problem)
254: char *kind;
255: char *value;
256: int problem;
257: {
258: fprintf (stderr, "patch: ");
259: if (problem == -1)
260: fprintf (stderr, "invalid");
261: else /* Assume -2. */
262: fprintf (stderr, "ambiguous");
263: fprintf (stderr, " %s `%s'\n", kind, value);
264: }
265:
266: static char *backup_args[] =
267: {
268: "never", "simple", "nil", "existing", "t", "numbered", 0
269: };
270:
271: static enum backup_type backup_types[] =
272: {
273: simple, simple, numbered_existing, numbered_existing, numbered, numbered
274: };
275:
276: /* Return the type of backup indicated by VERSION.
277: Unique abbreviations are accepted. */
278:
279: enum backup_type
280: get_version (version)
281: char *version;
282: {
283: int i;
284:
285: if (version == 0 || *version == 0)
286: return numbered_existing;
287: i = argmatch (version, backup_args);
288: if (i >= 0)
289: return backup_types[i];
290: invalid_arg ("version control type", version, i);
291: exit (1);
292: }
293: #endif /* NODIR */