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