Annotation of src/usr.bin/patch/backupfile.c, Revision 1.9
1.9 ! millert 1: /* $OpenBSD: backupfile.c,v 1.8 2003/04/05 17:17:53 deraadt 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.9 ! millert 17: static char rcsid[] = "$OpenBSD: backupfile.c,v 1.8 2003/04/05 17:17:53 deraadt 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:
1.9 ! millert 159: if (asprintf (&backup_name, "%s.~%d~", file, version) == -1)
1.1 deraadt 160: return 0;
161: return backup_name;
162: }
163:
164: /* If BACKUP is a numbered backup of BASE, return its version number;
165: otherwise return 0. BASE_LENGTH is the length of BASE.
166: BASE should already have ".~" appended to it. */
167:
168: static int
169: version_number (base, backup, base_length)
170: char *base;
171: char *backup;
172: int base_length;
173: {
174: int version;
175: char *p;
176:
177: version = 0;
178: if (!strncmp (base, backup, base_length) && ISDIGIT (backup[base_length]))
179: {
180: for (p = &backup[base_length]; ISDIGIT (*p); ++p)
181: version = version * 10 + *p - '0';
182: if (p[0] != '~' || p[1])
183: version = 0;
184: }
185: return version;
186: }
187:
188: /* Return the newly-allocated concatenation of STR1 and STR2.
189: If out of memory, return 0. */
190:
191: static char *
192: concat (str1, str2)
193: char *str1, *str2;
194: {
195: char *newstr;
196:
1.8 deraadt 197: if (asprintf(&newstr, "%s%s", str1, str2) == -1)
1.1 deraadt 198: return 0;
199: return newstr;
200: }
201:
202: /* If ARG is an unambiguous match for an element of the
203: null-terminated array OPTLIST, return the index in OPTLIST
204: of the matched element, else -1 if it does not match any element
205: or -2 if it is ambiguous (is a prefix of more than one element). */
206:
207: int
208: argmatch (arg, optlist)
209: char *arg;
210: char **optlist;
211: {
212: int i; /* Temporary index in OPTLIST. */
213: int arglen; /* Length of ARG. */
214: int matchind = -1; /* Index of first nonexact match. */
215: int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */
216:
217: arglen = strlen (arg);
218:
219: /* Test all elements for either exact match or abbreviated matches. */
220: for (i = 0; optlist[i]; i++)
221: {
222: if (!strncmp (optlist[i], arg, arglen))
223: {
224: if (strlen (optlist[i]) == arglen)
225: /* Exact match found. */
226: return i;
227: else if (matchind == -1)
228: /* First nonexact match found. */
229: matchind = i;
230: else
231: /* Second nonexact match found. */
232: ambiguous = 1;
233: }
234: }
235: if (ambiguous)
236: return -2;
237: else
238: return matchind;
239: }
240:
241: /* Error reporting for argmatch.
242: KIND is a description of the type of entity that was being matched.
243: VALUE is the invalid value that was given.
244: PROBLEM is the return value from argmatch. */
245:
246: void
247: invalid_arg (kind, value, problem)
248: char *kind;
249: char *value;
250: int problem;
251: {
252: fprintf (stderr, "patch: ");
253: if (problem == -1)
254: fprintf (stderr, "invalid");
255: else /* Assume -2. */
256: fprintf (stderr, "ambiguous");
257: fprintf (stderr, " %s `%s'\n", kind, value);
258: }
259:
260: static char *backup_args[] =
261: {
262: "never", "simple", "nil", "existing", "t", "numbered", 0
263: };
264:
265: static enum backup_type backup_types[] =
266: {
267: simple, simple, numbered_existing, numbered_existing, numbered, numbered
268: };
269:
270: /* Return the type of backup indicated by VERSION.
271: Unique abbreviations are accepted. */
272:
273: enum backup_type
274: get_version (version)
275: char *version;
276: {
277: int i;
278:
279: if (version == 0 || *version == 0)
280: return numbered_existing;
281: i = argmatch (version, backup_args);
282: if (i >= 0)
283: return backup_types[i];
284: invalid_arg ("version control type", version, i);
285: exit (1);
286: }
287: #endif /* NODIR */