truncate.c revision 162412
1/*
2 * Copyright (c) 2000 Sheldon Hearn <sheldonh@FreeBSD.org>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#ifndef lint
29static const char rcsid[] =
30    "$FreeBSD: head/usr.bin/truncate/truncate.c 162412 2006-09-18 16:39:23Z maxim $";
31#endif
32
33#include <sys/stat.h>
34
35#include <ctype.h>
36#include <err.h>
37#include <errno.h>
38#include <fcntl.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <unistd.h>
42
43static int	parselength(char *, off_t *);
44static void	usage(void);
45
46static int	no_create;
47static int	do_relative;
48static int	do_refer;
49static int	got_size;
50
51int
52main(int argc, char **argv)
53{
54	struct stat	sb;
55	mode_t	omode;
56	off_t	oflow, rsize, sz, tsize;
57	int	ch, error, fd, oflags;
58	char   *fname, *rname;
59
60	rsize = tsize = 0;
61	error = 0;
62	rname = NULL;
63	while ((ch = getopt(argc, argv, "cr:s:")) != -1)
64		switch (ch) {
65		case 'c':
66			no_create = 1;
67			break;
68		case 'r':
69			do_refer = 1;
70			rname = optarg;
71			break;
72		case 's':
73			if (parselength(optarg, &sz) == -1)
74				errx(EXIT_FAILURE,
75				    "invalid size argument `%s'", optarg);
76			if (*optarg == '+' || *optarg == '-')
77				do_relative = 1;
78			got_size = 1;
79			break;
80		default:
81			usage();
82			/* NOTREACHED */
83		}
84
85	argv += optind;
86	argc -= optind;
87
88	/*
89	 * Exactly one of do_refer or got_size must be specified.  Since
90	 * do_relative implies got_size, do_relative and do_refer are
91	 * also mutually exclusive.  See usage() for allowed invocations.
92	 */
93	if (do_refer + got_size != 1 || argc < 1)
94		usage();
95	if (do_refer) {
96		if (stat(rname, &sb) == -1)
97			err(EXIT_FAILURE, "%s", rname);
98		tsize = sb.st_size;
99	} else if (do_relative)
100		rsize = sz;
101	else
102		tsize = sz;
103
104	if (no_create)
105		oflags = O_WRONLY;
106	else
107		oflags = O_WRONLY | O_CREAT;
108	omode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
109
110	while ((fname = *argv++) != NULL) {
111		if (fd != -1)
112			close(fd);
113		if ((fd = open(fname, oflags, omode)) == -1) {
114			if (errno != ENOENT) {
115				warn("%s", fname);
116				error++;
117			}
118			continue;
119		}
120		if (do_relative) {
121			if (fstat(fd, &sb) == -1) {
122				warn("%s", fname);
123				error++;
124				continue;
125			}
126			oflow = sb.st_size + rsize;
127			if (oflow < (sb.st_size + rsize)) {
128				errno = EFBIG;
129				warn("%s", fname);
130				error++;
131				continue;
132			}
133			tsize = oflow;
134		}
135		if (tsize < 0)
136			tsize = 0;
137
138		if (ftruncate(fd, tsize) == -1) {
139			warn("%s", fname);
140			error++;
141			continue;
142		}
143	}
144	if (fd != -1)
145		close(fd);
146
147	return error ? EXIT_FAILURE : EXIT_SUCCESS;
148}
149
150/*
151 * Return the numeric value of a string given in the form [+-][0-9]+[GMKT]
152 * or -1 on format error or overflow.
153 */
154static int
155parselength(char *ls, off_t *sz)
156{
157	off_t	length, oflow;
158	int	lsign;
159
160	length = 0;
161	lsign = 1;
162
163	switch (*ls) {
164	case '-':
165		lsign = -1;
166	case '+':
167		ls++;
168	}
169
170#define	ASSIGN_CHK_OFLOW(x, y)	if (x < y) return -1; y = x
171	/*
172	 * Calculate the value of the decimal digit string, failing
173	 * on overflow.
174	 */
175	while (isdigit(*ls)) {
176		oflow = length * 10 + *ls++ - '0';
177		ASSIGN_CHK_OFLOW(oflow, length);
178	}
179
180	switch (*ls) {
181	case 'T':
182	case 't':
183		oflow = length * 1024;
184		ASSIGN_CHK_OFLOW(oflow, length);
185	case 'G':
186	case 'g':
187		oflow = length * 1024;
188		ASSIGN_CHK_OFLOW(oflow, length);
189	case 'M':
190	case 'm':
191		oflow = length * 1024;
192		ASSIGN_CHK_OFLOW(oflow, length);
193	case 'K':
194	case 'k':
195		if (ls[1] != '\0')
196			return -1;
197		oflow = length * 1024;
198		ASSIGN_CHK_OFLOW(oflow, length);
199	case '\0':
200		break;
201	default:
202		return -1;
203	}
204
205	*sz = length * lsign;
206	return 0;
207}
208
209static void
210usage(void)
211{
212	fprintf(stderr, "%s\n%s\n",
213	    "usage: truncate [-c] -s [+|-]size[K|k|M|m|G|g|T|t] file ...",
214	    "       truncate [-c] -r rfile file ...");
215	exit(EXIT_FAILURE);
216}
217