/* * Shrink files on line boundaries. * * Written by Landon Curt Noll , and placed in the * public domain. Rewritten for INN by Rich Salz. Re-rewritten for * OS/2 Changi nntpd by Harald Kipp. * * Bug reports related to THIS modified version should be sent to * * harald@os2point.ping.de * harald@haport.sesam.com * Fido: 2:2448/434 * * You may freely modify, copy or redistribute this software. * */ #include #include #include #include #include #include #include #include #include #ifndef NOLOG #error NOLOG must be defined to compile #endif #define MAX_SIZE (long)0x7fffffff /* ** Print usage message and exit. */ static void Usage() { fprintf(stderr, "Usage: shrink [-s size] [-v] file...\n\n"); fprintf(stderr, " -s size Truncation size (0 default); suffix may be k, m,\n"); fprintf(stderr, " or g to scale. Must not be larger than 2^31 - 1.\n\n"); fprintf(stderr, " -v Print status line.\n\n"); fprintf(stderr, "Files will be shrunk at end of line boundary. In no case will the\n"); fprintf(stderr, "file be longer than size bytes. If the first line is longer than the\n"); fprintf(stderr, "absolute value of size, the file will be truncated to zero length.\n"); exit(1); } /* ** Does file end with \n? Assume it does on I/O error, to avoid doing I/O. */ static int EndsWithNewline(FILE *F) { int c; if (fseek(F, 1, SEEK_END) < 0) { fprintf(stderr, "Can't seek to end of file, %s\n", strerror(errno)); return 1; } /* return the actual character or EOF */ if ((c = fgetc(F)) == EOF) { if (ferror(F)) fprintf(stderr, "Can't read last byte, %s\n", strerror(errno)); return 1; } return c == '\n'; } /* ** Add a newline to location of a file. */ static int AppendNewline(char *name) { FILE *F; if ((F = fopen(name, "ab")) == NULL) { fprintf(stderr, "Can't add newline, %s\n", strerror(errno)); return 0; } if(fputc('\n', F) == EOF || fflush(F) == EOF || ferror(F) || fclose(F) == EOF) { fprintf(stderr, "Can't add newline, %s\n", strerror(errno)); return 0; } return 1; } /* ** This routine does all the work. */ static int Process(FILE *fp, char *name, long size, int *Changedp) { long len; FILE *tmp; struct stat Sb; char buff[BUFSIZ + 1]; int c; int i; /* * Get the file's size. */ if (fstat((int)fileno(fp), &Sb) < 0) { fprintf(stderr, "Can't fstat, %s\n", strerror(errno)); return 0; } len = Sb.st_size; /* * Process a zero size request. */ if (size == 0) { if (len > 0) { fclose(fp); if ((fp = fopen(name, "wb")) == NULL) { fprintf(stderr, "Can't overwrite, %s\n", strerror(errno)); return 0; } fclose(fp); *Changedp = 1; } return 1; } /* * See if already small enough. */ if (len < size) { /* Newline already present? */ if (EndsWithNewline(fp)) { fclose(fp); return 1; } /* No newline, add it if it fits. */ if (len < size - 1) { fclose(fp); *Changedp = 1; return AppendNewline(name); } } else if (!EndsWithNewline(fp)) { if (!AppendNewline(name)) { fclose(fp); return 0; } } /* * 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(fp, -size, SEEK_END) < 0) { fprintf(stderr, "Can't fseek, %s\n", strerror(errno)); fclose(fp); return 0; } while ((c = getc(fp)) != '\n') if (c == EOF) { fprintf(stderr, "Can't read, %s\n", strerror(errno)); fclose(fp); return 0; } /* * Copy rest of file to temp. */ if((tmp = tmpfile()) == NULL) { fprintf(stderr, "Can't open temp file\n"); fclose(fp); return 0; } while ((i = (int)fread(buff, 1, sizeof buff, fp)) > 0) if ((int)fwrite(buff, 1, i, tmp) != i) { i = -1; break; } if (i < 0) { fprintf(stderr, "Can't copy to temp file, %s\n", strerror(errno)); fclose(fp); fclose(tmp); return 0; } fclose(fp); /* * Now copy temp back to original file. */ if ((fp = fopen(name, "wb")) == NULL) { fprintf(stderr, "Can't overwrite, %s\n", strerror(errno)); fclose(tmp); return 0; } fseek(tmp, 0, SEEK_SET); while ((i = fread(buff, 1, sizeof buff, tmp)) > 0) if ((int)fwrite(buff, 1, i, fp) != i) { i = -1; break; } if (i < 0) { fprintf(stderr, "Can't overwrite file, %s\n", strerror(errno)); fclose(fp); fclose(tmp); return 0; } fclose(fp); fclose(tmp); *Changedp = 1; return 1; } /* ** Convert size argument to numeric value. Return -1 on error. */ static long ParseSize(char *p) { long scale; long str_num; char *q; /* * Skip leading spaces */ while (isspace(*p)) p++; if (*p == '\0') return -1; /* * Determine the scaling factor */ q = &p[strlen(p) - 1]; switch (*q) { default: return -1; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': scale = 1; break; case 'k': case 'K': scale = 1024; *q = '\0'; break; case 'm': case 'M': scale = 1024 * 1024; *q = '\0'; break; case 'g': case 'G': scale = 1024 * 1024 * 1024; *q = '\0'; break; } /* * Convert string to number. */ if (sscanf(p, "%ld", &str_num) != 1) return -1; if (str_num > MAX_SIZE / scale) { fprintf(stderr, "Size is too big\n"); exit(1); } return scale * str_num; } int main(int argc, char *argv[]) { int Changed; int Verbose = 0; FILE *fp; char *p; int i; long size = MAX_SIZE; while ((i = getopt(argc, argv, "s:v")) != EOF) switch (i) { default: Usage(); break; case 's': if ((size = ParseSize(optarg)) < 0) Usage(); break; case 'v': Verbose = 1; break; } argc -= optind; argv += optind; if (argc == 0) Usage(); while ((p = *argv++) != NULL) { if ((fp = fopen(p, "rb")) == NULL) { fprintf(stderr, "Can't open %s, %s\n", p, strerror(errno)); continue; } Changed = 0; if (!Process(fp, p, size, &Changed)) fprintf(stderr, "Can't shrink %s\n", p); else if (Verbose && Changed) printf("Shrunk %s\n", p); } return(0); }