(root)/
gettext-0.22.4/
gettext-tools/
gnulib-lib/
backupfile.c
       1  /* backupfile.c -- make Emacs style backup file names
       2     Copyright (C) 1990-2003, 2005-2006, 2012, 2020 Free Software Foundation, Inc.
       3  
       4     This program is free software: you can redistribute it and/or modify
       5     it under the terms of the GNU General Public License as published by
       6     the Free Software Foundation; either version 3 of the License, or
       7     (at your option) any later version.
       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.  See the
      12     GNU General Public License for more details.
      13  
      14     You should have received a copy of the GNU General Public License
      15     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      16  
      17  /* Written by David MacKenzie <djm@gnu.ai.mit.edu>.
      18     Some algorithms adapted from GNU Emacs. */
      19  
      20  #include <config.h>
      21  
      22  #include "argmatch.h"
      23  #include "backupfile.h"
      24  
      25  #include <stdio.h>
      26  #include <sys/types.h>
      27  #if HAVE_STRING_H
      28  # include <string.h>
      29  #else
      30  # include <strings.h>
      31  #endif
      32  
      33  #if HAVE_DIRENT_H
      34  # include <dirent.h>
      35  #endif
      36  
      37  #include <stdlib.h>
      38  
      39  #include "basename-lgpl.h"
      40  
      41  #if HAVE_DIRENT_H
      42  # define HAVE_DIR 1
      43  #else
      44  # define HAVE_DIR 0
      45  #endif
      46  
      47  #include <limits.h>
      48  
      49  /* Upper bound on the string length of an integer converted to string.
      50     302 / 1000 is ceil (log10 (2.0)).  Subtract 1 for the sign bit;
      51     add 1 for integer division truncation; add 1 more for a minus sign.  */
      52  #define INT_STRLEN_BOUND(t) ((sizeof (t) * CHAR_BIT - 1) * 302 / 1000 + 2)
      53  
      54  /* ISDIGIT differs from isdigit, as follows:
      55     - Its arg may be any int or unsigned int; it need not be an unsigned char.
      56     - It's guaranteed to evaluate its argument exactly once.
      57     - It's typically faster.
      58     Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
      59     only '0' through '9' are digits.  Prefer ISDIGIT to isdigit unless
      60     it's important to use the locale's definition of 'digit' even when the
      61     host does not conform to Posix.  */
      62  #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
      63  
      64  #if D_INO_IN_DIRENT
      65  # define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0)
      66  #else
      67  # define REAL_DIR_ENTRY(dp) 1
      68  #endif
      69  
      70  /* The extension added to file names to produce a simple (as opposed
      71     to numbered) backup file name. */
      72  const char *simple_backup_suffix = "~";
      73  
      74  #if HAVE_DIR
      75  static int max_backup_version (const char *, const char *);
      76  static int version_number (const char *, const char *, size_t);
      77  #endif
      78  
      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 (const char *file, enum backup_type backup_type)
      86  {
      87    size_t backup_suffix_size_max;
      88    size_t file_len = strlen (file);
      89    size_t numbered_suffix_size_max = INT_STRLEN_BOUND (int) + 4;
      90    char *s;
      91    const char *suffix = simple_backup_suffix;
      92  
      93    /* Allow room for simple or '.~N~' backups.  */
      94    backup_suffix_size_max = strlen (simple_backup_suffix) + 1;
      95    if (HAVE_DIR && backup_suffix_size_max < numbered_suffix_size_max)
      96      backup_suffix_size_max = numbered_suffix_size_max;
      97  
      98    s = (char *) malloc (file_len + backup_suffix_size_max
      99                         + numbered_suffix_size_max);
     100    if (s)
     101      {
     102        strcpy (s, file);
     103  
     104  #if HAVE_DIR
     105        if (backup_type != simple)
     106          {
     107            int highest_backup;
     108            size_t dir_len = last_component (s) - s;
     109  
     110            strcpy (s + dir_len, ".");
     111            highest_backup = max_backup_version (file + dir_len, s);
     112            if (! (backup_type == numbered_existing && highest_backup == 0))
     113              {
     114                char *numbered_suffix = s + (file_len + backup_suffix_size_max);
     115                sprintf (numbered_suffix, ".~%d~", highest_backup + 1);
     116                suffix = numbered_suffix;
     117              }
     118            strcpy (s, file);
     119          }
     120  #endif /* HAVE_DIR */
     121  
     122        addext (s, suffix, '~');
     123      }
     124    return s;
     125  }
     126  
     127  #if HAVE_DIR
     128  
     129  /* Return the number of the highest-numbered backup file for file
     130     FILE in directory DIR.  If there are no numbered backups
     131     of FILE in DIR, or an error occurs reading DIR, return 0.
     132     */
     133  
     134  static int
     135  max_backup_version (const char *file, const char *dir)
     136  {
     137    DIR *dirp;
     138    struct dirent *dp;
     139    int highest_version;
     140    int this_version;
     141    size_t file_name_length;
     142  
     143    dirp = opendir (dir);
     144    if (!dirp)
     145      return 0;
     146  
     147    highest_version = 0;
     148    file_name_length = strlen (file);
     149  
     150    while ((dp = readdir (dirp)) != 0)
     151      {
     152        if (!REAL_DIR_ENTRY (dp) || strlen (dp->d_name) < file_name_length + 4)
     153          continue;
     154  
     155        this_version = version_number (file, dp->d_name, file_name_length);
     156        if (this_version > highest_version)
     157          highest_version = this_version;
     158      }
     159    if (closedir (dirp))
     160      return 0;
     161    return highest_version;
     162  }
     163  
     164  /* If BACKUP is a numbered backup of BASE, return its version number;
     165     otherwise return 0.  BASE_LENGTH is the length of BASE.
     166     */
     167  
     168  static int
     169  version_number (const char *base, const char *backup, size_t base_length)
     170  {
     171    int version;
     172    const char *p;
     173  
     174    version = 0;
     175    if (strncmp (base, backup, base_length) == 0
     176        && backup[base_length] == '.'
     177        && backup[base_length + 1] == '~')
     178      {
     179        for (p = &backup[base_length + 2]; ISDIGIT (*p); ++p)
     180          version = version * 10 + *p - '0';
     181        if (p[0] != '~' || p[1])
     182          version = 0;
     183      }
     184    return version;
     185  }
     186  #endif /* HAVE_DIR */
     187  
     188  static const char * const backup_args[] =
     189  {
     190    /* In a series of synonyms, present the most meaning full first, so
     191       that argmatch_valid be more readable. */
     192    "none", "off",
     193    "simple", "never",
     194    "existing", "nil",
     195    "numbered", "t",
     196    0
     197  };
     198  
     199  static const enum backup_type backup_types[] =
     200  {
     201    none, none,
     202    simple, simple,
     203    numbered_existing, numbered_existing,
     204    numbered, numbered
     205  };
     206  
     207  /* Return the type of backup specified by VERSION.
     208     If VERSION is NULL or the empty string, return numbered_existing.
     209     If VERSION is invalid or ambiguous, fail with a diagnostic appropriate
     210     for the specified CONTEXT.  Unambiguous abbreviations are accepted.  */
     211  
     212  enum backup_type
     213  get_version (const char *context, const char *version)
     214  {
     215    if (version == 0 || *version == 0)
     216      return numbered_existing;
     217    else
     218      return XARGMATCH (context, version, backup_args, backup_types);
     219  }
     220  
     221  
     222  /* Return the type of backup specified by VERSION.
     223     If VERSION is NULL, use the value of the envvar VERSION_CONTROL.
     224     If the specified string is invalid or ambiguous, fail with a diagnostic
     225     appropriate for the specified CONTEXT.
     226     Unambiguous abbreviations are accepted.  */
     227  
     228  enum backup_type
     229  xget_version (const char *context, const char *version)
     230  {
     231    if (version && *version)
     232      return get_version (context, version);
     233    else
     234      return get_version ("$VERSION_CONTROL", getenv ("VERSION_CONTROL"));
     235  }