bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

truncate: new module


From: Bruno Haible
Subject: truncate: new module
Date: Sat, 13 May 2017 02:58:11 +0200
User-agent: KMail/5.1.3 (Linux/4.4.0-75-generic; KDE/5.18.0; x86_64; ; )

The 'largefile' module should trigger replacement functions for all POSIX
functions that take an 'off_t' argument. In particular 'truncate'. Strangely,
gnulib does not have a 'truncate' module so far.


2017-05-13  Bruno Haible  <address@hidden>

        truncate: New module.
        * lib/unistd.in.h (truncate): New declaration.
        * lib/truncate.c: New file.
        * m4/truncate.m4: New file.
        * m4/unistd_h.m4 (gl_UNISTD_H): Test whether 'truncate' is declared.
        (gl_UNISTD_H_DEFAULTS): Initialize GNULIB_TRUNCATE, HAVE_TRUNCATE,
        REPLACE_TRUNCATE.
        * modules/unistd (Makefile.am): Substitute GNULIB_TRUNCATE,
        HAVE_TRUNCATE, REPLACE_TRUNCATE.
        * modules/truncate: New file.
        * tests/test-unistd-c++.cc (truncate): Test signature.
        * doc/posix-functions/truncate.texi: Mention the new module.

        * tests/test-truncate.c: New file.
        * modules/truncate-tests: New file.

diff --git a/doc/posix-functions/truncate.texi 
b/doc/posix-functions/truncate.texi
index a21f098..b52f2b3 100644
--- a/doc/posix-functions/truncate.texi
+++ b/doc/posix-functions/truncate.texi
@@ -4,14 +4,10 @@
 
 POSIX specification:@* 
