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