backupfile.c revision 302408
1255932Salfred/*- 2272407Shselasky * Copyright (C) 1990 Free Software Foundation, Inc. 3255932Salfred * 4255932Salfred * This program is free software; you can redistribute it and/or modify it 5255932Salfred * without restriction. 6255932Salfred * 7255932Salfred * This program is distributed in the hope that it will be useful, but WITHOUT 8255932Salfred * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 9255932Salfred * FITNESS FOR A PARTICULAR PURPOSE. 10255932Salfred * 11255932Salfred * backupfile.c -- make Emacs style backup file names 12255932Salfred * 13255932Salfred * David MacKenzie <djm@ai.mit.edu>. Some algorithms adapted from GNU Emacs. 14255932Salfred * 15255932Salfred * $OpenBSD: backupfile.c,v 1.20 2009/10/27 23:59:41 deraadt Exp $ 16255932Salfred * $FreeBSD: stable/11/usr.bin/patch/backupfile.c 285772 2015-07-21 22:57:27Z cem $ 17255932Salfred */ 18255932Salfred 19255932Salfred#include <ctype.h> 20255932Salfred#include <dirent.h> 21255932Salfred#include <libgen.h> 22255932Salfred#include <stdio.h> 23255932Salfred#include <stdlib.h> 24255932Salfred#include <string.h> 25255932Salfred#include <unistd.h> 26255932Salfred 27255932Salfred#include "backupfile.h" 28255932Salfred 29255932Salfred 30255932Salfred#define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c)) 31255932Salfred 32255932Salfred/* Which type of backup file names are generated. */ 33255932Salfredenum backup_type backup_type = none; 34255932Salfred 35255932Salfred/* 36255932Salfred * The extension added to file names to produce a simple (as opposed to 37255932Salfred * numbered) backup file name. 38255932Salfred */ 39255932Salfredconst char *simple_backup_suffix = "~"; 40255932Salfred 41255932Salfredstatic char *concat(const char *, const char *); 42255932Salfredstatic char *make_version_name(const char *, int); 43255932Salfredstatic int max_backup_version(const char *, const char *); 44255932Salfredstatic int version_number(const char *, const char *, size_t); 45255932Salfredstatic int argmatch(const char *, const char **); 46255932Salfredstatic void invalid_arg(const char *, const char *, int); 47255932Salfred 48255932Salfred/* 49255932Salfred * Return the name of the new backup file for file FILE, allocated with 50255932Salfred * malloc. Return 0 if out of memory. FILE must not end with a '/' unless it 51255932Salfred * is the root directory. Do not call this function if backup_type == none. 52255932Salfred */ 53255932Salfredchar * 54255932Salfredfind_backup_file_name(const char *file) 55255932Salfred{ 56255932Salfred char *dir, *base_versions, *tmp_file; 57255932Salfred int highest_backup; 58255932Salfred 59255932Salfred if (backup_type == simple) 60255932Salfred return concat(file, simple_backup_suffix); 61255932Salfred tmp_file = strdup(file); 62255932Salfred if (tmp_file == NULL) 63255932Salfred return NULL; 64255932Salfred base_versions = concat(basename(tmp_file), ".~"); 65255932Salfred free(tmp_file); 66255932Salfred if (base_versions == NULL) 67255932Salfred return NULL; 68255932Salfred tmp_file = strdup(file); 69255932Salfred if (tmp_file == NULL) { 70255932Salfred free(base_versions); 71255932Salfred return NULL; 72255932Salfred } 73255932Salfred dir = dirname(tmp_file); 74255932Salfred if (dir == NULL) { 75255932Salfred free(base_versions); 76255932Salfred free(tmp_file); 77255932Salfred return NULL; 78255932Salfred } 79255932Salfred highest_backup = max_backup_version(base_versions, dir); 80255932Salfred free(base_versions); 81255932Salfred free(tmp_file); 82255932Salfred if (backup_type == numbered_existing && highest_backup == 0) 83255932Salfred return concat(file, simple_backup_suffix); 84255932Salfred return make_version_name(file, highest_backup + 1); 85255932Salfred} 86255932Salfred 87255932Salfred/* 88255932Salfred * Return the number of the highest-numbered backup file for file FILE in 89255932Salfred * directory DIR. If there are no numbered backups of FILE in DIR, or an 90255932Salfred * error occurs reading DIR, return 0. FILE should already have ".~" appended 91255932Salfred * to it. 92255932Salfred */ 93255932Salfredstatic int 94255932Salfredmax_backup_version(const char *file, const char *dir) 95255932Salfred{ 96255932Salfred DIR *dirp; 97255932Salfred struct dirent *dp; 98255932Salfred int highest_version, this_version; 99255932Salfred size_t file_name_length; 100255932Salfred 101255932Salfred dirp = opendir(dir); 102255932Salfred if (dirp == NULL) 103255932Salfred return 0; 104255932Salfred 105255932Salfred highest_version = 0; 106255932Salfred file_name_length = strlen(file); 107255932Salfred 108255932Salfred while ((dp = readdir(dirp)) != NULL) { 109255932Salfred if (dp->d_namlen <= file_name_length) 110255932Salfred continue; 111255932Salfred 112255932Salfred this_version = version_number(file, dp->d_name, file_name_length); 113255932Salfred if (this_version > highest_version) 114255932Salfred highest_version = this_version; 115255932Salfred } 116255932Salfred closedir(dirp); 117255932Salfred return highest_version; 118255932Salfred} 119255932Salfred 120255932Salfred/* 121255932Salfred * Return a string, allocated with malloc, containing "FILE.~VERSION~". 122255932Salfred * Return 0 if out of memory. 123255932Salfred */ 124255932Salfredstatic char * 125255932Salfredmake_version_name(const char *file, int version) 126255932Salfred{ 127255932Salfred char *backup_name; 128255932Salfred 129255932Salfred if (asprintf(&backup_name, "%s.~%d~", file, version) == -1) 130255932Salfred return NULL; 131255932Salfred return backup_name; 132255932Salfred} 133255932Salfred 134255932Salfred/* 135255932Salfred * If BACKUP is a numbered backup of BASE, return its version number; 136255932Salfred * otherwise return 0. BASE_LENGTH is the length of BASE. BASE should 137255932Salfred * already have ".~" appended to it. 138255932Salfred */ 139255932Salfredstatic int 140255932Salfredversion_number(const char *base, const char *backup, size_t base_length) 141255932Salfred{ 142255932Salfred int version; 143255932Salfred const char *p; 144255932Salfred 145255932Salfred version = 0; 146255932Salfred if (!strncmp(base, backup, base_length) && ISDIGIT(backup[base_length])) { 147255932Salfred for (p = &backup[base_length]; ISDIGIT(*p); ++p) 148255932Salfred version = version * 10 + *p - '0'; 149255932Salfred if (p[0] != '~' || p[1]) 150255932Salfred version = 0; 151255932Salfred } 152255932Salfred return version; 153255932Salfred} 154255932Salfred 155255932Salfred/* 156255932Salfred * Return the newly-allocated concatenation of STR1 and STR2. If out of 157255932Salfred * memory, return 0. 158255932Salfred */ 159255932Salfredstatic char * 160255932Salfredconcat(const char *str1, const char *str2) 161255932Salfred{ 162255932Salfred char *newstr; 163255932Salfred 164255932Salfred if (asprintf(&newstr, "%s%s", str1, str2) == -1) 165255932Salfred return NULL; 166255932Salfred return newstr; 167255932Salfred} 168255932Salfred 169255932Salfred/* 170255932Salfred * If ARG is an unambiguous match for an element of the null-terminated array 171255932Salfred * OPTLIST, return the index in OPTLIST of the matched element, else -1 if it 172255932Salfred * does not match any element or -2 if it is ambiguous (is a prefix of more 173255932Salfred * than one element). 174255932Salfred */ 175255932Salfredstatic int 176255932Salfredargmatch(const char *arg, const char **optlist) 177255932Salfred{ 178255932Salfred int i; /* Temporary index in OPTLIST. */ 179255932Salfred size_t arglen; /* Length of ARG. */ 180255932Salfred int matchind = -1; /* Index of first nonexact match. */ 181255932Salfred int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */ 182255932Salfred 183255932Salfred arglen = strlen(arg); 184255932Salfred 185255932Salfred /* Test all elements for either exact match or abbreviated matches. */ 186255932Salfred for (i = 0; optlist[i]; i++) { 187255932Salfred if (!strncmp(optlist[i], arg, arglen)) { 188255932Salfred if (strlen(optlist[i]) == arglen) 189255932Salfred /* Exact match found. */ 190255932Salfred return i; 191255932Salfred else if (matchind == -1) 192255932Salfred /* First nonexact match found. */ 193255932Salfred matchind = i; 194255932Salfred else 195255932Salfred /* Second nonexact match found. */ 196255932Salfred ambiguous = 1; 197255932Salfred } 198255932Salfred } 199255932Salfred if (ambiguous) 200255932Salfred return -2; 201255932Salfred else 202255932Salfred return matchind; 203255932Salfred} 204255932Salfred 205255932Salfred/* 206255932Salfred * Error reporting for argmatch. KIND is a description of the type of entity 207255932Salfred * that was being matched. VALUE is the invalid value that was given. PROBLEM 208255932Salfred * is the return value from argmatch. 209255932Salfred */ 210255932Salfredstatic void 211255932Salfredinvalid_arg(const char *kind, const char *value, int problem) 212255932Salfred{ 213255932Salfred fprintf(stderr, "patch: "); 214255932Salfred if (problem == -1) 215255932Salfred fprintf(stderr, "invalid"); 216255932Salfred else /* Assume -2. */ 217255932Salfred fprintf(stderr, "ambiguous"); 218255932Salfred fprintf(stderr, " %s `%s'\n", kind, value); 219255932Salfred} 220255932Salfred 221255932Salfredstatic const char *backup_args[] = { 222255932Salfred "none", "never", "simple", "nil", "existing", "t", "numbered", 0 223255932Salfred}; 224255932Salfred 225255932Salfredstatic enum backup_type backup_types[] = { 226255932Salfred none, simple, simple, numbered_existing, 227255932Salfred numbered_existing, numbered, numbered 228255932Salfred}; 229255932Salfred 230255932Salfred/* 231255932Salfred * Return the type of backup indicated by VERSION. Unique abbreviations are 232255932Salfred * accepted. 233255932Salfred */ 234255932Salfredenum backup_type 235255932Salfredget_version(const char *version) 236255932Salfred{ 237255932Salfred int i; 238255932Salfred 239255932Salfred if (version == NULL || *version == '\0') 240255932Salfred return numbered_existing; 241255932Salfred i = argmatch(version, backup_args); 242255932Salfred if (i >= 0) 243255932Salfred return backup_types[i]; 244255932Salfred invalid_arg("version control type", version, i); 245255932Salfred exit(2); 246255932Salfred} 247255932Salfred