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