merge with 3.9h
[gnulib.git] / lib / backupfile.c
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 under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
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.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
17
18 /* David MacKenzie <djm@gnu.ai.mit.edu>.
19    Some algorithms adapted from GNU Emacs. */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <ctype.h>
27 #include <sys/types.h>
28 #include "backupfile.h"
29 #if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
30 #include <string.h>
31 #ifndef index
32 #define index strchr
33 #endif
34 #ifndef rindex
35 #define rindex strrchr
36 #endif
37 #else
38 #include <strings.h>
39 #endif
40
41 #ifdef HAVE_DIRENT_H
42 #include <dirent.h>
43 #define NLENGTH(direct) (strlen((direct)->d_name))
44 #else /* not HAVE_DIRENT_H */
45 #define dirent direct
46 #define NLENGTH(direct) ((direct)->d_namlen)
47 #ifdef HAVE_SYS_NDIR_H
48 #include <sys/ndir.h>
49 #endif /* HAVE_SYS_NDIR_H */
50 #ifdef HAVE_SYS_DIR_H
51 #include <sys/dir.h>
52 #endif /* HAVE_SYS_DIR_H */
53 #ifdef HAVE_NDIR_H
54 #include <ndir.h>
55 #endif /* HAVE_NDIR_H */
56 #endif /* HAVE_DIRENT_H */
57
58 #ifdef CLOSEDIR_VOID
59 /* Fake a return value. */
60 #define CLOSEDIR(d) (closedir (d), 0)
61 #else
62 #define CLOSEDIR(d) closedir (d)
63 #endif
64
65 #ifdef STDC_HEADERS
66 #include <stdlib.h>
67 #else
68 char *malloc ();
69 #endif
70
71 #if !defined (isascii) || defined (STDC_HEADERS)
72 #undef isascii
73 #define isascii(c) 1
74 #endif
75
76 #define ISDIGIT(c) (isascii ((unsigned char ) c) \
77                     && isdigit ((unsigned char) (c)))
78
79 #if defined (HAVE_UNISTD_H)
80 #include <unistd.h>
81 #endif
82
83 #if defined (_POSIX_VERSION)
84 /* POSIX does not require that the d_ino field be present, and some
85    systems do not provide it. */
86 #define REAL_DIR_ENTRY(dp) 1
87 #else
88 #define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0)
89 #endif
90
91 /* Which type of backup file names are generated. */
92 enum backup_type backup_type = none;
93
94 /* The extension added to file names to produce a simple (as opposed
95    to numbered) backup file name. */
96 char *simple_backup_suffix = "~";
97
98 char *basename ();
99 char *dirname ();
100 static char *concat ();
101 char *find_backup_file_name ();
102 static char *make_version_name ();
103 static int max_backup_version ();
104 static int version_number ();
105
106 /* Return the name of the new backup file for file FILE,
107    allocated with malloc.  Return 0 if out of memory.
108    FILE must not end with a '/' unless it is the root directory.
109    Do not call this function if backup_type == none. */
110
111 char *
112 find_backup_file_name (file)
113      char *file;
114 {
115   char *dir;
116   char *base_versions;
117   int highest_backup;
118
119   if (backup_type == simple)
120     return concat (file, simple_backup_suffix);
121   base_versions = concat (basename (file), ".~");
122   if (base_versions == 0)
123     return 0;
124   dir = dirname (file);
125   if (dir == 0)
126     {
127       free (base_versions);
128       return 0;
129     }
130   highest_backup = max_backup_version (base_versions, dir);
131   free (base_versions);
132   free (dir);
133   if (backup_type == numbered_existing && highest_backup == 0)
134     return concat (file, simple_backup_suffix);
135   return make_version_name (file, highest_backup + 1);
136 }
137
138 /* Return the number of the highest-numbered backup file for file
139    FILE in directory DIR.  If there are no numbered backups
140    of FILE in DIR, or an error occurs reading DIR, return 0.
141    FILE should already have ".~" appended to it. */
142
143 static int
144 max_backup_version (file, dir)
145      char *file, *dir;
146 {
147   DIR *dirp;
148   struct dirent *dp;
149   int highest_version;
150   int this_version;
151   int file_name_length;
152
153   dirp = opendir (dir);
154   if (!dirp)
155     return 0;
156
157   highest_version = 0;
158   file_name_length = strlen (file);
159
160   while ((dp = readdir (dirp)) != 0)
161     {
162       if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) <= file_name_length)
163         continue;
164
165       this_version = version_number (file, dp->d_name, file_name_length);
166       if (this_version > highest_version)
167         highest_version = this_version;
168     }
169   if (CLOSEDIR (dirp))
170     return 0;
171   return highest_version;
172 }
173
174 /* Return a string, allocated with malloc, containing
175    "FILE.~VERSION~".  Return 0 if out of memory. */
176
177 static char *
178 make_version_name (file, version)
179      char *file;
180      int version;
181 {
182   char *backup_name;
183
184   backup_name = malloc (strlen (file) + 16);
185   if (backup_name == 0)
186     return 0;
187   sprintf (backup_name, "%s.~%d~", file, version);
188   return backup_name;
189 }
190
191 /* If BACKUP is a numbered backup of BASE, return its version number;
192    otherwise return 0.  BASE_LENGTH is the length of BASE.
193    BASE should already have ".~" appended to it. */
194
195 static int
196 version_number (base, backup, base_length)
197      char *base;
198      char *backup;
199      int base_length;
200 {
201   int version;
202   char *p;
203
204   version = 0;
205   if (!strncmp (base, backup, base_length) && ISDIGIT (backup[base_length]))
206     {
207       for (p = &backup[base_length]; ISDIGIT (*p); ++p)
208         version = version * 10 + *p - '0';
209       if (p[0] != '~' || p[1])
210         version = 0;
211     }
212   return version;
213 }
214
215 /* Return the newly-allocated concatenation of STR1 and STR2.
216    If out of memory, return 0. */
217
218 static char *
219 concat (str1, str2)
220      char *str1, *str2;
221 {
222   char *newstr;
223   int str1_length = strlen (str1);
224
225   newstr = malloc (str1_length + strlen (str2) + 1);
226   if (newstr == 0)
227     return 0;
228   strcpy (newstr, str1);
229   strcpy (newstr + str1_length, str2);
230   return newstr;
231 }