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