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