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 #include <sys/stat.h> 29 30 #include <ctype.h> 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <unistd.h> 36 37 static off_t parselength(char *, off_t *); 38 static void usage(void); 39 40 static int no_create; 41 static int do_relative; 42 static int do_refer; 43 static int got_size; 44 45 int 46 main(int argc, char **argv) 47 { 48 struct stat sb; 49 mode_t omode; 50 off_t oflow, rsize, sz, tsize; 51 int ch, error, fd, oflags; 52 char *fname, *rname; 53 54 rsize = tsize = 0; 55 error = 0; 56 rname = NULL; 57 while ((ch = getopt(argc, argv, "cr:s:")) != -1) 58 switch (ch) { 59 case 'c': 60 no_create = 1; 61 break; 62 case 'r': 63 do_refer = 1; 64 rname = optarg; 65 break; 66 case 's': 67 if (parselength(optarg, &sz) == -1) { 68 fprintf(stderr, 69 "invalid size argument `%s'", optarg); 70 exit(EXIT_FAILURE); 71 } 72 if (*optarg == '+' || *optarg == '-') 73 do_relative = 1; 74 got_size = 1; 75 break; 76 default: 77 usage(); 78 /* NOTREACHED */ 79 } 80 81 argv += optind; 82 argc -= optind; 83 84 /* 85 * Exactly one of do_refer or got_size must be specified. Since 86 * do_relative implies got_size, do_relative and do_refer are 87 * also mutually exclusive. See usage() for allowed invocations. 88 */ 89 if (do_refer + got_size != 1 || argc < 1) 90 usage(); 91 if (do_refer) { 92 if (stat(rname, &sb) == -1) { 93 fprintf(stderr, "%s", rname); 94 exit(EXIT_FAILURE); 95 } 96 tsize = sb.st_size; 97 } else if (do_relative) 98 rsize = sz; 99 else 100 tsize = sz; 101 102 if (no_create) 103 oflags = O_WRONLY; 104 else 105 oflags = O_WRONLY | O_CREAT; 106 omode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; 107 108 while ((fname = *argv++) != NULL) { 109 if ((fd = open(fname, oflags, omode)) == -1) { 110 if (errno != ENOENT) { 111 perror(fname); 112 error++; 113 } 114 continue; 115 } 116 if (do_relative) { 117 if (fstat(fd, &sb) == -1) { 118 perror(fname); 119 error++; 120 continue; 121 } 122 oflow = sb.st_size + rsize; 123 if (oflow < (sb.st_size + rsize)) { 124 errno = EFBIG; 125 perror(fname); 126 error++; 127 continue; 128 } 129 tsize = oflow; 130 } 131 if (tsize < 0) 132 tsize = 0; 133 134 if (ftruncate(fd, tsize) == -1) { 135 perror(fname); 136 error++; 137 continue; 138 } 139 140 close(fd); 141 } 142 143 return error ? EXIT_FAILURE : EXIT_SUCCESS; 144 } 145 146 /* 147 * Return the numeric value of a string given in the form [+-][0-9]+[GMK] 148 * or -1 on format error or overflow. 149 */ 150 static off_t 151 parselength(char *ls, off_t *sz) 152 { 153 off_t length, oflow; 154 int lsign; 155 156 length = 0; 157 lsign = 1; 158 159 switch (*ls) { 160 case '-': 161 lsign = -1; 162 case '+': 163 ls++; 164 } 165 166 #define ASSIGN_CHK_OFLOW(x, y) if (x < y) return -1; y = x 167 /* 168 * Calculate the value of the decimal digit string, failing 169 * on overflow. 170 */ 171 while (isdigit(*ls)) { 172 oflow = length * 10 + *ls++ - '0'; 173 ASSIGN_CHK_OFLOW(oflow, length); 174 } 175 176 switch (*ls) { 177 case 'G': 178 case 'g': 179 oflow = length * 1024; 180 ASSIGN_CHK_OFLOW(oflow, length); 181 case 'M': 182 case 'm': 183 oflow = length * 1024; 184 ASSIGN_CHK_OFLOW(oflow, length); 185 case 'K': 186 case 'k': 187 if (ls[1] != '\0') 188 return -1; 189 oflow = length * 1024; 190 ASSIGN_CHK_OFLOW(oflow, length); 191 case '\0': 192 break; 193 default: 194 return -1; 195 } 196 197 *sz = length * lsign; 198 return 0; 199 } 200 201 static void 202 usage(void) 203 { 204 fprintf(stderr, "%s\n%s\n", 205 "usage: truncate [-c] -s [+|-]size[K|k|M|m|G|g] file ...", 206 " truncate [-c] -r rfile file ..."); 207 exit(EXIT_FAILURE); 208 } 209