(find_backup_file_name, max_backup_version, make_version_name,
[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 #ifdef HAVE_STRING_H
30 # include <string.h>
31 #else
32 # include <strings.h>
33 #endif
34
35 #ifdef HAVE_DIRENT_H
36 # include <dirent.h>
37 # define NLENGTH(direct) (strlen((direct)->d_name))
38 #else /* not HAVE_DIRENT_H */
39 # define dirent direct
40 # define NLENGTH(direct) ((direct)->d_namlen)
41 # ifdef HAVE_SYS_NDIR_H
42 #  include <sys/ndir.h>
43 # endif /* HAVE_SYS_NDIR_H */
44 # ifdef HAVE_SYS_DIR_H
45 #  include <sys/dir.h>
46 # endif /* HAVE_SYS_DIR_H */
47 # ifdef HAVE_NDIR_H
48 #  include <ndir.h>
49 # endif /* HAVE_NDIR_H */
50 #endif /* HAVE_DIRENT_H */
51
52 #ifdef CLOSEDIR_VOID
53 /* Fake a return value. */
54 # define CLOSEDIR(d) (closedir (d), 0)
55 #else
56 # define CLOSEDIR(d) closedir (d)
57 #endif
58
59 #ifdef STDC_HEADERS
60 # include <stdlib.h>
61 #else
62 char *malloc ();
63 #endif
64
65 #if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
66 # define ISASCII(c) 1
67 #else
68 # define ISASCII(c) isascii(c)
69 #endif
70
71 #define ISDIGIT(c) (ISASCII ((unsigned char) (c)) \
72                     && isdigit ((unsigned char) (c)))
73
74 #if defined (HAVE_UNISTD_H)
75 #include <unistd.h>
76 #endif
77
78 #if defined (_POSIX_VERSION)
79 /* POSIX does not require that the d_ino field be present, and some
80    systems do not provide it. */
81 # define REAL_DIR_ENTRY(dp) 1
82 #else
83 # define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0)
84 #endif
85
86 /* Which type of backup file names are generated. */
87 enum backup_type backup_type = none;
88
89 /* The extension added to file names to produce a simple (as opposed
90    to numbered) backup file name. */
91 char *simple_backup_suffix = "~";
92
93 char *basename ();
94 char *dirname ();
95 static char *concat ();
96 char *find_backup_file_name ();
97 static char *make_version_name ();
98 static int max_backup_version ();
99 static int version_number ();
100
101 /* Return the name of the new backup file for file FILE,
102    allocated with malloc.  Return 0 if out of memory.
103    FILE must not end with a '/' unless it is the root directory.
104    Do not call this function if backup_type == none. */
105
106 char *
107 find_backup_file_name (file)
108      const char *file;
109 {
110   char *dir;
111   char *base_versions;
112   int highest_backup;
113
114   if (backup_type == simple)
115     return concat (file, simple_backup_suffix);
116   base_versions = concat (basename (file), ".~");
117   if (base_versions == 0)
118     return 0;
119   dir = dirname (file);
120   if (dir == 0)
121     {
122       free (base_versions);
123       return 0;
124     }
125   highest_backup = max_backup_version (base_versions, dir);
126   free (base_versions);
127   free (dir);
128   if (backup_type == numbered_existing && highest_backup == 0)
129     return concat (file, simple_backup_suffix);
130   return make_version_name (file, highest_backup + 1);
131 }
132
133 /* Return the number of the highest-numbered backup file for file
134    FILE in directory DIR.  If there are no numbered backups
135    of FILE in DIR, or an error occurs reading DIR, return 0.
136    FILE should already have ".~" appended to it. */
137
138 static int
139 max_backup_version (file, dir)
140      const char *file;
141      const char *dir;
142 {
143   DIR *dirp;
144   struct dirent *dp;
145   int highest_version;
146   int this_version;
147   size_t 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      const 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      const char *base;
194      const char *backup;
195      int base_length;
196 {
197   int version;
198   const 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      const char *str1;
217      const char *str2;
218 {
219   char *newstr;
220   int str1_length = strlen (str1);
221
222   newstr = malloc (str1_length + strlen (str2) + 1);
223   if (newstr == 0)
224     return 0;
225   strcpy (newstr, str1);
226   strcpy (newstr + str1_length, str2);
227   return newstr;
228 }