xref: /plan9/sys/src/ape/cmd/patch/backupfile.c (revision 0b459c2cb92b7c9d88818e9a2f72e678e5bc4553)
1*0b459c2cSDavid du Colombier /* backupfile.c -- make Emacs style backup file names
2*0b459c2cSDavid du Colombier    Copyright (C) 1990,1991,1992,1993,1995,1997 Free Software Foundation, Inc.
3*0b459c2cSDavid du Colombier 
4*0b459c2cSDavid du Colombier    This program is free software; you can redistribute it and/or modify
5*0b459c2cSDavid du Colombier    it under the terms of the GNU General Public License as published by
6*0b459c2cSDavid du Colombier    the Free Software Foundation; either version 2, or (at your option)
7*0b459c2cSDavid du Colombier    any later version.
8*0b459c2cSDavid du Colombier 
9*0b459c2cSDavid du Colombier    This program is distributed in the hope that it will be useful,
10*0b459c2cSDavid du Colombier    but WITHOUT ANY WARRANTY; without even the implied warranty of
11*0b459c2cSDavid du Colombier    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12*0b459c2cSDavid du Colombier    GNU General Public License for more details.
13*0b459c2cSDavid du Colombier 
14*0b459c2cSDavid du Colombier    You should have received a copy of the GNU General Public License
15*0b459c2cSDavid du Colombier    along with this program; see the file COPYING.
16*0b459c2cSDavid du Colombier    If not, write to the Free Software Foundation,
17*0b459c2cSDavid du Colombier    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18*0b459c2cSDavid du Colombier 
19*0b459c2cSDavid du Colombier /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.
20*0b459c2cSDavid du Colombier    Some algorithms adapted from GNU Emacs. */
21*0b459c2cSDavid du Colombier 
22*0b459c2cSDavid du Colombier #if HAVE_CONFIG_H
23*0b459c2cSDavid du Colombier # include <config.h>
24*0b459c2cSDavid du Colombier #endif
25*0b459c2cSDavid du Colombier 
26*0b459c2cSDavid du Colombier #include <argmatch.h>
27*0b459c2cSDavid du Colombier #include <backupfile.h>
28*0b459c2cSDavid du Colombier 
29*0b459c2cSDavid du Colombier #include <stdio.h>
30*0b459c2cSDavid du Colombier #include <sys/types.h>
31*0b459c2cSDavid du Colombier #if HAVE_STRING_H
32*0b459c2cSDavid du Colombier # include <string.h>
33*0b459c2cSDavid du Colombier #else
34*0b459c2cSDavid du Colombier # include <strings.h>
35*0b459c2cSDavid du Colombier #endif
36*0b459c2cSDavid du Colombier 
37*0b459c2cSDavid du Colombier #if HAVE_DIRENT_H
38*0b459c2cSDavid du Colombier # include <dirent.h>
39*0b459c2cSDavid du Colombier # define NLENGTH(direct) strlen ((direct)->d_name)
40*0b459c2cSDavid du Colombier #else
41*0b459c2cSDavid du Colombier # define dirent direct
42*0b459c2cSDavid du Colombier # define NLENGTH(direct) ((size_t) (direct)->d_namlen)
43*0b459c2cSDavid du Colombier # if HAVE_SYS_NDIR_H
44*0b459c2cSDavid du Colombier #  include <sys/ndir.h>
45*0b459c2cSDavid du Colombier # endif
46*0b459c2cSDavid du Colombier # if HAVE_SYS_DIR_H
47*0b459c2cSDavid du Colombier #  include <sys/dir.h>
48*0b459c2cSDavid du Colombier # endif
49*0b459c2cSDavid du Colombier # if HAVE_NDIR_H
50*0b459c2cSDavid du Colombier #  include <ndir.h>
51*0b459c2cSDavid du Colombier # endif
52*0b459c2cSDavid du Colombier #endif
53*0b459c2cSDavid du Colombier 
54*0b459c2cSDavid du Colombier #if CLOSEDIR_VOID
55*0b459c2cSDavid du Colombier /* Fake a return value. */
56*0b459c2cSDavid du Colombier # define CLOSEDIR(d) (closedir (d), 0)
57*0b459c2cSDavid du Colombier #else
58*0b459c2cSDavid du Colombier # define CLOSEDIR(d) closedir (d)
59*0b459c2cSDavid du Colombier #endif
60*0b459c2cSDavid du Colombier 
61*0b459c2cSDavid du Colombier #if STDC_HEADERS
62*0b459c2cSDavid du Colombier # include <stdlib.h>
63*0b459c2cSDavid du Colombier #else
64*0b459c2cSDavid du Colombier char *malloc ();
65*0b459c2cSDavid du Colombier #endif
66*0b459c2cSDavid du Colombier 
67*0b459c2cSDavid du Colombier #if HAVE_DIRENT_H || HAVE_NDIR_H || HAVE_SYS_DIR_H || HAVE_SYS_NDIR_H
68*0b459c2cSDavid du Colombier # define HAVE_DIR 1
69*0b459c2cSDavid du Colombier #else
70*0b459c2cSDavid du Colombier # define HAVE_DIR 0
71*0b459c2cSDavid du Colombier #endif
72*0b459c2cSDavid du Colombier 
73*0b459c2cSDavid du Colombier #if HAVE_LIMITS_H
74*0b459c2cSDavid du Colombier # include <limits.h>
75*0b459c2cSDavid du Colombier #endif
76*0b459c2cSDavid du Colombier #ifndef CHAR_BIT
77*0b459c2cSDavid du Colombier #define CHAR_BIT 8
78*0b459c2cSDavid du Colombier #endif
79*0b459c2cSDavid du Colombier /* Upper bound on the string length of an integer converted to string.
80*0b459c2cSDavid du Colombier    302 / 1000 is ceil (log10 (2.0)).  Subtract 1 for the sign bit;
81*0b459c2cSDavid du Colombier    add 1 for integer division truncation; add 1 more for a minus sign.  */
82*0b459c2cSDavid du Colombier #define INT_STRLEN_BOUND(t) ((sizeof (t) * CHAR_BIT - 1) * 302 / 1000 + 2)
83*0b459c2cSDavid du Colombier 
84*0b459c2cSDavid du Colombier /* ISDIGIT differs from isdigit, as follows:
85*0b459c2cSDavid du Colombier    - Its arg may be any int or unsigned int; it need not be an unsigned char.
86*0b459c2cSDavid du Colombier    - It's guaranteed to evaluate its argument exactly once.
87*0b459c2cSDavid du Colombier    - It's typically faster.
88*0b459c2cSDavid du Colombier    Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
89*0b459c2cSDavid du Colombier    only '0' through '9' are digits.  Prefer ISDIGIT to isdigit unless
90*0b459c2cSDavid du Colombier    it's important to use the locale's definition of `digit' even when the
91*0b459c2cSDavid du Colombier    host does not conform to Posix.  */
92*0b459c2cSDavid du Colombier #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
93*0b459c2cSDavid du Colombier 
94*0b459c2cSDavid du Colombier #if D_INO_IN_DIRENT
95*0b459c2cSDavid du Colombier # define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0)
96*0b459c2cSDavid du Colombier #else
97*0b459c2cSDavid du Colombier # define REAL_DIR_ENTRY(dp) 1
98*0b459c2cSDavid du Colombier #endif
99*0b459c2cSDavid du Colombier 
100*0b459c2cSDavid du Colombier /* Which type of backup file names are generated. */
101*0b459c2cSDavid du Colombier enum backup_type backup_type = none;
102*0b459c2cSDavid du Colombier 
103*0b459c2cSDavid du Colombier /* The extension added to file names to produce a simple (as opposed
104*0b459c2cSDavid du Colombier    to numbered) backup file name. */
105*0b459c2cSDavid du Colombier const char *simple_backup_suffix = ".orig";
106*0b459c2cSDavid du Colombier 
107*0b459c2cSDavid du Colombier static int max_backup_version __BACKUPFILE_P ((const char *, const char *));
108*0b459c2cSDavid du Colombier static int version_number __BACKUPFILE_P ((const char *, const char *, size_t));
109*0b459c2cSDavid du Colombier 
110*0b459c2cSDavid du Colombier /* Return the name of the new backup file for file FILE,
111*0b459c2cSDavid du Colombier    allocated with malloc.  Return 0 if out of memory.
112*0b459c2cSDavid du Colombier    FILE must not end with a '/' unless it is the root directory.
113*0b459c2cSDavid du Colombier    Do not call this function if backup_type == none. */
114*0b459c2cSDavid du Colombier 
115*0b459c2cSDavid du Colombier char *
find_backup_file_name(file)116*0b459c2cSDavid du Colombier find_backup_file_name (file)
117*0b459c2cSDavid du Colombier      const char *file;
118*0b459c2cSDavid du Colombier {
119*0b459c2cSDavid du Colombier   size_t backup_suffix_size_max;
120*0b459c2cSDavid du Colombier   size_t file_len = strlen (file);
121*0b459c2cSDavid du Colombier   size_t numbered_suffix_size_max = INT_STRLEN_BOUND (int) + 4;
122*0b459c2cSDavid du Colombier   char *s;
123*0b459c2cSDavid du Colombier   const char *suffix = simple_backup_suffix;
124*0b459c2cSDavid du Colombier 
125*0b459c2cSDavid du Colombier   /* Allow room for simple or `.~N~' backups.  */
126*0b459c2cSDavid du Colombier   backup_suffix_size_max = strlen (simple_backup_suffix) + 1;
127*0b459c2cSDavid du Colombier   if (HAVE_DIR && backup_suffix_size_max < numbered_suffix_size_max)
128*0b459c2cSDavid du Colombier     backup_suffix_size_max = numbered_suffix_size_max;
129*0b459c2cSDavid du Colombier 
130*0b459c2cSDavid du Colombier   s = malloc (file_len + backup_suffix_size_max + numbered_suffix_size_max);
131*0b459c2cSDavid du Colombier   if (s)
132*0b459c2cSDavid du Colombier     {
133*0b459c2cSDavid du Colombier       strcpy (s, file);
134*0b459c2cSDavid du Colombier 
135*0b459c2cSDavid du Colombier #if HAVE_DIR
136*0b459c2cSDavid du Colombier       if (backup_type != simple)
137*0b459c2cSDavid du Colombier 	{
138*0b459c2cSDavid du Colombier 	  int highest_backup;
139*0b459c2cSDavid du Colombier 	  size_t dir_len = base_name (s) - s;
140*0b459c2cSDavid du Colombier 
141*0b459c2cSDavid du Colombier 	  strcpy (s + dir_len, ".");
142*0b459c2cSDavid du Colombier 	  highest_backup = max_backup_version (file + dir_len, s);
143*0b459c2cSDavid du Colombier 	  if (! (backup_type == numbered_existing && highest_backup == 0))
144*0b459c2cSDavid du Colombier 	    {
145*0b459c2cSDavid du Colombier 	      char *numbered_suffix = s + (file_len + backup_suffix_size_max);
146*0b459c2cSDavid du Colombier 	      sprintf (numbered_suffix, ".~%d~", highest_backup + 1);
147*0b459c2cSDavid du Colombier 	      suffix = numbered_suffix;
148*0b459c2cSDavid du Colombier 	    }
149*0b459c2cSDavid du Colombier 	  strcpy (s, file);
150*0b459c2cSDavid du Colombier 	}
151*0b459c2cSDavid du Colombier #endif /* HAVE_DIR */
152*0b459c2cSDavid du Colombier 
153*0b459c2cSDavid du Colombier       addext (s, suffix, '~');
154*0b459c2cSDavid du Colombier     }
155*0b459c2cSDavid du Colombier   return s;
156*0b459c2cSDavid du Colombier }
157*0b459c2cSDavid du Colombier 
158*0b459c2cSDavid du Colombier #if HAVE_DIR
159*0b459c2cSDavid du Colombier 
160*0b459c2cSDavid du Colombier /* Return the number of the highest-numbered backup file for file
161*0b459c2cSDavid du Colombier    FILE in directory DIR.  If there are no numbered backups
162*0b459c2cSDavid du Colombier    of FILE in DIR, or an error occurs reading DIR, return 0.
163*0b459c2cSDavid du Colombier    */
164*0b459c2cSDavid du Colombier 
165*0b459c2cSDavid du Colombier static int
max_backup_version(file,dir)166*0b459c2cSDavid du Colombier max_backup_version (file, dir)
167*0b459c2cSDavid du Colombier      const char *file;
168*0b459c2cSDavid du Colombier      const char *dir;
169*0b459c2cSDavid du Colombier {
170*0b459c2cSDavid du Colombier   DIR *dirp;
171*0b459c2cSDavid du Colombier   struct dirent *dp;
172*0b459c2cSDavid du Colombier   int highest_version;
173*0b459c2cSDavid du Colombier   int this_version;
174*0b459c2cSDavid du Colombier   size_t file_name_length;
175*0b459c2cSDavid du Colombier 
176*0b459c2cSDavid du Colombier   dirp = opendir (dir);
177*0b459c2cSDavid du Colombier   if (!dirp)
178*0b459c2cSDavid du Colombier     return 0;
179*0b459c2cSDavid du Colombier 
180*0b459c2cSDavid du Colombier   highest_version = 0;
181*0b459c2cSDavid du Colombier   file_name_length = strlen (file);
182*0b459c2cSDavid du Colombier 
183*0b459c2cSDavid du Colombier   while ((dp = readdir (dirp)) != 0)
184*0b459c2cSDavid du Colombier     {
185*0b459c2cSDavid du Colombier       if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) < file_name_length + 4)
186*0b459c2cSDavid du Colombier 	continue;
187*0b459c2cSDavid du Colombier 
188*0b459c2cSDavid du Colombier       this_version = version_number (file, dp->d_name, file_name_length);
189*0b459c2cSDavid du Colombier       if (this_version > highest_version)
190*0b459c2cSDavid du Colombier 	highest_version = this_version;
191*0b459c2cSDavid du Colombier     }
192*0b459c2cSDavid du Colombier   if (CLOSEDIR (dirp))
193*0b459c2cSDavid du Colombier     return 0;
194*0b459c2cSDavid du Colombier   return highest_version;
195*0b459c2cSDavid du Colombier }
196*0b459c2cSDavid du Colombier 
197*0b459c2cSDavid du Colombier /* If BACKUP is a numbered backup of BASE, return its version number;
198*0b459c2cSDavid du Colombier    otherwise return 0.  BASE_LENGTH is the length of BASE.
199*0b459c2cSDavid du Colombier    */
200*0b459c2cSDavid du Colombier 
201*0b459c2cSDavid du Colombier static int
version_number(base,backup,base_length)202*0b459c2cSDavid du Colombier version_number (base, backup, base_length)
203*0b459c2cSDavid du Colombier      const char *base;
204*0b459c2cSDavid du Colombier      const char *backup;
205*0b459c2cSDavid du Colombier      size_t base_length;
206*0b459c2cSDavid du Colombier {
207*0b459c2cSDavid du Colombier   int version;
208*0b459c2cSDavid du Colombier   const char *p;
209*0b459c2cSDavid du Colombier 
210*0b459c2cSDavid du Colombier   version = 0;
211*0b459c2cSDavid du Colombier   if (strncmp (base, backup, base_length) == 0
212*0b459c2cSDavid du Colombier       && backup[base_length] == '.'
213*0b459c2cSDavid du Colombier       && backup[base_length + 1] == '~')
214*0b459c2cSDavid du Colombier     {
215*0b459c2cSDavid du Colombier       for (p = &backup[base_length + 2]; ISDIGIT (*p); ++p)
216*0b459c2cSDavid du Colombier 	version = version * 10 + *p - '0';
217*0b459c2cSDavid du Colombier       if (p[0] != '~' || p[1])
218*0b459c2cSDavid du Colombier 	version = 0;
219*0b459c2cSDavid du Colombier     }
220*0b459c2cSDavid du Colombier   return version;
221*0b459c2cSDavid du Colombier }
222*0b459c2cSDavid du Colombier #endif /* HAVE_DIR */
223*0b459c2cSDavid du Colombier 
224*0b459c2cSDavid du Colombier static const char * const backup_args[] =
225*0b459c2cSDavid du Colombier {
226*0b459c2cSDavid du Colombier   "never", "simple", "nil", "existing", "t", "numbered", 0
227*0b459c2cSDavid du Colombier };
228*0b459c2cSDavid du Colombier 
229*0b459c2cSDavid du Colombier static const enum backup_type backup_types[] =
230*0b459c2cSDavid du Colombier {
231*0b459c2cSDavid du Colombier   simple, simple, numbered_existing, numbered_existing, numbered, numbered
232*0b459c2cSDavid du Colombier };
233*0b459c2cSDavid du Colombier 
234*0b459c2cSDavid du Colombier /* Return the type of backup indicated by VERSION.
235*0b459c2cSDavid du Colombier    Unique abbreviations are accepted. */
236*0b459c2cSDavid du Colombier 
237*0b459c2cSDavid du Colombier enum backup_type
get_version(version)238*0b459c2cSDavid du Colombier get_version (version)
239*0b459c2cSDavid du Colombier      const char *version;
240*0b459c2cSDavid du Colombier {
241*0b459c2cSDavid du Colombier   int i;
242*0b459c2cSDavid du Colombier 
243*0b459c2cSDavid du Colombier   if (version == 0 || *version == 0)
244*0b459c2cSDavid du Colombier     return numbered_existing;
245*0b459c2cSDavid du Colombier   i = argmatch (version, backup_args);
246*0b459c2cSDavid du Colombier   if (i < 0)
247*0b459c2cSDavid du Colombier     {
248*0b459c2cSDavid du Colombier       invalid_arg ("version control type", version, i);
249*0b459c2cSDavid du Colombier       exit (2);
250*0b459c2cSDavid du Colombier     }
251*0b459c2cSDavid du Colombier   return backup_types[i];
252*0b459c2cSDavid du Colombier }
253