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