@url{http://www.opengroup.org/onlinepubs/9699919799/functions/truncate.html}
 
-Gnulib module: ---
+Gnulib module: truncate
 
 Portability problems fixed by Gnulib:
 @itemize
address@hidden itemize
-
-Portability problems not fixed by Gnulib:
address@hidden
 @item
 This function is missing on some platforms:
 mingw, MSVC 9.
@@ -20,3 +16,7 @@ On platforms where @code{off_t} is a 32-bit type, this 
function is not
 applicable to arbitrary lengths for files larger than 2 GB.  The fix is to
 use the @code{AC_SYS_LARGEFILE} macro.
 @end itemize
+
+Portability problems not fixed by Gnulib:
address@hidden
address@hidden itemize
diff --git a/lib/truncate.c b/lib/truncate.c
new file mode 100644
index 0000000..8ca5c0c
--- /dev/null
+++ b/lib/truncate.c
@@ -0,0 +1,51 @@
+/* truncate emulations for native Windows.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <unistd.h>
+
+#include <errno.h>
+#include <fcntl.h>
+
+int
+truncate (const char *filename, off_t length)
+{
+  int fd;
+
+  if (length == 0)
+    {
+      fd = open (filename, O_WRONLY | O_TRUNC);
+      if (fd < 0)
+        return -1;
+    }
+  else
+    {
+      fd = open (filename, O_WRONLY);
+      if (fd < 0)
+        return -1;
+      if (ftruncate (fd, length) < 0)
+        {
+          int saved_errno = errno;
+          close (fd);
+          errno = saved_errno;
+          return -1;
+        }
+    }
+  close (fd);
+  return 0;
+}
diff --git a/lib/unistd.in.h b/lib/unistd.in.h
index d9f741f..8222fd2 100644
--- a/lib/unistd.in.h
+++ b/lib/unistd.in.h
@@ -1457,6 +1457,36 @@ _GL_WARN_ON_USE (symlinkat, "symlinkat is not portable - 
"
 #endif
 
 
+#if @GNULIB_TRUNCATE@
+/* Change the size of the file designated by FILENAME to become equal to 
LENGTH.
+   Return 0 if successful, otherwise -1 and errno set.
+   See the POSIX:2008 specification
+   <http://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html>.  
*/
+# if @REPLACE_TRUNCATE@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef truncate
+#   define truncate rpl_truncate
+#  endif
+_GL_FUNCDECL_RPL (truncate, int, (const char *filename, off_t length)
+                                 _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (truncate, int, (const char *filename, off_t length));
+# else
+#  if address@hidden@
+_GL_FUNCDECL_SYS (truncate, int, (const char *filename, off_t length)
+                                 _GL_ARG_NONNULL ((1)));
+#  endif
+_GL_CXXALIAS_SYS (truncate, int, (const char *filename, off_t length));
+# endif
+_GL_CXXALIASWARN (truncate);
+#elif defined GNULIB_POSIXCHECK
+# undef truncate
+# if HAVE_RAW_DECL_TRUNCATE
+_GL_WARN_ON_USE (truncate, "truncate is unportable - "
+                 "use gnulib module truncate for portability");
+# endif
+#endif
+
+
 #if @GNULIB_TTYNAME_R@
 /* Store at most BUFLEN characters of the pathname of the terminal FD is
    open on in BUF.  Return 0 on success, otherwise an error number.  */
diff --git a/m4/truncate.m4 b/m4/truncate.m4
new file mode 100644
index 0000000..9d348eb
--- /dev/null
+++ b/m4/truncate.m4
@@ -0,0 +1,33 @@
+# truncate.m4 serial 1   -*- Autoconf -*-
+dnl Copyright (C) 2017 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_TRUNCATE],
+[
+  AC_REQUIRE([gl_UNISTD_H_DEFAULTS])
+  AC_CHECK_FUNCS_ONCE([truncate])
+  if test $ac_cv_func_truncate = yes; then
+    m4_ifdef([gl_LARGEFILE], [
+      AC_REQUIRE([AC_CANONICAL_HOST])
+      case "$host_os" in
+        mingw*)
+          dnl Native Windows, and Large File Support is requested.
+          dnl The mingw64 truncate64() function is based on ftruncate64(),
+          dnl which is unreliable (it may delete the file, see
+          dnl 
<http://mingw-w64.sourcearchive.com/documentation/2.0-1/ftruncate64_8c_source.html>).
+          dnl Use gnulib's ftruncate() and truncate() implementation instead.
+          REPLACE_TRUNCATE=1
+          ;;
+      esac
+    ], [
+      :
+    ])
+  else
+    HAVE_TRUNCATE=0
+  fi
+])
+
+# Prerequisites of lib/truncate.c.
+AC_DEFUN([gl_PREREQ_TRUNCATE], [:])
diff --git a/m4/unistd_h.m4 b/m4/unistd_h.m4
index 25aef19..cc44677 100644
--- a/m4/unistd_h.m4
+++ b/m4/unistd_h.m4
@@ -1,4 +1,4 @@
-# unistd_h.m4 serial 69
+# unistd_h.m4 serial 70
 dnl Copyright (C) 2006-2017 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -46,8 +46,8 @@ AC_DEFUN([gl_UNISTD_H],
     gethostname getlogin getlogin_r getpagesize
     getusershell setusershell endusershell
     group_member isatty lchown link linkat lseek pipe pipe2 pread pwrite
-    readlink readlinkat rmdir sethostname sleep symlink symlinkat ttyname_r
-    unlink unlinkat usleep])
+    readlink readlinkat rmdir sethostname sleep symlink symlinkat
+    truncate ttyname_r unlink unlinkat usleep])
 ])
 
 AC_DEFUN([gl_UNISTD_MODULE_INDICATOR],
@@ -102,6 +102,7 @@ AC_DEFUN([gl_UNISTD_H_DEFAULTS],
   GNULIB_SLEEP=0;                AC_SUBST([GNULIB_SLEEP])
   GNULIB_SYMLINK=0;              AC_SUBST([GNULIB_SYMLINK])
   GNULIB_SYMLINKAT=0;            AC_SUBST([GNULIB_SYMLINKAT])
+  GNULIB_TRUNCATE=0;             AC_SUBST([GNULIB_TRUNCATE])
   GNULIB_TTYNAME_R=0;            AC_SUBST([GNULIB_TTYNAME_R])
   GNULIB_UNISTD_H_NONBLOCKING=0; AC_SUBST([GNULIB_UNISTD_H_NONBLOCKING])
   GNULIB_UNISTD_H_SIGPIPE=0;     AC_SUBST([GNULIB_UNISTD_H_SIGPIPE])
@@ -139,6 +140,7 @@ AC_DEFUN([gl_UNISTD_H_DEFAULTS],
   HAVE_SLEEP=1;           AC_SUBST([HAVE_SLEEP])
   HAVE_SYMLINK=1;         AC_SUBST([HAVE_SYMLINK])
   HAVE_SYMLINKAT=1;       AC_SUBST([HAVE_SYMLINKAT])
+  HAVE_TRUNCATE=1;        AC_SUBST([HAVE_TRUNCATE])
   HAVE_UNLINKAT=1;        AC_SUBST([HAVE_UNLINKAT])
   HAVE_USLEEP=1;          AC_SUBST([HAVE_USLEEP])
   HAVE_DECL_ENVIRON=1;    AC_SUBST([HAVE_DECL_ENVIRON])
@@ -179,6 +181,7 @@ AC_DEFUN([gl_UNISTD_H_DEFAULTS],
   REPLACE_SLEEP=0;        AC_SUBST([REPLACE_SLEEP])
   REPLACE_SYMLINK=0;      AC_SUBST([REPLACE_SYMLINK])
   REPLACE_SYMLINKAT=0;    AC_SUBST([REPLACE_SYMLINKAT])
+  REPLACE_TRUNCATE=0;     AC_SUBST([REPLACE_TRUNCATE])
   REPLACE_TTYNAME_R=0;    AC_SUBST([REPLACE_TTYNAME_R])
   REPLACE_UNLINK=0;       AC_SUBST([REPLACE_UNLINK])
   REPLACE_UNLINKAT=0;     AC_SUBST([REPLACE_UNLINKAT])
diff --git a/modules/truncate b/modules/truncate
new file mode 100644
index 0000000..e141b1a
--- /dev/null
+++ b/modules/truncate
@@ -0,0 +1,32 @@
+Description:
+truncate() function: truncate a file to a specified length.
+
+Files:
+lib/truncate.c
+m4/truncate.m4
+
+Depends-on:
+unistd
+sys_types
+largefile
+open            [test $HAVE_TRUNCATE = 0 || test $REPLACE_TRUNCATE = 1]
+ftruncate       [test $HAVE_TRUNCATE = 0 || test $REPLACE_TRUNCATE = 1]
+
+configure.ac:
+gl_FUNC_TRUNCATE
+if test $HAVE_TRUNCATE = 0 || test $REPLACE_TRUNCATE = 1; then
+  AC_LIBOBJ([truncate])
+  gl_PREREQ_TRUNCATE
+fi
+gl_UNISTD_MODULE_INDICATOR([truncate])
+
+Makefile.am:
+
+Include:
+<unistd.h>
+
+License:
+GPL
+
+Maintainer:
+all
diff --git a/modules/truncate-tests b/modules/truncate-tests
new file mode 100644
index 0000000..9c1125f
--- /dev/null
+++ b/modules/truncate-tests
@@ -0,0 +1,12 @@
+Files:
+tests/test-truncate.c
+tests/signature.h
+tests/macros.h
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-truncate
+check_PROGRAMS += test-truncate
diff --git a/modules/unistd b/modules/unistd
index 8af837c..c258110 100644
--- a/modules/unistd
+++ b/modules/unistd
@@ -77,6 +77,7 @@ unistd.h: unistd.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) $(ARG_NONNULL_H
              -e 's/@''GNULIB_SLEEP''@/$(GNULIB_SLEEP)/g' \
              -e 's/@''GNULIB_SYMLINK''@/$(GNULIB_SYMLINK)/g' \
              -e 's/@''GNULIB_SYMLINKAT''@/$(GNULIB_SYMLINKAT)/g' \
+             -e 's/@''GNULIB_TRUNCATE''@/$(GNULIB_TRUNCATE)/g' \
              -e 's/@''GNULIB_TTYNAME_R''@/$(GNULIB_TTYNAME_R)/g' \
              -e 
's/@''GNULIB_UNISTD_H_GETOPT''@/0$(GNULIB_${gl_include_guard_prefix}_UNISTD_H_GETOPT)/g'
 \
              -e 
's/@''GNULIB_UNISTD_H_NONBLOCKING''@/$(GNULIB_UNISTD_H_NONBLOCKING)/g' \
@@ -114,6 +115,7 @@ unistd.h: unistd.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) $(ARG_NONNULL_H
              -e 's|@''HAVE_SLEEP''@|$(HAVE_SLEEP)|g' \
              -e 's|@''HAVE_SYMLINK''@|$(HAVE_SYMLINK)|g' \
              -e 's|@''HAVE_SYMLINKAT''@|$(HAVE_SYMLINKAT)|g' \
+             -e 's|@''HAVE_TRUNCATE''@|$(HAVE_TRUNCATE)|g' \
              -e 's|@''HAVE_UNLINKAT''@|$(HAVE_UNLINKAT)|g' \
              -e 's|@''HAVE_USLEEP''@|$(HAVE_USLEEP)|g' \
              -e 's|@''HAVE_DECL_ENVIRON''@|$(HAVE_DECL_ENVIRON)|g' \
@@ -155,6 +157,7 @@ unistd.h: unistd.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) $(ARG_NONNULL_H
              -e 's|@''REPLACE_SLEEP''@|$(REPLACE_SLEEP)|g' \
              -e 's|@''REPLACE_SYMLINK''@|$(REPLACE_SYMLINK)|g' \
              -e 's|@''REPLACE_SYMLINKAT''@|$(REPLACE_SYMLINKAT)|g' \
+             -e 's|@''REPLACE_TRUNCATE''@|$(REPLACE_TRUNCATE)|g' \
              -e 's|@''REPLACE_TTYNAME_R''@|$(REPLACE_TTYNAME_R)|g' \
              -e 's|@''REPLACE_UNLINK''@|$(REPLACE_UNLINK)|g' \
              -e 's|@''REPLACE_UNLINKAT''@|$(REPLACE_UNLINKAT)|g' \
diff --git a/tests/test-truncate.c b/tests/test-truncate.c
new file mode 100644
index 0000000..f5b72e1
--- /dev/null
+++ b/tests/test-truncate.c
@@ -0,0 +1,110 @@
+/* Test truncating a file.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+#include <unistd.h>
+
+#include "signature.h"
+SIGNATURE_CHECK (truncate, int, (const char *, off_t));
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include "ignore-value.h"
+#include "macros.h"
+
+#define BASE "test-truncate.t"
+
+int
+main (int argc, char *argv[])
+{
+  /* Clean up any trash from prior testsuite runs.  */
+  ignore_value (system ("rm -rf " BASE "*"));
+
+  {
+    int fd = open (BASE "file", O_RDWR | O_TRUNC | O_CREAT, 0600);
+    ASSERT (fd >= 0);
+    ASSERT (write (fd, "Hello", 5) == 5);
+    close (fd);
+  }
+
+  {
+    int fd = open (BASE "file", O_RDONLY);
+    ASSERT (fd >= 0);
+    ASSERT (lseek (fd, 0, SEEK_END) == 5);
+    close (fd);
+  }
+
+  /* Test increasing the size.  */
+  ASSERT (truncate (BASE "file", 314159) == 0);
+  {
+    int fd = open (BASE "file", O_RDONLY);
+    ASSERT (fd >= 0);
+    ASSERT (lseek (fd, 0, SEEK_END) == 314159);
+    close (fd);
+  }
+
+  /* Test reducing the size.  */
+  ASSERT (truncate (BASE "file", 3) == 0);
+  {
+    int fd = open (BASE "file", O_RDONLY);
+    ASSERT (fd >= 0);
+    ASSERT (lseek (fd, 0, SEEK_END) == 3);
+    close (fd);
+  }
+
+  /* Test reducing the size to 0.  */
+  ASSERT (truncate (BASE "file", 0) == 0);
+  {
+    int fd = open (BASE "file", O_RDONLY);
+    ASSERT (fd >= 0);
+    ASSERT (lseek (fd, 0, SEEK_END) == 0);
+    close (fd);
+  }
+
+  /* Test behaviour for nonexistent files.  */
+  {
+    errno = 0;
+    ASSERT (truncate ("/nonexistent", 0) == -1);
+    ASSERT (errno == ENOENT);
+  }
+  /* Test behaviour for directories.  */
+  {
+    errno = 0;
+    ASSERT (truncate (".", 0) == -1);
+    ASSERT (errno == EISDIR
+            || /* on native Windows */ errno == EACCES);
+  }
+  /* Test behaviour for trailing slashes.  */
+  {
+    errno = 0;
+    ASSERT (truncate (BASE "file/", 0) == -1);
+    ASSERT (errno == ENOTDIR
+            || /* on native Windows */ errno == EINVAL);
+  }
+  /* Test behaviour for invalid lengths.  */
+  {
+    errno = 0;
+    ASSERT (truncate (BASE "file", -3) == -1);
+    ASSERT (errno == EINVAL);
+  }
+
+  /* Cleanup.  */
+  ASSERT (unlink (BASE "file") == 0);
+
+  return 0;
+}
diff --git a/tests/test-unistd-c++.cc b/tests/test-unistd-c++.cc
index a19acc8..14cdb4a 100644
--- a/tests/test-unistd-c++.cc
+++ b/tests/test-unistd-c++.cc
@@ -200,6 +200,10 @@ SIGNATURE_CHECK (GNULIB_NAMESPACE::symlinkat, int,
                  (char const *, int, char const *));
 #endif
 
+#if GNULIB_TEST_TRUNCATE
+SIGNATURE_CHECK (GNULIB_NAMESPACE::truncate, int, (const char *, off_t));
+#endif
+
 #if GNULIB_TEST_TTYNAME_R
 SIGNATURE_CHECK (GNULIB_NAMESPACE::ttyname_r, int,
                  (int fd, char *buf, size_t buflen));




reply via email to

[Prev in Thread] Current Thread [Next in Thread]