1184610Salfred/* backupfile.c -- make Emacs style backup file names 2184610Salfred Copyright (C) 1990-1999, 2000-2003, 2005-2006 Free Software Foundation, Inc. 3184610Salfred 4184610Salfred This program is free software; you can redistribute it and/or modify 5184610Salfred it under the terms of the GNU General Public License as published by 6184610Salfred the Free Software Foundation; either version 2, or (at your option) 7184610Salfred any later version. 8184610Salfred 9184610Salfred This program is distributed in the hope that it will be useful, 10184610Salfred but WITHOUT ANY WARRANTY; without even the implied warranty of 11184610Salfred MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12184610Salfred GNU General Public License for more details. 13184610Salfred 14184610Salfred You should have received a copy of the GNU General Public License 15184610Salfred along with this program; see the file COPYING. 16184610Salfred If not, write to the Free Software Foundation, 17184610Salfred 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 18184610Salfred 19184610Salfred/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. 20184610Salfred Some algorithms adapted from GNU Emacs. */ 21184610Salfred 22184610Salfred#include <config.h> 23184610Salfred 24184610Salfred#include "argmatch.h" 25184610Salfred#include "backupfile.h" 26184610Salfred 27184610Salfred#include <stdio.h> 28184610Salfred#include <sys/types.h> 29184610Salfred#if HAVE_STRING_H 30184610Salfred# include <string.h> 31184610Salfred#else 32184610Salfred# include <strings.h> 33184610Salfred#endif 34184610Salfred 35184610Salfred#if HAVE_DIRENT_H 36184610Salfred# include <dirent.h> 37184610Salfred#endif 38184610Salfred 39184610Salfred#include <stdlib.h> 40184610Salfred 41184610Salfred#include "basename.h" 42184610Salfred 43184610Salfred#if HAVE_DIRENT_H 44184610Salfred# define HAVE_DIR 1 45184610Salfred#else 46184610Salfred# define HAVE_DIR 0 47184610Salfred#endif 48248236Shselasky 49248236Shselasky#include <limits.h> 50248236Shselasky 51248236Shselasky/* Upper bound on the string length of an integer converted to string. 52184610Salfred 302 / 1000 is ceil (log10 (2.0)). Subtract 1 for the sign bit; 53184610Salfred add 1 for integer division truncation; add 1 more for a minus sign. */ 54184610Salfred#define INT_STRLEN_BOUND(t) ((sizeof (t) * CHAR_BIT - 1) * 302 / 1000 + 2) 55184610Salfred 56184610Salfred/* ISDIGIT differs from isdigit, as follows: 57184610Salfred - Its arg may be any int or unsigned int; it need not be an unsigned char. 58184610Salfred - It's guaranteed to evaluate its argument exactly once. 59184610Salfred - It's typically faster. 60184610Salfred Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that 61184610Salfred only '0' through '9' are digits. Prefer ISDIGIT to isdigit unless 62184610Salfred it's important to use the locale's definition of `digit' even when the 63184610Salfred host does not conform to Posix. */ 64184610Salfred#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9) 65184610Salfred 66184610Salfred#if D_INO_IN_DIRENT 67184610Salfred# define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0) 68184610Salfred#else 69184610Salfred# define REAL_DIR_ENTRY(dp) 1 70185087Salfred#endif 71184610Salfred 72184610Salfred/* The extension added to file names to produce a simple (as opposed 73184610Salfred to numbered) backup file name. */ 74184610Salfredconst char *simple_backup_suffix = "~"; 75184610Salfred 76184610Salfred#if HAVE_DIR 77184610Salfredstatic int max_backup_version (const char *, const char *); 78184610Salfredstatic int version_number (const char *, const char *, size_t); 79184610Salfred#endif 80184610Salfred 81184610Salfred/* Return the name of the new backup file for file FILE, 82184610Salfred allocated with malloc. Return 0 if out of memory. 83184610Salfred FILE must not end with a '/' unless it is the root directory. 84184610Salfred Do not call this function if backup_type == none. */ 85184610Salfred 86184610Salfredchar * 87184610Salfredfind_backup_file_name (const char *file, enum backup_type backup_type) 88184610Salfred{ 89184610Salfred size_t backup_suffix_size_max; 90184610Salfred size_t file_len = strlen (file); 91184610Salfred size_t numbered_suffix_size_max = INT_STRLEN_BOUND (int) + 4; 92184610Salfred char *s; 93184610Salfred const char *suffix = simple_backup_suffix; 94184610Salfred 95184610Salfred /* Allow room for simple or `.~N~' backups. */ 96184610Salfred backup_suffix_size_max = strlen (simple_backup_suffix) + 1; 97184610Salfred if (HAVE_DIR && backup_suffix_size_max < numbered_suffix_size_max) 98184610Salfred backup_suffix_size_max = numbered_suffix_size_max; 99184610Salfred 100184610Salfred s = malloc (file_len + backup_suffix_size_max + numbered_suffix_size_max); 101184610Salfred if (s) 102184610Salfred { 103184610Salfred strcpy (s, file); 104184610Salfred 105184610Salfred#if HAVE_DIR 106184610Salfred if (backup_type != simple) 107184610Salfred { 108184610Salfred int highest_backup; 109184610Salfred size_t dir_len = basename (s) - s; 110184610Salfred 111184610Salfred strcpy (s + dir_len, "."); 112184610Salfred highest_backup = max_backup_version (file + dir_len, s); 113184610Salfred if (! (backup_type == numbered_existing && highest_backup == 0)) 114184610Salfred { 115184610Salfred char *numbered_suffix = s + (file_len + backup_suffix_size_max); 116184610Salfred sprintf (numbered_suffix, ".~%d~", highest_backup + 1); 117184610Salfred suffix = numbered_suffix; 118184610Salfred } 119184610Salfred strcpy (s, file); 120184610Salfred } 121184610Salfred#endif /* HAVE_DIR */ 122184610Salfred 123184610Salfred addext (s, suffix, '~'); 124184610Salfred } 125184610Salfred return s; 126184610Salfred} 127184610Salfred 128184610Salfred#if HAVE_DIR 129184610Salfred 130184610Salfred/* Return the number of the highest-numbered backup file for file 131184610Salfred FILE in directory DIR. If there are no numbered backups 132184610Salfred of FILE in DIR, or an error occurs reading DIR, return 0. 133184610Salfred */ 134184610Salfred 135184610Salfredstatic int 136184610Salfredmax_backup_version (const char *file, const char *dir) 137184610Salfred{ 138184610Salfred DIR *dirp; 139184610Salfred struct dirent *dp; 140184610Salfred int highest_version; 141184610Salfred int this_version; 142184610Salfred size_t file_name_length; 143184610Salfred 144184610Salfred dirp = opendir (dir); 145184610Salfred if (!dirp) 146184610Salfred return 0; 147184610Salfred 148184610Salfred highest_version = 0; 149184610Salfred file_name_length = strlen (file); 150184610Salfred 151184610Salfred while ((dp = readdir (dirp)) != 0) 152184610Salfred { 153184610Salfred if (!REAL_DIR_ENTRY (dp) || strlen (dp->d_name) < file_name_length + 4) 154184610Salfred continue; 155184610Salfred 156184610Salfred this_version = version_number (file, dp->d_name, file_name_length); 157184610Salfred if (this_version > highest_version) 158184610Salfred highest_version = this_version; 159184610Salfred } 160184610Salfred if (closedir (dirp)) 161184610Salfred return 0; 162184610Salfred return highest_version; 163184610Salfred} 164184610Salfred 165184610Salfred/* If BACKUP is a numbered backup of BASE, return its version number; 166184610Salfred otherwise return 0. BASE_LENGTH is the length of BASE. 167184610Salfred */ 168184610Salfred 169184610Salfredstatic int 170184610Salfredversion_number (const char *base, const char *backup, size_t base_length) 171184610Salfred{ 172184610Salfred int version; 173184610Salfred const char *p; 174184610Salfred 175184610Salfred version = 0; 176184610Salfred if (strncmp (base, backup, base_length) == 0 177184610Salfred && backup[base_length] == '.' 178184610Salfred && backup[base_length + 1] == '~') 179184610Salfred { 180184610Salfred for (p = &backup[base_length + 2]; ISDIGIT (*p); ++p) 181184610Salfred version = version * 10 + *p - '0'; 182184610Salfred if (p[0] != '~' || p[1]) 183184610Salfred version = 0; 184184610Salfred } 185184610Salfred return version; 186184610Salfred} 187184610Salfred#endif /* HAVE_DIR */ 188184610Salfred 189184610Salfredstatic const char * const backup_args[] = 190184610Salfred{ 191184610Salfred /* In a series of synonyms, present the most meaning full first, so 192184610Salfred that argmatch_valid be more readable. */ 193184610Salfred "none", "off", 194184610Salfred "simple", "never", 195184610Salfred "existing", "nil", 196184610Salfred "numbered", "t", 197184610Salfred 0 198184610Salfred}; 199184610Salfred 200184610Salfredstatic const enum backup_type backup_types[] = 201184610Salfred{ 202184610Salfred none, none, 203184610Salfred simple, simple, 204184610Salfred numbered_existing, numbered_existing, 205184610Salfred numbered, numbered 206184610Salfred}; 207184610Salfred 208184610Salfred/* Return the type of backup specified by VERSION. 209184610Salfred If VERSION is NULL or the empty string, return numbered_existing. 210184610Salfred If VERSION is invalid or ambiguous, fail with a diagnostic appropriate 211184610Salfred for the specified CONTEXT. Unambiguous abbreviations are accepted. */ 212184610Salfred 213184610Salfredenum backup_type 214184610Salfredget_version (const char *context, const char *version) 215184610Salfred{ 216184610Salfred if (version == 0 || *version == 0) 217184610Salfred return numbered_existing; 218184610Salfred else 219184610Salfred return XARGMATCH (context, version, backup_args, backup_types); 220184610Salfred} 221184610Salfred 222184610Salfred 223184610Salfred/* Return the type of backup specified by VERSION. 224184610Salfred If VERSION is NULL, use the value of the envvar VERSION_CONTROL. 225184610Salfred If the specified string is invalid or ambiguous, fail with a diagnostic 226184610Salfred appropriate for the specified CONTEXT. 227184610Salfred Unambiguous abbreviations are accepted. */ 228184610Salfred 229184610Salfredenum backup_type 230184610Salfredxget_version (const char *context, const char *version) 231184610Salfred{ 232184610Salfred if (version && *version) 233184610Salfred return get_version (context, version); 234184610Salfred else 235184610Salfred return get_version ("$VERSION_CONTROL", getenv ("VERSION_CONTROL")); 236184610Salfred} 237184610Salfred