1a9845d8delphij/*-
27551d83pfg * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
37551d83pfg *
404a5c2bsheldonh * Copyright (c) 2000 Sheldon Hearn <sheldonh@FreeBSD.org>.
504a5c2bsheldonh * All rights reserved.
65df0554sheldonh *
75df0554sheldonh * Redistribution and use in source and binary forms, with or without
85df0554sheldonh * modification, are permitted provided that the following conditions
95df0554sheldonh * are met:
105df0554sheldonh * 1. Redistributions of source code must retain the above copyright
115df0554sheldonh *    notice, this list of conditions and the following disclaimer.
125df0554sheldonh * 2. Redistributions in binary form must reproduce the above copyright
135df0554sheldonh *    notice, this list of conditions and the following disclaimer in the
145df0554sheldonh *    documentation and/or other materials provided with the distribution.
155df0554sheldonh *
165df0554sheldonh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
175df0554sheldonh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
185df0554sheldonh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
195df0554sheldonh * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
205df0554sheldonh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
215df0554sheldonh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
225df0554sheldonh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
235df0554sheldonh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
245df0554sheldonh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
255df0554sheldonh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
265df0554sheldonh * SUCH DAMAGE.
275df0554sheldonh *
285df0554sheldonh */
295df0554sheldonh
3004a5c2bsheldonhstatic const char rcsid[] =
3104a5c2bsheldonh    "$FreeBSD$";
3204a5c2bsheldonh
335df0554sheldonh#include <sys/stat.h>
345df0554sheldonh
355df0554sheldonh#include <ctype.h>
365df0554sheldonh#include <err.h>
375df0554sheldonh#include <errno.h>
385df0554sheldonh#include <fcntl.h>
395df0554sheldonh#include <stdio.h>
405df0554sheldonh#include <stdlib.h>
415df0554sheldonh#include <unistd.h>
425df0554sheldonh
438d2851asobomax#include <libutil.h>
448d2851asobomax
450b20191impstatic void	usage(void);
465df0554sheldonh
475df0554sheldonhint
485df0554sheldonhmain(int argc, char **argv)
495df0554sheldonh{
50930c5c9manu	struct stat sb;
51930c5c9manu	mode_t omode;
52c2c1309eadler	off_t oflow, rsize, sz, tsize, round;
53f663e7ejilles	uint64_t usz;
54930c5c9manu	int ch, error, fd, oflags;
55930c5c9manu	int no_create;
56930c5c9manu	int do_relative;
5776772b9manu	int do_round;
58930c5c9manu	int do_refer;
59930c5c9manu	int got_size;
60930c5c9manu	char *fname, *rname;
615df0554sheldonh
6216fd058cperciva	fd = -1;
63a9845d8delphij	rsize = tsize = sz = 0;
6476772b9manu	no_create = do_relative = do_round = do_refer = got_size = 0;
655df0554sheldonh	error = 0;
66fa8e7ccsheldonh	rname = NULL;
675df0554sheldonh	while ((ch = getopt(argc, argv, "cr:s:")) != -1)
685df0554sheldonh		switch (ch) {
695df0554sheldonh		case 'c':
70fa8e7ccsheldonh			no_create = 1;
715df0554sheldonh			break;
725df0554sheldonh		case 'r':
73fa8e7ccsheldonh			do_refer = 1;
745df0554sheldonh			rname = optarg;
755df0554sheldonh			break;
765df0554sheldonh		case 's':
7776772b9manu			if (*optarg == '+' || *optarg == '-') {
7876772b9manu				do_relative = 1;
7976772b9manu			} else if (*optarg == '%' || *optarg == '/') {
8076772b9manu				do_round = 1;
8176772b9manu			}
8276772b9manu			if (expand_number(do_relative || do_round ?
8376772b9manu			    optarg + 1 : optarg,
84f663e7ejilles			    &usz) == -1 || (off_t)usz < 0)
855df0554sheldonh				errx(EXIT_FAILURE,
865df0554sheldonh				    "invalid size argument `%s'", optarg);
87f663e7ejilles
8876772b9manu			sz = (*optarg == '-' || *optarg == '/') ?
8976772b9manu				-(off_t)usz : (off_t)usz;
90fa8e7ccsheldonh			got_size = 1;
915df0554sheldonh			break;
925df0554sheldonh		default:
935df0554sheldonh			usage();
945df0554sheldonh			/* NOTREACHED */
955df0554sheldonh		}
965df0554sheldonh
975df0554sheldonh	argv += optind;
985df0554sheldonh	argc -= optind;
995df0554sheldonh
1005df0554sheldonh	/*
1015df0554sheldonh	 * Exactly one of do_refer or got_size must be specified.  Since
1025df0554sheldonh	 * do_relative implies got_size, do_relative and do_refer are
1035df0554sheldonh	 * also mutually exclusive.  See usage() for allowed invocations.
1045df0554sheldonh	 */
105fa8e7ccsheldonh	if (do_refer + got_size != 1 || argc < 1)
1065df0554sheldonh		usage();
1075df0554sheldonh	if (do_refer) {
1085df0554sheldonh		if (stat(rname, &sb) == -1)
1095df0554sheldonh			err(EXIT_FAILURE, "%s", rname);
1105df0554sheldonh		tsize = sb.st_size;
11176772b9manu	} else if (do_relative || do_round)
112fa8e7ccsheldonh		rsize = sz;
113fa8e7ccsheldonh	else
114fa8e7ccsheldonh		tsize = sz;
1155df0554sheldonh
1165df0554sheldonh	if (no_create)
1175df0554sheldonh		oflags = O_WRONLY;
1185df0554sheldonh	else
1195df0554sheldonh		oflags = O_WRONLY | O_CREAT;
1205df0554sheldonh	omode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1215df0554sheldonh
1225df0554sheldonh	while ((fname = *argv++) != NULL) {
1233839b53maxim		if (fd != -1)
1243839b53maxim			close(fd);
1255df0554sheldonh		if ((fd = open(fname, oflags, omode)) == -1) {
1265df0554sheldonh			if (errno != ENOENT) {
1275df0554sheldonh				warn("%s", fname);
1285df0554sheldonh				error++;
1295df0554sheldonh			}
1305df0554sheldonh			continue;
1315df0554sheldonh		}
1325df0554sheldonh		if (do_relative) {
1335df0554sheldonh			if (fstat(fd, &sb) == -1) {
1345df0554sheldonh				warn("%s", fname);
1355df0554sheldonh				error++;
1365df0554sheldonh				continue;
1375df0554sheldonh			}
1385df0554sheldonh			oflow = sb.st_size + rsize;
1395df0554sheldonh			if (oflow < (sb.st_size + rsize)) {
1405df0554sheldonh				errno = EFBIG;
1415df0554sheldonh				warn("%s", fname);
1425df0554sheldonh				error++;
1435df0554sheldonh				continue;
1445df0554sheldonh			}
1455df0554sheldonh			tsize = oflow;
1465df0554sheldonh		}
14776772b9manu		if (do_round) {
14876772b9manu			if (fstat(fd, &sb) == -1) {
14976772b9manu				warn("%s", fname);
15076772b9manu				error++;
15176772b9manu				continue;
15276772b9manu			}
15376772b9manu			sz = rsize;
15476772b9manu			if (sz < 0)
15576772b9manu				sz = -sz;
15676772b9manu			if (sb.st_size % sz) {
15776772b9manu				round = sb.st_size / sz;
15876772b9manu				if (round != sz && rsize < 0)
15976772b9manu					round--;
16076772b9manu				else if (rsize > 0)
16176772b9manu					round++;
16276772b9manu				tsize = (round < 0 ? 0 : round) * sz;
16376772b9manu			} else
16476772b9manu				tsize = sb.st_size;
16576772b9manu		}
1665df0554sheldonh		if (tsize < 0)
1675df0554sheldonh			tsize = 0;
1685df0554sheldonh
1695df0554sheldonh		if (ftruncate(fd, tsize) == -1) {
1705df0554sheldonh			warn("%s", fname);
1715df0554sheldonh			error++;
1725df0554sheldonh			continue;
1735df0554sheldonh		}
1745df0554sheldonh	}
1753839b53maxim	if (fd != -1)
1763839b53maxim		close(fd);
1775df0554sheldonh
1785df0554sheldonh	return error ? EXIT_FAILURE : EXIT_SUCCESS;
1795df0554sheldonh}
1805df0554sheldonh
1815df0554sheldonhstatic void
1825df0554sheldonhusage(void)
1835df0554sheldonh{
1845df0554sheldonh	fprintf(stderr, "%s\n%s\n",
18576772b9manu	    "usage: truncate [-c] -s [+|-|%|/]size[K|k|M|m|G|g|T|t] file ...",
1865df0554sheldonh	    "       truncate [-c] -r rfile file ...");
1875df0554sheldonh	exit(EXIT_FAILURE);
1885df0554sheldonh}
189