truncate.c revision 63437
1207618Srdivacky/*
2207618Srdivacky * Copyright (c) 2000 FreeBSD, Inc. All rights reserved.
3207618Srdivacky *
4207618Srdivacky * Redistribution and use in source and binary forms, with or without
5207618Srdivacky * modification, are permitted provided that the following conditions
6207618Srdivacky * are met:
7207618Srdivacky * 1. Redistributions of source code must retain the above copyright
8207618Srdivacky *    notice, this list of conditions and the following disclaimer.
9207618Srdivacky * 2. Redistributions in binary form must reproduce the above copyright
10207618Srdivacky *    notice, this list of conditions and the following disclaimer in the
11207618Srdivacky *    documentation and/or other materials provided with the distribution.
12207618Srdivacky *
13207618Srdivacky * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14207618Srdivacky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15208599Srdivacky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16207618Srdivacky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17207618Srdivacky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18208599Srdivacky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19208599Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20208599Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21207618Srdivacky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22207618Srdivacky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23207618Srdivacky * SUCH DAMAGE.
24207618Srdivacky *
25208599Srdivacky * $FreeBSD: head/usr.bin/truncate/truncate.c 63437 2000-07-18 17:03:58Z sheldonh $
26208599Srdivacky */
27208599Srdivacky
28208599Srdivacky#include <sys/stat.h>
29208599Srdivacky#include <sys/types.h>
30208599Srdivacky
31208599Srdivacky#include <ctype.h>
32208599Srdivacky#include <err.h>
33208599Srdivacky#include <errno.h>
34208599Srdivacky#include <fcntl.h>
35208599Srdivacky#include <limits.h>
36208599Srdivacky#include <stdio.h>
37208599Srdivacky#include <stdlib.h>
38208599Srdivacky#include <unistd.h>
39208599Srdivacky
40208599Srdivackystatic off_t	parselength __P((char *, off_t *));
41208599Srdivackystatic void	usage __P((void));
42208599Srdivacky
43208599Srdivackystatic int	no_create;
44208599Srdivackystatic int	do_relative;
45208599Srdivackystatic int	do_refer;
46208599Srdivackystatic int	got_size;
47208599Srdivacky
48208599Srdivackyint
49208599Srdivackymain(int argc, char **argv)
50208599Srdivacky{
51208599Srdivacky	struct stat	sb;
52208599Srdivacky	mode_t	omode;
53208599Srdivacky	off_t	oflow, rsize, sz, tsize;
54208599Srdivacky	int	ch, error, fd, oflags;
55208599Srdivacky	char   *fname, *rname;
56208599Srdivacky
57208599Srdivacky	error = 0;
58208599Srdivacky	while ((ch = getopt(argc, argv, "cr:s:")) != -1)
59208599Srdivacky		switch (ch) {
60208599Srdivacky		case 'c':
61208599Srdivacky			no_create++;
62208599Srdivacky			break;
63208599Srdivacky		case 'r':
64208599Srdivacky			do_refer++;
65208599Srdivacky			rname = optarg;
66208599Srdivacky			break;
67208599Srdivacky		case 's':
68208599Srdivacky			if (parselength(optarg, &sz) == -1)
69208599Srdivacky				errx(EXIT_FAILURE,
70208599Srdivacky				    "invalid size argument `%s'", optarg);
71208599Srdivacky			if (*optarg == '+' || *optarg == '-')
72208599Srdivacky				do_relative++;
73208599Srdivacky			got_size++;
74208599Srdivacky			break;
75208599Srdivacky		default:
76208599Srdivacky			usage();
77208599Srdivacky			/* NOTREACHED */
78208599Srdivacky		}
79208599Srdivacky
80208599Srdivacky	argv += optind;
81208599Srdivacky	argc -= optind;
82208599Srdivacky
83208599Srdivacky	/*
84208599Srdivacky	 * Exactly one of do_refer or got_size must be specified.  Since
85208599Srdivacky	 * do_relative implies got_size, do_relative and do_refer are
86208599Srdivacky	 * also mutually exclusive.  See usage() for allowed invocations.
87208599Srdivacky	 */
88208599Srdivacky	if (!(do_refer || got_size) || (do_refer && got_size) || argc < 1)
89208599Srdivacky		usage();
90208599Srdivacky	if (do_refer) {
91208599Srdivacky		if (stat(rname, &sb) == -1)
92208599Srdivacky			err(EXIT_FAILURE, "%s", rname);
93208599Srdivacky		tsize = sb.st_size;
94208599Srdivacky	} else if (got_size) {
95208599Srdivacky		if (do_relative)
96208599Srdivacky			rsize = sz;
97208599Srdivacky		else
98208599Srdivacky			tsize = sz;
99208599Srdivacky	}
100208599Srdivacky
101208599Srdivacky	if (no_create)
102208599Srdivacky		oflags = O_WRONLY;
103208599Srdivacky	else
104208599Srdivacky		oflags = O_WRONLY | O_CREAT;
105208599Srdivacky	omode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
106208599Srdivacky
107208599Srdivacky	while ((fname = *argv++) != NULL) {
108208599Srdivacky		if ((fd = open(fname, oflags, omode)) == -1) {
109208599Srdivacky			if (errno != ENOENT) {
110208599Srdivacky				warn("%s", fname);
111208599Srdivacky				error++;
112208599Srdivacky			}
113208599Srdivacky			continue;
114208599Srdivacky		}
115208599Srdivacky		if (do_relative) {
116208599Srdivacky			if (fstat(fd, &sb) == -1) {
117208599Srdivacky				warn("%s", fname);
118208599Srdivacky				error++;
119208599Srdivacky				continue;
120208599Srdivacky			}
121208599Srdivacky			oflow = sb.st_size + rsize;
122208599Srdivacky			if (oflow < (sb.st_size + rsize)) {
123208599Srdivacky				errno = EFBIG;
124208599Srdivacky				warn("%s", fname);
125208599Srdivacky				error++;
126208599Srdivacky				continue;
127208599Srdivacky			}
128208599Srdivacky			tsize = oflow;
129208599Srdivacky		}
130208599Srdivacky		if (tsize < 0)
131208599Srdivacky			tsize = 0;
132208599Srdivacky
133208599Srdivacky		if (ftruncate(fd, tsize) == -1) {
134208599Srdivacky			warn("%s", fname);
135			error++;
136			continue;
137		}
138	}
139
140	return error ? EXIT_FAILURE : EXIT_SUCCESS;
141}
142
143/*
144 * Return the numeric value of a string given in the form [+-][0-9]+[GMK]
145 * or -1 on format error or overflow.
146 */
147static off_t
148parselength(char *ls, off_t *sz)
149{
150	off_t	length, oflow;
151	int	lsign;
152
153	length = 0;
154	lsign = 1;
155
156	switch (*ls) {
157	case '-':
158		lsign = -1;
159	case '+':
160		ls++;
161	}
162
163#define	ASSIGN_CHK_OFLOW(x, y)	if (x < y) return -1; y = x
164	/*
165	 * Calculate the value of the decimal digit string, failing
166	 * on overflow.
167	 */
168	while (isdigit(*ls)) {
169		oflow = length * 10 + *ls++ - '0';
170		ASSIGN_CHK_OFLOW(oflow, length);
171	}
172
173	switch (*ls) {
174	case 'G':
175		oflow = length * 1024;
176		ASSIGN_CHK_OFLOW(oflow, length);
177	case 'M':
178		oflow = length * 1024;
179		ASSIGN_CHK_OFLOW(oflow, length);
180	case 'K':
181		if (ls[1] != '\0')
182			return -1;
183		oflow = length * 1024;
184		ASSIGN_CHK_OFLOW(oflow, length);
185	case '\0':
186		break;
187	default:
188		return -1;
189	}
190
191	*sz = length * lsign;
192	return 0;
193}
194
195static void
196usage(void)
197{
198	fprintf(stderr, "%s\n%s\n",
199	    "usage: truncate [-c] -s [+|-]size[K|M|G] file ...",
200	    "       truncate [-c] -r rfile file ...");
201	exit(EXIT_FAILURE);
202}
203