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