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