1 /* $Id: shrinkfile.c 6135 2003-01-19 01:15:40Z rra $
3 ** Shrink files on line boundaries.
5 ** Written by Landon Curt Noll <chongo@toad.com>, and placed in the
6 ** public domain. Rewritten for INN by Rich Salz.
9 ** shrinkfile [-n] [-s size [-m maxsize]] [-v] file...
10 ** -n No writes, exit 0 if any file is too large, 1 otherwise
11 ** -s size Truncation size (0 default); suffix may be k, m,
12 ** or g to scale. Must not be larger than 2^31 - 1.
13 ** -m maxsize Maximum size allowed before truncation. If maxsize
14 ** <= size, then it is reset to size. Default == size.
15 ** -v Print status line.
17 ** Files will be shrunk an end of line boundary. In no case will the
18 ** file be longer than size bytes if it was longer than maxsize bytes.
19 ** If the first line is longer than the absolute value of size, the file
20 ** will be truncated to zero length.
22 ** The -n flag may be used to determine of any file is too large. No
23 ** files will be altered in this mode.
33 #include "inn/innconf.h"
34 #include "inn/messages.h"
37 #define MAX_SIZE 0x7fffffffUL
41 ** Open a safe unique temporary file that will go away when closed.
50 filename = concatpath(innconf->pathtmp, "shrinkXXXXXX");
51 fd = mkstemp(filename);
53 sysdie("cannot create temporary file");
56 sysdie("cannot fdopen %s", filename);
64 ** Does file end with \n? Assume it does on I/O error, to avoid doing I/O.
67 EndsWithNewline(FILE *F)
71 if (fseeko(F, 1, SEEK_END) < 0) {
72 syswarn("cannot seek to end of file");
76 /* return the actual character or EOF */
77 if ((c = fgetc(F)) == EOF) {
79 syswarn("cannot read last byte");
87 ** Add a newline to location of a file.
90 AppendNewline(char *name)
94 if ((F = xfopena(name)) == NULL) {
95 syswarn("cannot add newline");
99 if (fputc('\n', F) == EOF
102 || fclose(F) == EOF) {
103 syswarn("cannot add newline");
111 ** Just check if it is too big
114 TooBig(FILE *F, off_t maxsize)
118 /* Get the file's size. */
119 if (fstat((int)fileno(F), &Sb) < 0) {
120 syswarn("cannot fstat");
124 /* return true if too large */
125 return (maxsize > Sb.st_size ? false : true);
129 ** This routine does all the work.
132 Process(FILE *F, char *name, off_t size, off_t maxsize, bool *Changedp)
137 char buff[BUFSIZ + 1];
142 /* Get the file's size. */
143 if (fstat((int)fileno(F), &Sb) < 0) {
144 syswarn("cannot fstat");
149 /* Process a zero size request. */
150 if (size == 0 && len > maxsize) {
153 if ((F = fopen(name, "w")) == NULL) {
154 syswarn("cannot overwrite");
163 /* See if already small enough. */
164 if (len <= maxsize) {
165 /* Newline already present? */
166 if (EndsWithNewline(F)) {
171 /* No newline, add it if it fits. */
172 if (len < size - 1) {
175 return AppendNewline(name);
178 else if (!EndsWithNewline(F)) {
179 if (!AppendNewline(name)) {
185 /* We now have a file that ends with a newline that is bigger than
186 * we want. Starting from {size} bytes from end, move forward
187 * until we get a newline. */
188 if (fseeko(F, -size, SEEK_END) < 0) {
189 syswarn("cannot fseeko");
194 while ((c = getc(F)) != '\n')
196 syswarn("cannot read");
201 /* Copy rest of file to temp. */
204 while ((i = fread(buff, 1, sizeof buff, F)) > 0)
205 if (fwrite(buff, 1, i, tmp) != i) {
210 syswarn("cannot copy to temporary file");
216 /* Now copy temp back to original file. */
218 if ((F = fopen(name, "w")) == NULL) {
219 syswarn("cannot overwrite file");
223 fseeko(tmp, 0, SEEK_SET);
225 while ((i = fread(buff, 1, sizeof buff, tmp)) > 0)
226 if (fwrite(buff, 1, i, F) != i) {
231 syswarn("cannot overwrite file");
245 ** Convert size argument to numeric value. Return -1 on error.
251 unsigned long str_num;
254 /* Skip leading spaces */
260 /* determine the scaling factor */
261 q = &p[strlen(p) - 1];
265 case '0': case '1': case '2': case '3': case '4':
266 case '5': case '6': case '7': case '8': case '9':
278 scale = 1024 * 1024 * 1024;
283 /* Convert string to number. */
284 if (sscanf(p, "%lud", &str_num) != 1)
286 if (str_num > MAX_SIZE / scale)
287 die("size is too big");
289 return scale * str_num;
294 ** Print usage message and exit.
300 "Usage: shrinkfile [-n] [ -m maxsize ] [-s size] [-v] file...");
306 main(int ac, char *av[])
317 /* First thing, set up our identity. */
318 message_program_name = "shrinkfile";
325 if (!innconf_read(NULL))
329 while ((i = getopt(ac, av, "m:s:vn")) != EOF)
338 if ((maxsize = ParseSize(optarg)) < 0)
342 if ((size = ParseSize(optarg)) < 0)
349 if (maxsize < size) {
357 while ((p = *av++) != NULL) {
358 if ((F = fopen(p, "r")) == NULL) {
359 syswarn("cannot open %s", p);
363 /* -n (no_op) or normal processing */
366 /* check if too big and exit zero if it is */
367 if (TooBig(F, maxsize)) {
369 notice("%s is too large", p);
374 /* no -n, do some real work */
377 if (!Process(F, p, size, maxsize, &Changed))
378 syswarn("cannot shrink %s", p);
379 else if (Verbose && Changed)
380 notice("shrunk %s", p);
383 if (no_op && Verbose) {
384 notice("did not find a file that was too large");
387 /* if -n, then exit non-zero to indicate no file too big */