** Shrink files on line boundaries.
** Written by Landon Curt Noll <chongo@toad.com>, and placed in the
** public domain. Rewritten for INN by Rich Salz.
** shrinkfile [-s size] [-v] file...
** -s size Truncation size (0 default); suffix may be k, m,
** or g to scale. Must not be larger than 2^31 - 1.
** Files will be shrunk an end of line boundary. In no case will the
** file be longer than size bytes. If the first line is longer than
** the absolute value of size, the file will be truncated to zero
#define MAX_SIZE (OFFSET_T)0x7fffffff
** Open a safe unique temporary file that will go away when closed.
if ((p
= getenv("TMPDIR")) == NULL
)
(void)sprintf(buff
, "%s/shrinkXXXXXX", p
);
if ((i
= open(buff
, O_RDWR
| O_CREAT
| O_EXCL
| O_TRUNC
, 0600)) < 0) {
(void)fprintf(stderr
, "Can't make temporary file, %s\n",
if ((F
= fdopen(i
, "w+")) == NULL
) {
(void)fprintf(stderr
, "Can't fdopen %d, %s\n", i
, strerror(errno
));
** Does file end with \n? Assume it does on I/O error, to avoid doing I/O.
if (fseek(F
, (OFFSET_T
)1, SEEK_END
) < 0) {
(void)fprintf(stderr
, "Can't seek to end of file, %s\n",
/* return the actual character or EOF */
if ((c
= fgetc(F
)) == EOF
) {
(void)fprintf(stderr
, "Can't read last byte, %s\n",
** Add a newline to location of a file.
if ((F
= xfopena(name
)) == NULL
) {
(void)fprintf(stderr
, "Can't add newline, %s\n", strerror(errno
));
if (fputc('\n', F
) == EOF
(void)fprintf(stderr
, "Can't add newline, %s\n", strerror(errno
));
** This routine does all the work.
Process(F
, name
, size
, Changedp
)
/* Get the file's size. */
if (fstat((int)fileno(F
), &Sb
) < 0) {
(void)fprintf(stderr
, "Can't fstat, %s\n", strerror(errno
));
/* Process a zero size request. */
if ((F
= fopen(name
, "w")) == NULL
) {
(void)fprintf(stderr
, "Can't overwrite, %s\n", strerror(errno
));
/* See if already small enough. */
/* Newline already present? */
if (EndsWithNewline(F
)) {
/* No newline, add it if it fits. */
return AppendNewline(name
);
else if (!EndsWithNewline(F
)) {
if (!AppendNewline(name
)) {
/* We now have a file that ends with a newline that is bigger than
* we want. Starting from {size} bytes from end, move forward
* until we get a newline. */
if (fseek(F
, -size
, SEEK_END
) < 0) {
(void)fprintf(stderr
, "Can't fseek, %s\n", strerror(errno
));
while ((c
= getc(F
)) != '\n')
(void)fprintf(stderr
, "Can't read, %s\n", strerror(errno
));
/* Copy rest of file to temp. */
while ((i
= fread((POINTER
)buff
, (SIZE_T
)1, (SIZE_T
)sizeof buff
, F
)) > 0)
if (fwrite((POINTER
)buff
, (SIZE_T
)1, (SIZE_T
)i
, tmp
) != i
) {
(void)fprintf(stderr
, "Can't copy to temp file, %s\n",
/* Now copy temp back to original file. */
if ((F
= fopen(name
, "w")) == NULL
) {
(void)fprintf(stderr
, "Can't overwrite, %s\n", strerror(errno
));
(void)fseek(tmp
, (OFFSET_T
)0, SEEK_SET
);
while ((i
= fread((POINTER
)buff
, (SIZE_T
)1, (SIZE_T
)sizeof buff
, tmp
)) > 0)
if (fwrite((POINTER
)buff
, (SIZE_T
)1, (SIZE_T
)i
, F
) != i
) {
(void)fprintf(stderr
, "Can't overwrite file, %s\n", strerror(errno
));
** Convert size argument to numeric value. Return -1 on error.
/* Skip leading spaces */
/* determine the scaling factor */
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
scale
= 1024 * 1024 * 1024;
/* Convert string to number. */
if (sscanf(p
, "%ld", &str_num
) != 1)
if (str_num
> MAX_SIZE
/ scale
) {
(void)fprintf(stderr
, "Size is too big\n");
** Print usage message and exit.
(void)fprintf(stderr
, "Usage: shrinkfile [-s size] [-v] file...\n");
while ((i
= getopt(ac
, av
, "s:v")) != EOF
)
if ((size
= ParseSize(optarg
)) < 0)
while ((p
= *av
++) != NULL
) {
if ((F
= fopen(p
, "r")) == NULL
) {
(void)fprintf(stderr
, "Can't open %s, %s\n", p
, strerror(errno
));
if (!Process(F
, p
, size
, &Changed
))
(void)fprintf(stderr
, "Can't shrink %s\n", p
);
else if (Verbose
&& Changed
)
(void)printf("Shrunk %s\n", p
);