Home | History | Annotate | Line # | Download | only in patch
backupfile.c revision 1.2
      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 without restriction.
      6 
      7    This program is distributed in the hope that it will be useful,
      8    but WITHOUT ANY WARRANTY; without even the implied warranty of
      9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  */
     10 
     11 /* David MacKenzie <djm (at) ai.mit.edu>.
     12    Some algorithms adapted from GNU Emacs. */
     13 
     14 #ifndef lint
     15 static char rcsid[] = "$Id: backupfile.c,v 1.2 1993/08/02 17:55:13 mycroft Exp $";
     16 #endif /* not lint */
     17 
     18 #include <stdio.h>
     19 #include <ctype.h>
     20 #include <sys/types.h>
     21 #include "backupfile.h"
     22 #include "config.h"
     23 char *index ();
     24 char *rindex ();
     25 char *malloc ();
     26 
     27 #ifdef DIRENT
     28 #include <dirent.h>
     29 #ifdef direct
     30 #undef direct
     31 #endif
     32 #define direct dirent
     33 #define NLENGTH(direct) (strlen((direct)->d_name))
     34 #else /* !DIRENT */
     35 #define NLENGTH(direct) ((direct)->d_namlen)
     36 #ifdef USG
     37 #ifdef SYSNDIR
     38 #include <sys/ndir.h>
     39 #else /* !SYSNDIR */
     40 #include <ndir.h>
     41 #endif /* !SYSNDIR */
     42 #else /* !USG */
     43 #ifdef SYSDIR
     44 #include <sys/dir.h>
     45 #endif /* SYSDIR */
     46 #endif /* !USG */
     47 #endif /* !DIRENT */
     48 
     49 #ifndef isascii
     50 #define ISDIGIT(c) (isdigit ((unsigned char) (c)))
     51 #else
     52 #define ISDIGIT(c) (isascii (c) && isdigit (c))
     53 #endif
     54 
     55 #if defined (HAVE_UNISTD_H)
     56 #include <unistd.h>
     57 #endif
     58 
     59 #if defined (_POSIX_VERSION)
     60 /* POSIX does not require that the d_ino field be present, and some
     61    systems do not provide it. */
     62 #define REAL_DIR_ENTRY(dp) 1
     63 #else
     64 #define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0)
     65 #endif
     66 
     67 /* Which type of backup file names are generated. */
     68 enum backup_type backup_type = none;
     69 
     70 /* The extension added to file names to produce a simple (as opposed
     71    to numbered) backup file name. */
     72 char *simple_backup_suffix = "~";
     73 
     74 char *basename ();
     75 char *dirname ();
     76 static char *concat ();
     77 char *find_backup_file_name ();
     78 static char *make_version_name ();
     79 static int max_backup_version ();
     80 static int version_number ();
     81 
     82 #ifndef NODIR
     83 /* Return the name of the new backup file for file FILE,
     84    allocated with malloc.  Return 0 if out of memory.
     85    FILE must not end with a '/' unless it is the root directory.
     86    Do not call this function if backup_type == none. */
     87 
     88 char *
     89 find_backup_file_name (file)
     90      char *file;
     91 {
     92   char *dir;
     93   char *base_versions;
     94   int highest_backup;
     95 
     96   if (backup_type == simple)
     97     return concat (file, simple_backup_suffix);
     98   base_versions = concat (basename (file), ".~");
     99   if (base_versions == 0)
    100     return 0;
    101   dir = dirname (file);
    102   if (dir == 0)
    103     {
    104       free (base_versions);
    105       return 0;
    106     }
    107   highest_backup = max_backup_version (base_versions, dir);
    108   free (base_versions);
    109   free (dir);
    110   if (backup_type == numbered_existing && highest_backup == 0)
    111     return concat (file, simple_backup_suffix);
    112   return make_version_name (file, highest_backup + 1);
    113 }
    114 
    115 /* Return the number of the highest-numbered backup file for file
    116    FILE in directory DIR.  If there are no numbered backups
    117    of FILE in DIR, or an error occurs reading DIR, return 0.
    118    FILE should already have ".~" appended to it. */
    119 
    120 static int
    121 max_backup_version (file, dir)
    122      char *file, *dir;
    123 {
    124   DIR *dirp;
    125   struct direct *dp;
    126   int highest_version;
    127   int this_version;
    128   int file_name_length;
    129 
    130   dirp = opendir (dir);
    131   if (!dirp)
    132     return 0;
    133 
    134   highest_version = 0;
    135   file_name_length = strlen (file);
    136 
    137   while ((dp = readdir (dirp)) != 0)
    138     {
    139       if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) <= file_name_length)
    140 	continue;
    141 
    142       this_version = version_number (file, dp->d_name, file_name_length);
    143       if (this_version > highest_version)
    144 	highest_version = this_version;
    145     }
    146   closedir (dirp);
    147   return highest_version;
    148 }
    149 
    150 /* Return a string, allocated with malloc, containing
    151    "FILE.~VERSION~".  Return 0 if out of memory. */
    152 
    153 static char *
    154 make_version_name (file, version)
    155      char *file;
    156      int version;
    157 {
    158   char *backup_name;
    159 
    160   backup_name = malloc (strlen (file) + 16);
    161   if (backup_name == 0)
    162     return 0;
    163   sprintf (backup_name, "%s.~%d~", file, version);
    164   return backup_name;
    165 }
    166 
    167 /* If BACKUP is a numbered backup of BASE, return its version number;
    168    otherwise return 0.  BASE_LENGTH is the length of BASE.
    169    BASE should already have ".~" appended to it. */
    170 
    171 static int
    172 version_number (base, backup, base_length)
    173      char *base;
    174      char *backup;
    175      int base_length;
    176 {
    177   int version;
    178   char *p;
    179 
    180   version = 0;
    181   if (!strncmp (base, backup, base_length) && ISDIGIT (backup[base_length]))
    182     {
    183       for (p = &backup[base_length]; ISDIGIT (*p); ++p)
    184 	version = version * 10 + *p - '0';
    185       if (p[0] != '~' || p[1])
    186 	version = 0;
    187     }
    188   return version;
    189 }
    190 
    191 /* Return the newly-allocated concatenation of STR1 and STR2.
    192    If out of memory, return 0. */
    193 
    194 static char *
    195 concat (str1, str2)
    196      char *str1, *str2;
    197 {
    198   char *newstr;
    199   char str1_length = strlen (str1);
    200 
    201   newstr = malloc (str1_length + strlen (str2) + 1);
    202   if (newstr == 0)
    203     return 0;
    204   strcpy (newstr, str1);
    205   strcpy (newstr + str1_length, str2);
    206   return newstr;
    207 }
    208 
    209 /* Return NAME with any leading path stripped off.  */
    210 
    211 char *
    212 basename (name)
    213      char *name;
    214 {
    215   char *base;
    216 
    217   base = rindex (name, '/');
    218   return base ? base + 1 : name;
    219 }
    220 
    221 /* Return the leading directories part of PATH,
    222    allocated with malloc.  If out of memory, return 0.
    223    Assumes that trailing slashes have already been
    224    removed.  */
    225 
    226 char *
    227 dirname (path)
    228      char *path;
    229 {
    230   char *newpath;
    231   char *slash;
    232   int length;    /* Length of result, not including NUL. */
    233 
    234   slash = rindex (path, '/');
    235   if (slash == 0)
    236 	{
    237 	  /* File is in the current directory.  */
    238 	  path = ".";
    239 	  length = 1;
    240 	}
    241   else
    242 	{
    243 	  /* Remove any trailing slashes from result. */
    244 	  while (slash > path && *slash == '/')
    245 		--slash;
    246 
    247 	  length = slash - path + 1;
    248 	}
    249   newpath = malloc (length + 1);
    250   if (newpath == 0)
    251     return 0;
    252   strncpy (newpath, path, length);
    253   newpath[length] = 0;
    254   return newpath;
    255 }
    256 
    257 /* If ARG is an unambiguous match for an element of the
    258    null-terminated array OPTLIST, return the index in OPTLIST
    259    of the matched element, else -1 if it does not match any element
    260    or -2 if it is ambiguous (is a prefix of more than one element). */
    261 
    262 int
    263 argmatch (arg, optlist)
    264      char *arg;
    265      char **optlist;
    266 {
    267   int i;			/* Temporary index in OPTLIST. */
    268   int arglen;			/* Length of ARG. */
    269   int matchind = -1;		/* Index of first nonexact match. */
    270   int ambiguous = 0;		/* If nonzero, multiple nonexact match(es). */
    271 
    272   arglen = strlen (arg);
    273 
    274   /* Test all elements for either exact match or abbreviated matches.  */
    275   for (i = 0; optlist[i]; i++)
    276     {
    277       if (!strncmp (optlist[i], arg, arglen))
    278 	{
    279 	  if (strlen (optlist[i]) == arglen)
    280 	    /* Exact match found.  */
    281 	    return i;
    282 	  else if (matchind == -1)
    283 	    /* First nonexact match found.  */
    284 	    matchind = i;
    285 	  else
    286 	    /* Second nonexact match found.  */
    287 	    ambiguous = 1;
    288 	}
    289     }
    290   if (ambiguous)
    291     return -2;
    292   else
    293     return matchind;
    294 }
    295 
    296 /* Error reporting for argmatch.
    297    KIND is a description of the type of entity that was being matched.
    298    VALUE is the invalid value that was given.
    299    PROBLEM is the return value from argmatch. */
    300 
    301 void
    302 invalid_arg (kind, value, problem)
    303      char *kind;
    304      char *value;
    305      int problem;
    306 {
    307   fprintf (stderr, "patch: ");
    308   if (problem == -1)
    309     fprintf (stderr, "invalid");
    310   else				/* Assume -2. */
    311     fprintf (stderr, "ambiguous");
    312   fprintf (stderr, " %s `%s'\n", kind, value);
    313 }
    314 
    315 static char *backup_args[] =
    316 {
    317   "never", "simple", "nil", "existing", "t", "numbered", 0
    318 };
    319 
    320 static enum backup_type backup_types[] =
    321 {
    322   simple, simple, numbered_existing, numbered_existing, numbered, numbered
    323 };
    324 
    325 /* Return the type of backup indicated by VERSION.
    326    Unique abbreviations are accepted. */
    327 
    328 enum backup_type
    329 get_version (version)
    330      char *version;
    331 {
    332   int i;
    333 
    334   if (version == 0 || *version == 0)
    335     return numbered_existing;
    336   i = argmatch (version, backup_args);
    337   if (i >= 0)
    338     return backup_types[i];
    339   invalid_arg ("version control type", version, i);
    340   exit (1);
    341 }
    342 #endif /* NODIR */
    343