truncate.c revision 188887
1178172Simp/*-
2178172Simp * Copyright (c) 2000 Sheldon Hearn <sheldonh@FreeBSD.org>.
3206713Sjmallett * All rights reserved.
4206713Sjmallett *
5206713Sjmallett * Redistribution and use in source and binary forms, with or without
6206713Sjmallett * modification, are permitted provided that the following conditions
7206713Sjmallett * are met:
8206713Sjmallett * 1. Redistributions of source code must retain the above copyright
9206713Sjmallett *    notice, this list of conditions and the following disclaimer.
10206713Sjmallett * 2. Redistributions in binary form must reproduce the above copyright
11206713Sjmallett *    notice, this list of conditions and the following disclaimer in the
12206713Sjmallett *    documentation and/or other materials provided with the distribution.
13206713Sjmallett *
14206713Sjmallett * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15206713Sjmallett * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16206713Sjmallett * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17206713Sjmallett * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18206713Sjmallett * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19206713Sjmallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20206713Sjmallett * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21206713Sjmallett * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22206713Sjmallett * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23206713Sjmallett * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24206713Sjmallett * SUCH DAMAGE.
25206713Sjmallett *
26206713Sjmallett */
27178172Simp
28178172Simp#ifndef lint
29178172Simpstatic const char rcsid[] =
30178172Simp    "$FreeBSD: head/usr.bin/truncate/truncate.c 188887 2009-02-21 03:42:31Z delphij $";
31178172Simp#endif
32178172Simp
33178172Simp#include <sys/stat.h>
34178172Simp
35178172Simp#include <ctype.h>
36178172Simp#include <err.h>
37178172Simp#include <errno.h>
38178172Simp#include <fcntl.h>
39178172Simp#include <stdio.h>
40178172Simp#include <stdlib.h>
41178172Simp#include <unistd.h>
42178172Simp
43178172Simpstatic int	parselength(char *, off_t *);
44178172Simpstatic void	usage(void);
45178172Simp
46178172Simpstatic int	no_create;
47178172Simpstatic int	do_relative;
48178172Simpstatic int	do_refer;
49178172Simpstatic int	got_size;
50178172Simp
51178172Simpint
52178172Simpmain(int argc, char **argv)
53178172Simp{
54178172Simp	struct stat	sb;
55178172Simp	mode_t	omode;
56178172Simp	off_t	oflow, rsize, sz, tsize;
57178172Simp	int	ch, error, fd, oflags;
58178172Simp	char   *fname, *rname;
59178172Simp
60178172Simp	fd = -1;
61178172Simp	rsize = tsize = sz = 0;
62178172Simp	error = 0;
63178172Simp	rname = NULL;
64178172Simp	while ((ch = getopt(argc, argv, "cr:s:")) != -1)
65178172Simp		switch (ch) {
66178172Simp		case 'c':
67178172Simp			no_create = 1;
68178172Simp			break;
69178172Simp		case 'r':
70178172Simp			do_refer = 1;
71178172Simp			rname = optarg;
72227658Sjchandra			break;
73241374Sattilio		case 's':
74218383Sjmallett			if (parselength(optarg, &sz) == -1)
75178172Simp				errx(EXIT_FAILURE,
76178172Simp				    "invalid size argument `%s'", optarg);
77178172Simp			if (*optarg == '+' || *optarg == '-')
78178172Simp				do_relative = 1;
79178172Simp			got_size = 1;
80178172Simp			break;
81178172Simp		default:
82178172Simp			usage();
83178172Simp			/* NOTREACHED */
84178172Simp		}
85178172Simp
86218383Sjmallett	argv += optind;
87178172Simp	argc -= optind;
88178172Simp
89178172Simp	/*
90206713Sjmallett	 * Exactly one of do_refer or got_size must be specified.  Since
91206713Sjmallett	 * do_relative implies got_size, do_relative and do_refer are
92206713Sjmallett	 * also mutually exclusive.  See usage() for allowed invocations.
93206713Sjmallett	 */
94206713Sjmallett	if (do_refer + got_size != 1 || argc < 1)
95206713Sjmallett		usage();
96178172Simp	if (do_refer) {
97178172Simp		if (stat(rname, &sb) == -1)
98218383Sjmallett			err(EXIT_FAILURE, "%s", rname);
99218383Sjmallett		tsize = sb.st_size;
100218383Sjmallett	} else if (do_relative)
101218383Sjmallett		rsize = sz;
102218383Sjmallett	else
103218383Sjmallett		tsize = sz;
104178172Simp
105178172Simp	if (no_create)
106218383Sjmallett		oflags = O_WRONLY;
107178172Simp	else
108178172Simp		oflags = O_WRONLY | O_CREAT;
109178172Simp	omode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
110216947Sjmallett
111216947Sjmallett	while ((fname = *argv++) != NULL) {
112216947Sjmallett		if (fd != -1)
113216947Sjmallett			close(fd);
114216947Sjmallett		if ((fd = open(fname, oflags, omode)) == -1) {
115206713Sjmallett			if (errno != ENOENT) {
116216972Sjmallett				warn("%s", fname);
117178172Simp				error++;
118178172Simp			}
119178172Simp			continue;
120178172Simp		}
121178172Simp		if (do_relative) {
122178172Simp			if (fstat(fd, &sb) == -1) {
123178172Simp				warn("%s", fname);
124178172Simp				error++;
125178172Simp				continue;
126178172Simp			}
127178172Simp			oflow = sb.st_size + rsize;
128178172Simp			if (oflow < (sb.st_size + rsize)) {
129178172Simp				errno = EFBIG;
130178172Simp				warn("%s", fname);
131178172Simp				error++;
132178172Simp				continue;
133178172Simp			}
134178172Simp			tsize = oflow;
135178172Simp		}
136178172Simp		if (tsize < 0)
137178172Simp			tsize = 0;
138218383Sjmallett
139218383Sjmallett		if (ftruncate(fd, tsize) == -1) {
140218383Sjmallett			warn("%s", fname);
141218383Sjmallett			error++;
142218383Sjmallett			continue;
143218383Sjmallett		}
144218383Sjmallett	}
145218383Sjmallett	if (fd != -1)
146218383Sjmallett		close(fd);
147218383Sjmallett
148218383Sjmallett	return error ? EXIT_FAILURE : EXIT_SUCCESS;
149218383Sjmallett}
150218383Sjmallett
151218383Sjmallett/*
152218383Sjmallett * Return the numeric value of a string given in the form [+-][0-9]+[GMKT]
153218383Sjmallett * or -1 on format error or overflow.
154218383Sjmallett */
155218383Sjmallettstatic int
156218383Sjmallettparselength(char *ls, off_t *sz)
157218383Sjmallett{
158206713Sjmallett	off_t	length, oflow;
159216972Sjmallett	int	lsign;
160216972Sjmallett
161216972Sjmallett	length = 0;
162218383Sjmallett	lsign = 1;
163218383Sjmallett
164218383Sjmallett	switch (*ls) {
165218383Sjmallett	case '-':
166218383Sjmallett		lsign = -1;
167218383Sjmallett	case '+':
168206713Sjmallett		ls++;
169218383Sjmallett	}
170217354Sjchandra
171217354Sjchandra#define	ASSIGN_CHK_OFLOW(x, y)	if (x < y) return -1; y = x
172217354Sjchandra	/*
173217354Sjchandra	 * Calculate the value of the decimal digit string, failing
174216972Sjmallett	 * on overflow.
175178172Simp	 */
176216972Sjmallett	while (isdigit(*ls)) {
177218383Sjmallett		oflow = length * 10 + *ls++ - '0';
178178172Simp		ASSIGN_CHK_OFLOW(oflow, length);
179178172Simp	}
180216972Sjmallett
181178172Simp	switch (*ls) {
182178172Simp	case 'T':
183178172Simp	case 't':
184178172Simp		oflow = length * 1024;
185178172Simp		ASSIGN_CHK_OFLOW(oflow, length);
186178172Simp	case 'G':
187178172Simp	case 'g':
188178172Simp		oflow = length * 1024;
189178172Simp		ASSIGN_CHK_OFLOW(oflow, length);
190178172Simp	case 'M':
191178172Simp	case 'm':
192178172Simp		oflow = length * 1024;
193178172Simp		ASSIGN_CHK_OFLOW(oflow, length);
194178172Simp	case 'K':
195178172Simp	case 'k':
196178172Simp		if (ls[1] != '\0')
197178172Simp			return -1;
198178172Simp		oflow = length * 1024;
199178172Simp		ASSIGN_CHK_OFLOW(oflow, length);
200178172Simp	case '\0':
201178172Simp		break;
202216972Sjmallett	default:
203202031Simp		return -1;
204206829Sjmallett	}
205202031Simp
206202031Simp	*sz = length * lsign;
207202031Simp	return 0;
208202031Simp}
209202031Simp
210202031Simpstatic void
211202031Simpusage(void)
212202031Simp{
213206829Sjmallett	fprintf(stderr, "%s\n%s\n",
214202031Simp	    "usage: truncate [-c] -s [+|-]size[K|k|M|m|G|g|T|t] file ...",
215202031Simp	    "       truncate [-c] -r rfile file ...");
216202031Simp	exit(EXIT_FAILURE);
217202031Simp}
218202031Simp