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