source: trunk/third/patch/backupfile.c @ 9069

Revision 9069, 9.6 KB checked in by ghudson, 28 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r9068, which included commits to RCS files with non-trunk default branches.
Line 
1/* backupfile.c -- make Emacs style backup file names
2   Copyright (C) 1990, 1991, 1992 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/* Written by David MacKenzie <djm@gnu.ai.mit.edu>.
19   Some algorithms adapted from GNU Emacs. */
20
21#include "config.h"
22#include <stdio.h>
23#include <ctype.h>
24#include <sys/types.h>
25#include "backupfile.h"
26#ifdef STDC_HEADERS
27#include <string.h>
28#include <stdlib.h>
29#else
30char *malloc ();
31#endif
32
33#if defined (HAVE_UNISTD_H)
34#include <unistd.h>
35#endif
36
37#if defined(DIRENT) || defined(_POSIX_VERSION)
38#include <dirent.h>
39#define NLENGTH(direct) (strlen((direct)->d_name))
40#else /* not (DIRENT or _POSIX_VERSION) */
41#define dirent direct
42#define NLENGTH(direct) ((direct)->d_namlen)
43#ifdef SYSNDIR
44#include <sys/ndir.h>
45#endif /* SYSNDIR */
46#ifdef SYSDIR
47#include <sys/dir.h>
48#endif /* SYSDIR */
49#ifdef NDIR
50#include <ndir.h>
51#endif /* NDIR */
52#endif /* DIRENT or _POSIX_VERSION */
53
54#ifndef isascii
55#define ISDIGIT(c) (isdigit ((unsigned char) (c)))
56#else
57#define ISDIGIT(c) (isascii (c) && isdigit (c))
58#endif
59
60#if defined (_POSIX_VERSION)
61/* POSIX does not require that the d_ino field be present, and some
62   systems do not provide it. */
63#define REAL_DIR_ENTRY(dp) 1
64#else
65#define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0)
66#endif
67
68/* Which type of backup file names are generated. */
69enum backup_type backup_type = none;
70
71/* The extension added to file names to produce a simple (as opposed
72   to numbered) backup file name. */
73char *simple_backup_suffix = "~";
74
75char *basename ();
76char *dirname ();
77static char *concat ();
78char *find_backup_file_name ();
79static char *make_version_name ();
80static int max_backup_version ();
81static int version_number ();
82
83/* Return NAME with any leading path stripped off.  */
84
85char *
86basename (name)
87     char *name;
88{
89  char *r = name, *p = name;
90
91  while (*p)
92    if (*p++ == '/')
93      r = p;
94  return r;
95}
96
97#ifndef NODIR
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
103char *
104find_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    {
113      char *s = malloc (strlen (file) + strlen (simple_backup_suffix) + 1);
114      strcpy (s, file);
115      addext (s, simple_backup_suffix, '~');
116      return s;
117    }
118  base_versions = concat (basename (file), ".~");
119  if (base_versions == 0)
120    return 0;
121  dir = dirname (file);
122  if (dir == 0)
123    {
124      free (base_versions);
125      return 0;
126    }
127  highest_backup = max_backup_version (base_versions, dir);
128  free (base_versions);
129  free (dir);
130  if (backup_type == numbered_existing && highest_backup == 0)
131    return concat (file, simple_backup_suffix);
132  return make_version_name (file, highest_backup + 1);
133}
134
135/* Return the number of the highest-numbered backup file for file
136   FILE in directory DIR.  If there are no numbered backups
137   of FILE in DIR, or an error occurs reading DIR, return 0.
138   FILE should already have ".~" appended to it. */
139
140static int
141max_backup_version (file, dir)
142     char *file, *dir;
143{
144  DIR *dirp;
145  struct dirent *dp;
146  int highest_version;
147  int this_version;
148  int file_name_length;
149 
150  dirp = opendir (dir);
151  if (!dirp)
152    return 0;
153 
154  highest_version = 0;
155  file_name_length = strlen (file);
156
157  while ((dp = readdir (dirp)) != 0)
158    {
159      if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) <= file_name_length)
160        continue;
161     
162      this_version = version_number (file, dp->d_name, file_name_length);
163      if (this_version > highest_version)
164        highest_version = this_version;
165    }
166  closedir (dirp);
167  return highest_version;
168}
169
170/* Return a string, allocated with malloc, containing
171   "FILE.~VERSION~".  Return 0 if out of memory. */
172
173static char *
174make_version_name (file, version)
175     char *file;
176     int version;
177{
178  char *backup_name;
179
180  backup_name = malloc (strlen (file) + 16);
181  if (backup_name == 0)
182    return 0;
183  sprintf (backup_name, "%s.~%d~", file, version);
184  return backup_name;
185}
186
187/* If BACKUP is a numbered backup of BASE, return its version number;
188   otherwise return 0.  BASE_LENGTH is the length of BASE.
189   BASE should already have ".~" appended to it. */
190
191static int
192version_number (base, backup, base_length)
193     char *base;
194     char *backup;
195     int base_length;
196{
197  int version;
198  char *p;
199 
200  version = 0;
201  if (!strncmp (base, backup, base_length) && ISDIGIT (backup[base_length]))
202    {
203      for (p = &backup[base_length]; ISDIGIT (*p); ++p)
204        version = version * 10 + *p - '0';
205      if (p[0] != '~' || p[1])
206        version = 0;
207    }
208  return version;
209}
210
211/* Return the newly-allocated concatenation of STR1 and STR2.
212   If out of memory, return 0. */
213
214static char *
215concat (str1, str2)
216     char *str1, *str2;
217{
218  char *newstr;
219  char str1_length = strlen (str1);
220
221  newstr = malloc (str1_length + strlen (str2) + 1);
222  if (newstr == 0)
223    return 0;
224  strcpy (newstr, str1);
225  strcpy (newstr + str1_length, str2);
226  return newstr;
227}
228
229/* Return the leading directories part of PATH,
230   allocated with malloc.  If out of memory, return 0.
231   Assumes that trailing slashes have already been
232   removed.  */
233
234char *
235dirname (path)
236     char *path;
237{
238  char *newpath;
239  char *slash;
240  int length;    /* Length of result, not including NUL. */
241
242  slash = basename (path);
243  if (slash == path)
244        {
245          /* File is in the current directory.  */
246          path = ".";
247          length = 1;
248        }
249  else
250        {
251          /* Remove any trailing slashes from result. */
252          while (*--slash == '/' && slash > path)
253            ;
254
255          length = slash - path + 1;
256        }
257  newpath = malloc (length + 1);
258  if (newpath == 0)
259    return 0;
260  strncpy (newpath, path, length);
261  newpath[length] = 0;
262  return newpath;
263}
264
265/* If ARG is an unambiguous match for an element of the
266   null-terminated array OPTLIST, return the index in OPTLIST
267   of the matched element, else -1 if it does not match any element
268   or -2 if it is ambiguous (is a prefix of more than one element). */
269
270int
271argmatch (arg, optlist)
272     char *arg;
273     char **optlist;
274{
275  int i;                        /* Temporary index in OPTLIST. */
276  int arglen;                   /* Length of ARG. */
277  int matchind = -1;            /* Index of first nonexact match. */
278  int ambiguous = 0;            /* If nonzero, multiple nonexact match(es). */
279 
280  arglen = strlen (arg);
281 
282  /* Test all elements for either exact match or abbreviated matches.  */
283  for (i = 0; optlist[i]; i++)
284    {
285      if (!strncmp (optlist[i], arg, arglen))
286        {
287          if (strlen (optlist[i]) == arglen)
288            /* Exact match found.  */
289            return i;
290          else if (matchind == -1)
291            /* First nonexact match found.  */
292            matchind = i;
293          else
294            /* Second nonexact match found.  */
295            ambiguous = 1;
296        }
297    }
298  if (ambiguous)
299    return -2;
300  else
301    return matchind;
302}
303
304/* Error reporting for argmatch.
305   KIND is a description of the type of entity that was being matched.
306   VALUE is the invalid value that was given.
307   PROBLEM is the return value from argmatch. */
308
309void
310invalid_arg (kind, value, problem)
311     char *kind;
312     char *value;
313     int problem;
314{
315  fprintf (stderr, "patch: ");
316  if (problem == -1)
317    fprintf (stderr, "invalid");
318  else                          /* Assume -2. */
319    fprintf (stderr, "ambiguous");
320  fprintf (stderr, " %s `%s'\n", kind, value);
321}
322
323static char *backup_args[] =
324{
325  "never", "simple", "nil", "existing", "t", "numbered", 0
326};
327
328static enum backup_type backup_types[] =
329{
330  simple, simple, numbered_existing, numbered_existing, numbered, numbered
331};
332
333/* Return the type of backup indicated by VERSION.
334   Unique abbreviations are accepted. */
335
336enum backup_type
337get_version (version)
338     char *version;
339{
340  int i;
341
342  if (version == 0 || *version == 0)
343    return numbered_existing;
344  i = argmatch (version, backup_args);
345  if (i >= 0)
346    return backup_types[i];
347  invalid_arg ("version control type", version, i);
348  exit (1);
349}
350#endif /* NODIR */
351
352/* Append to FILENAME the extension EXT, unless the result would be too long,
353   in which case just append the character E.  */
354
355void
356addext (filename, ext, e)
357     char *filename, *ext;
358     int e;
359{
360  char *s = basename (filename);
361  int slen = strlen (s), extlen = strlen (ext);
362  long slen_max = -1;
363
364#if HAVE_PATHCONF && defined (_PC_NAME_MAX)
365#ifndef _POSIX_NAME_MAX
366#define _POSIX_NAME_MAX 14
367#endif
368  if (slen + extlen <= _POSIX_NAME_MAX)
369    /* The file name is so short there's no need to call pathconf.  */
370    slen_max = _POSIX_NAME_MAX;
371  else if (s == filename)
372    slen_max = pathconf (".", _PC_NAME_MAX);
373  else
374    {
375      char c = *s;
376      *s = 0;
377      slen_max = pathconf (filename, _PC_NAME_MAX);
378      *s = c;
379    }
380#endif
381  if (slen_max == -1) {
382#if HAVE_LONG_FILE_NAMES
383    slen_max = 255;
384#else
385    slen_max = 14;
386#endif
387  }
388  if (slen + extlen <= slen_max)
389    strcpy (s + slen, ext);
390  else
391    {
392      if (slen_max <= slen) {
393        /* Try to preserve difference between .h .c etc.  */
394        if (slen == slen_max && s[slen - 2] == '.')
395          s[slen - 2] = s[slen - 1];
396
397        slen = slen_max - 1;
398      }
399      s[slen] = e;
400      s[slen + 1] = 0;
401    }
402}
Note: See TracBrowser for help on using the repository browser.