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