maint: ftruncate is always available, even without gnulib
[coreutils.git] / src / truncate.c
blobece52ee76b71b00cfefe353e73794f9c74025e5b
1 /* truncate -- truncate or extend the length of files.
2 Copyright (C) 2008-2010 Free Software Foundation, Inc.
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.
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.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 /* Written by Pádraig Brady
19 This is backwards compatible with the FreeBSD utility, but is more
20 flexible wrt the size specifications and the use of long options,
21 to better fit the "GNU" environment. */
23 #include <config.h> /* sets _FILE_OFFSET_BITS=64 etc. */
24 #include <stdio.h>
25 #include <getopt.h>
26 #include <sys/types.h>
28 #include "system.h"
29 #include "error.h"
30 #include "posixver.h"
31 #include "quote.h"
32 #include "xstrtol.h"
34 /* The official name of this program (e.g., no `g' prefix). */
35 #define PROGRAM_NAME "truncate"
37 #define AUTHORS proper_name_utf8 ("Padraig Brady", "P\303\241draig Brady")
39 /* (-c) If true, don't create if not already there */
40 static bool no_create;
42 /* (-o) If true, --size refers to blocks not bytes */
43 static bool block_mode;
45 /* (-r) Reference file to use size from */
46 static char const *ref_file;
48 static struct option const longopts[] =
50 {"no-create", no_argument, NULL, 'c'},
51 {"io-blocks", no_argument, NULL, 'o'},
52 {"reference", required_argument, NULL, 'r'},
53 {"size", required_argument, NULL, 's'},
54 {GETOPT_HELP_OPTION_DECL},
55 {GETOPT_VERSION_OPTION_DECL},
56 {NULL, 0, NULL, 0}
59 typedef enum
60 { rm_abs = 0, rm_rel, rm_min, rm_max, rm_rdn, rm_rup } rel_mode_t;
62 /* Set size to the value of STR, interpreted as a decimal integer,
63 optionally multiplied by various values.
64 Return -1 on error, 0 on success.
66 This supports dd BLOCK size suffixes + lowercase g,t,m for bsd compat
67 Note we don't support dd's b=512, c=1, w=2 or 21x512MiB formats. */
68 static int
69 parse_len (char const *str, off_t *size)
71 enum strtol_error e;
72 intmax_t tmp_size;
73 e = xstrtoimax (str, NULL, 10, &tmp_size, "EgGkKmMPtTYZ0");
74 if (e == LONGINT_OK
75 && !(OFF_T_MIN <= tmp_size && tmp_size <= OFF_T_MAX))
76 e = LONGINT_OVERFLOW;
78 if (e == LONGINT_OK)
80 errno = 0;
81 *size = tmp_size;
82 return 0;
85 errno = (e == LONGINT_OVERFLOW ? EOVERFLOW : 0);
86 return -1;
89 void
90 usage (int status)
92 if (status != EXIT_SUCCESS)
93 fprintf (stderr, _("Try `%s --help' for more information.\n"),
94 program_name);
95 else
97 printf (_("Usage: %s OPTION... FILE...\n"), program_name);
98 fputs (_("\
99 Shrink or extend the size of each FILE to the specified size\n\
101 A FILE argument that does not exist is created.\n\
103 If a FILE is larger than the specified size, the extra data is lost.\n\
104 If a FILE is shorter, it is extended and the extended part (hole)\n\
105 reads as zero bytes.\n\
107 "), stdout);
108 fputs (_("\
109 Mandatory arguments to long options are mandatory for short options too.\n\
110 "), stdout);
111 fputs (_("\
112 -c, --no-create do not create any files\n\
113 "), stdout);
114 fputs (_("\
115 -o, --io-blocks treat SIZE as number of IO blocks instead of bytes\n\
116 "), stdout);
117 fputs (_("\
118 -r, --reference=FILE use this FILE's size\n\
119 -s, --size=SIZE use this SIZE\n"), stdout);
120 fputs (HELP_OPTION_DESCRIPTION, stdout);
121 fputs (VERSION_OPTION_DESCRIPTION, stdout);
122 emit_size_note ();
123 fputs (_("\n\
124 SIZE may also be prefixed by one of the following modifying characters:\n\
125 `+' extend by, `-' reduce by, `<' at most, `>' at least,\n\
126 `/' round down to multiple of, `%' round up to multiple of.\n"), stdout);
127 fputs (_("\
129 Note that the -r and -s options are mutually exclusive.\n\
130 "), stdout);
131 emit_ancillary_info ();
133 exit (status);
136 /* return 1 on error, 0 on success */
137 static int
138 do_ftruncate (int fd, char const *fname, off_t ssize, rel_mode_t rel_mode)
140 struct stat sb;
141 off_t nsize;
143 if ((block_mode || rel_mode) && fstat (fd, &sb) != 0)
145 error (0, errno, _("cannot fstat %s"), quote (fname));
146 return 1;
148 if (block_mode)
150 off_t const blksize = ST_BLKSIZE (sb);
151 if (ssize < OFF_T_MIN / blksize || ssize > OFF_T_MAX / blksize)
153 error (0, 0,
154 _("overflow in %" PRIdMAX
155 " * %" PRIdMAX " byte blocks for file %s"),
156 (intmax_t) ssize, (intmax_t) blksize,
157 quote (fname));
158 return 1;
160 ssize *= blksize;
162 if (rel_mode)
164 uintmax_t const fsize = sb.st_size;
166 if (sb.st_size < 0)
168 /* Complain only for a regular file, a directory,
169 or a shared memory object, as POSIX 1003.1-2004 specifies
170 ftruncate's behavior only for these file types. */
171 if (S_ISREG (sb.st_mode) || S_ISDIR (sb.st_mode)
172 || S_TYPEISSHM (&sb))
174 /* overflow is the only reason I can think
175 this would ever go negative for the above types */
176 error (0, 0, _("%s has unusable, apparently negative size"),
177 quote (fname));
178 return 1;
180 return 0;
183 if (rel_mode == rm_min)
184 nsize = MAX (fsize, (uintmax_t) ssize);
185 else if (rel_mode == rm_max)
186 nsize = MIN (fsize, (uintmax_t) ssize);
187 else if (rel_mode == rm_rdn)
188 /* 0..ssize-1 -> 0 */
189 nsize = (fsize / ssize) * ssize;
190 else if (rel_mode == rm_rup)
191 /* 1..ssize -> ssize */
193 /* Here ssize>=1 && fsize>=0 */
194 uintmax_t const overflow = ((fsize + ssize - 1) / ssize) * ssize;
195 if (overflow > OFF_T_MAX)
197 error (0, 0, _("overflow rounding up size of file %s"),
198 quote (fname));
199 return 1;
201 nsize = overflow;
203 else
205 if (ssize > OFF_T_MAX - (off_t)fsize)
207 error (0, 0, _("overflow extending size of file %s"),
208 quote (fname));
209 return 1;
211 nsize = fsize + ssize;
214 else
215 nsize = ssize;
216 if (nsize < 0)
217 nsize = 0;
219 if (ftruncate (fd, nsize) == -1) /* note updates mtime & ctime */
221 /* Complain only when ftruncate fails on a regular file, a
222 directory, or a shared memory object, as POSIX 1003.1-2004
223 specifies ftruncate's behavior only for these file types.
224 For example, do not complain when Linux kernel 2.4 ftruncate
225 fails on /dev/fd0. */
226 int const ftruncate_errno = errno;
227 if (fstat (fd, &sb) != 0)
229 error (0, errno, _("cannot fstat %s"), quote (fname));
230 return 1;
232 else if (S_ISREG (sb.st_mode) || S_ISDIR (sb.st_mode)
233 || S_TYPEISSHM (&sb))
235 error (0, ftruncate_errno,
236 _("truncating %s at %" PRIdMAX " bytes"), quote (fname),
237 (intmax_t) nsize);
238 return 1;
240 return 0;
243 return 0;
247 main (int argc, char **argv)
249 bool got_size = false;
250 off_t size IF_LINT (= 0);
251 rel_mode_t rel_mode = rm_abs;
252 mode_t omode;
253 int c, errors = 0, fd = -1, oflags;
254 char const *fname;
256 initialize_main (&argc, &argv);
257 set_program_name (argv[0]);
258 setlocale (LC_ALL, "");
259 bindtextdomain (PACKAGE, LOCALEDIR);
260 textdomain (PACKAGE);
262 atexit (close_stdout);
264 while ((c = getopt_long (argc, argv, "cor:s:", longopts, NULL)) != -1)
266 switch (c)
268 case 'c':
269 no_create = true;
270 break;
272 case 'o':
273 block_mode = true;
274 break;
276 case 'r':
277 ref_file = optarg;
278 break;
280 case 's':
281 /* skip any whitespace */
282 while (isspace (to_uchar (*optarg)))
283 optarg++;
284 switch (*optarg)
286 case '<':
287 rel_mode = rm_max;
288 optarg++;
289 break;
290 case '>':
291 rel_mode = rm_min;
292 optarg++;
293 break;
294 case '/':
295 rel_mode = rm_rdn;
296 optarg++;
297 break;
298 case '%':
299 rel_mode = rm_rup;
300 optarg++;
301 break;
303 /* skip any whitespace */
304 while (isspace (to_uchar (*optarg)))
305 optarg++;
306 if (*optarg == '+' || *optarg == '-')
308 if (rel_mode)
310 error (0, 0, _("multiple relative modifiers specified"));
311 /* Note other combinations are flagged as invalid numbers */
312 usage (EXIT_FAILURE);
314 rel_mode = rm_rel;
316 if (parse_len (optarg, &size) == -1)
317 error (EXIT_FAILURE, errno, _("invalid number %s"),
318 quote (optarg));
319 /* Rounding to multiple of 0 is nonsensical */
320 if ((rel_mode == rm_rup || rel_mode == rm_rdn) && size == 0)
321 error (EXIT_FAILURE, 0, _("division by zero"));
322 got_size = true;
323 break;
325 case_GETOPT_HELP_CHAR;
327 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
329 default:
330 usage (EXIT_FAILURE);
334 argv += optind;
335 argc -= optind;
337 /* must specify either size or reference file */
338 if ((ref_file && got_size) || (!ref_file && !got_size))
340 error (0, 0, _("you must specify one of %s or %s"),
341 quote_n (0, "--size"), quote_n (1, "--reference"));
342 usage (EXIT_FAILURE);
344 /* block_mode without size is not valid */
345 if (block_mode && !got_size)
347 error (0, 0, _("%s was specified but %s was not"),
348 quote_n (0, "--io-blocks"), quote_n (1, "--size"));
349 usage (EXIT_FAILURE);
351 /* must specify at least 1 file */
352 if (argc < 1)
354 error (0, 0, _("missing file operand"));
355 usage (EXIT_FAILURE);
358 if (ref_file)
360 struct stat sb;
361 if (stat (ref_file, &sb) != 0)
362 error (EXIT_FAILURE, errno, _("cannot stat %s"), quote (ref_file));
363 size = sb.st_size;
366 oflags = O_WRONLY | (no_create ? 0 : O_CREAT) | O_NONBLOCK;
367 omode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
369 while ((fname = *argv++) != NULL)
371 if ((fd = open (fname, oflags, omode)) == -1)
373 /* `truncate -s0 -c no-such-file` shouldn't gen error
374 `truncate -s0 no-such-dir/file` should gen ENOENT error
375 `truncate -s0 no-such-dir/` should gen EISDIR error
376 `truncate -s0 .` should gen EISDIR error */
377 if (!(no_create && errno == ENOENT))
379 int const open_errno = errno;
380 struct stat sb;
381 if (stat (fname, &sb) == 0)
383 /* Complain only for a regular file, a directory,
384 or a shared memory object, as POSIX 1003.1-2004 specifies
385 ftruncate's behavior only for these file types. */
386 if (!S_ISREG (sb.st_mode) && !S_ISDIR (sb.st_mode)
387 && !S_TYPEISSHM (&sb))
388 continue;
390 error (0, open_errno, _("cannot open %s for writing"),
391 quote (fname));
392 errors++;
394 continue;
398 if (fd != -1)
400 errors += do_ftruncate (fd, fname, size, rel_mode);
401 if (close (fd) != 0)
403 error (0, errno, _("closing %s"), quote (fname));
404 errors++;
409 return errors ? EXIT_FAILURE : EXIT_SUCCESS;