(ISDIGIT): Replace with smaller, faster edition
[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 Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 IN_CTYPE_DOMAIN(c) 1
67 #else
68 # define IN_CTYPE_DOMAIN(c) isascii(c)
69 #endif
70
71 #define ISDIGIT_LOCALE(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
72
73 /* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
74    - Its arg may be any int or unsigned int; it need not be an unsigned char.
75    - It's guaranteed to evaluate its argument exactly once.
76    - It's typically faster.
77    Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
78    only '0' through '9' are digits.  Prefer ISDIGIT to ISDIGIT_LOCALE unless
79    it's important to use the locale's definition of `digit' even when the
80    host does not conform to Posix.  */
81 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
82
83 #if defined (HAVE_UNISTD_H)
84 #include <unistd.h>
85 #endif
86
87 #if defined (_POSIX_VERSION)
88 /* POSIX does not require that the d_ino field be present, and some
89    systems do not provide it. */
90 # define REAL_DIR_ENTRY(dp) 1
91 #else
92 # define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0)
93 #endif
94
95 /* Which type of backup file names are generated. */
96 enum backup_type backup_type = none;
97
98 /* The extension added to file names to produce a simple (as opposed
99    to numbered) backup file name. */
100 char *simple_backup_suffix = "~";
101
102 char *basename ();
103 char *dirname ();
104 static char *concat ();
105 char *find_backup_file_name ();
106 static char *make_version_name ();
107 static int max_backup_version ();
108 static int version_number ();
109
110 /* Return the name of the new backup file for file FILE,
111    allocated with malloc.  Return 0 if out of memory.
112    FILE must not end with a '/' unless it is the root directory.
113    Do not call this function if backup_type == none. */
114
115 char *
116 find_backup_file_name (file)
117      const char *file;
118 {
119   char *dir;
120   char *base_versions;
121   int highest_backup;
122
123   if (backup_type == simple)
124     return concat (file, simple_backup_suffix);
125   base_versions = concat (basename (file), ".~");
126   if (base_versions == 0)
127     return 0;
128   dir = dirname (file);
129   if (dir == 0)
130     {
131       free (base_versions);
132       return 0;
133     }
134   highest_backup = max_backup_version (base_versions, dir);
135   free (base_versions);
136   free (dir);
137   if (backup_type == numbered_existing && highest_backup == 0)
138     return concat (file, simple_backup_suffix);
139   return make_version_name (file, highest_backup + 1);
140 }
141
142 /* Return the number of the highest-numbered backup file for file
143    FILE in directory DIR.  If there are no numbered backups
144    of FILE in DIR, or an error occurs reading DIR, return 0.
145    FILE should already have ".~" appended to it. */
146
147 static int
148 max_backup_version (file, dir)
149      const char *file;
150      const char *dir;
151 {
152   DIR *dirp;
153   struct dirent *dp;
154   int highest_version;
155   int this_version;
156   size_t file_name_length;
157
158   dirp = opendir (dir);
159   if (!dirp)
160     return 0;
161
162   highest_version = 0;
163   file_name_length = strlen (file);
164
165   while ((dp = readdir (dirp)) != 0)
166     {
167       if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) <= file_name_length)
168         continue;
169
170       this_version = version_number (file, dp->d_name, file_name_length);
171       if (this_version > highest_version)
172         highest_version = this_version;
173     }
174   if (CLOSEDIR (dirp))
175     return 0;
176   return highest_version;
177 }
178
179 /* Return a string, allocated with malloc, containing
180    "FILE.~VERSION~".  Return 0 if out of memory. */
181
182 static char *
183 make_version_name (file, version)
184      const char *file;
185      int version;
186 {
187   char *backup_name;
188
189   backup_name = malloc (strlen (file) + 16);
190   if (backup_name == 0)
191     return 0;
192   sprintf (backup_name, "%s.~%d~", file, version);
193   return backup_name;
194 }
195
196 /* If BACKUP is a numbered backup of BASE, return its version number;
197    otherwise return 0.  BASE_LENGTH is the length of BASE.
198    BASE should already have ".~" appended to it. */
199
200 static int
201 version_number (base, backup, base_length)
202      const char *base;
203      const char *backup;
204      int base_length;
205 {
206   int version;
207   const char *p;
208
209   version = 0;
210   if (!strncmp (base, backup, base_length) && ISDIGIT (backup[base_length]))
211     {
212       for (p = &backup[base_length]; ISDIGIT (*p); ++p)
213         version = version * 10 + *p - '0';
214       if (p[0] != '~' || p[1])
215         version = 0;
216     }
217   return version;
218 }
219
220 /* Return the newly-allocated concatenation of STR1 and STR2.
221    If out of memory, return 0. */
222
223 static char *
224 concat (str1, str2)
225      const char *str1;
226      const char *str2;
227 {
228   char *newstr;
229   int str1_length = strlen (str1);
230
231   newstr = malloc (str1_length + strlen (str2) + 1);
232   if (newstr == 0)
233     return 0;
234   strcpy (newstr, str1);
235   strcpy (newstr + str1_length, str2);
236   return newstr;
237 }