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