/* * cfsize.c -- change size of the file(s) given on the command line * * Written by Sandeep Sahore * * This program is used to reduce the size of the file(s) specified on the command line. * It will chop off the file from the beginning or head. Shrinking a file in this manner * from the head retains the latest data and throws away the oldest. The program ensures * that the file is not chopped off from the middle of a line by moving the file pointer * to the beginning of the next line before reducing it. Moreover the program terminates * if the desired size is more than the current size of the file. This prevents the user * from inflating the input files instead of shrinking them. Run the compiled executable * on the command line without any options for obtaining usage. * * $Id: nfreq.c,v 1.2 2002/06/23 15:32:01 ssahore Exp $ */ #include #include #include #include #include #include #include #include #include #define KILO 1024 #define MEGA KILO*1024 #define GIGA MEGA*1024 void ckopt(int); /* check if "-s" switch has been given on the command line */ char *eofarg(char *); /* find the end of the argument string */ char *progname(char *); /* get the basename of the program */ void usage(char *prog); /* print usage upon error */ void fpreset(char *); /* reset the file offset to the beginning */ long getfsz(int, char *); /* get filesize given as the argument to the "-s" switch */ void catcherr(const char *, ...); /* catch errors raised by specific functions and abend */ void fsplit(long, char *, FILE *); /* reduce file to the desired size starting from the head */ char *prog; /* store the command name in the variable prog */ int kb, mb, gb; /* desired filesize unit flags */ char errstr[BUFSIZ]; /* buffer for storing the error messages */ int main(int argc, char *argv[]) { FILE *fin; /* extern int errno; */ unsigned long fsz; char *sarg, *fname; int c, flag, eofopt; struct stat st, *pst; /* extern char *sys_errlist[]; */ pst = &st; flag = eofopt = 0; prog = progname(argv[0]); if (argc == 1) /* print usage and abend if invoked without arguments */ usage(prog); while (--argc > 0 && (*++argv)[0] == '-' && !eofopt) { /* terminate abnormally if a standalone dash is encountered */ if (!*(argv[0]+1)) usage(prog); while (c = *++argv[0]) switch (c) { /* specify the size the file has to be reduced to */ case 's': if (*++argv[0]) sarg = argv[0]; else { if (sarg = *++argv) argc--; else fprintf(stderr, "%s: argument required for option -- %c\n", prog, c), usage(prog); } fsz = getfsz(c, sarg); argv[0] = eofarg(sarg); ++flag; break; /* process "--" (the end of options) */ case '-': if (!*(argv[0]-2) && *(argv[0]-1) == '-' && !*(argv[0]+1)) eofopt++; else fprintf(stderr, "%s: illegal option -- %c\n", prog, c), usage(prog); break; default: fprintf(stderr, "%s: illegal option -- %c\n", prog, c); usage(prog); } } /* check for mandatory switches */ ckopt(flag); /* compute desired filesize depending on the argument to "-s" */ if (kb) fsz *= KILO; else if (mb) fsz *= MEGA; else if (gb) fsz *= GIGA; /* end abnormally if no files are given on the command line */ if (!argc) usage(prog); /* Open and truncate file(s) that are given on the command line from the head */ while (argc--) { if (!(fin = fopen(*argv, "r"))) catcherr("Cannot open %s", *argv); else { /* stat file and abend if desired filesize is more than current filesize */ if (stat(*argv, pst)) catcherr("Cannot stat", *argv); if (pst->st_size < fsz) { fprintf(stderr, "%s: New filesize more than current size\n", prog); usage(prog); } fsplit(fsz, *argv, fin); } ++argv; } return 0; } /* * get the basename of the program */ char *progname(char *cp) { while (*cp) ++cp; --cp; while (*cp) { if (*cp == '/') return ++cp; --cp; } return ++cp; } /* * calculate desired filesize given as the argument to the "-s" switch */ long getfsz(int s, char *sarg) { int c; long n = 0; while (c = tolower(*sarg)) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = 10 * n + (c - '0'); break; case 'k': case 'm': case 'g': if (*(sarg-1) >= '0' && *(sarg-1) <= '9' && !*(sarg+1)) { if (c == 'k') kb++; else if (c == 'm') mb++; else if (c == 'g') gb++; } else fprintf(stderr, "%s: invalid argument to option -- %c\n", prog, s), usage(prog); break; default: fprintf(stderr, "%s: invalid argument to option -- %c\n", prog, s), usage(prog); } ++sarg; } return n; } /* * verify if the mandatory "-s" switch has been supplied */ void ckopt(int flag) { if (!flag) fprintf(stderr, "%s: need to specify the -s option\n", prog), usage(prog); } /* * reduce file(s) to the desired size by removing the * oldest entries and leaving the latest ones intact. */ void fsplit(long fsz, char *fnam, FILE *fin) { int c; FILE *fout; long neg = -fsz; /* end abnormally if the temp file cannot be created */ if (!(fout = tmpfile())) catcherr("tmpfile()"); /* set file pointer to "neg" bytes from end of current file */ if (fseek(fin, neg, SEEK_END)) catcherr("fseek()"); /* go to end of the line to avoid inline file breakage */ while ((c = getc(fin)) != '\n') ; /* move the data that needs to be retained to the temp file */ while ((c = getc(fin)) != EOF) putc(c, fout); /* truncate and prepare the current file for writing */ if (!(fin = fopen(fnam, "w"))) catcherr("Cannot open %s", fnam); /* set file pointer to the beginning of the temp file */ if (fseek(fout, 0L, SEEK_SET)) catcherr("fseek()"); /* move the contents of the temp file to the current file */ while ((c = getc(fout)) != EOF) putc(c, fin); /* flush buffered writes to the current file by closing it */ if (fclose(fin)) catcherr("Cannot close %s", fnam); } /* * find the end of the argument string */ char *eofarg(char *optarg) { while (*optarg) ++optarg; return --optarg; } /* * catch errors raised by specific functions and end abnormally */ void catcherr(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "%s: ", prog); vsnprintf(errstr, (size_t) sizeof errstr, fmt, ap); perror(errstr); va_end(ap); exit(2); } /* * print usage and abend if an error is encountered during processing */ void usage(char *prog) { fprintf(stderr, "usage: %s -s filesize[k|m|g] file ...\n", prog); exit(1); }