#include #include #include #include #define NDEBUG #include #include "asplit.h" #include "config.h" /* Prototypes of utility functions defined herein. */ static int close_file(FILE * f); static FILE * open_infile(const char *fname); static FILE * open_outfile(const char *fname); /* Name under which program was invoked. */ const char *program_name; /* * Takes a file pointer to the file to which usage information should be * written. If stderr, the call to usage is considered an exceptional * call, and the function exits with failure status; otherwise, the call * is considered normal (as when -h arg is given), and the function exits * with success status. */ void usage(FILE *fp) { fprintf(fp, "\n" "Usage: %s [OPTION] -i IN_FILE -a OUT_FILE_A -b OUT_FILE_B -n NUM_LINES\n\n" , program_name); fprintf(fp, "Split IN_FILE into 2 files, putting the first NUM_LINES\n" "into OUT_FILE_A and the rest into OUT_FILE_B.\n\n" " -i input file, IN_FILE\n" " -a name of first output file, OUT_FILE_A\n" " -b name of second output file, OUT_FILE_B\n" " -n the number of lines to put in OUT_FILE_A\n\n" " -h display this help and exit\n" " -v verbose output\n" " -V display version and exit\n"); fprintf(fp, "\n" "When IN_FILE is -, use standard input; when OUT_FILE_A or OUTFILE_B is -,\n" "use standard output.\n"); fprintf(fp, "\n" "Examples:\n\n" " %s -n 100 -i - -a output1 -b output2\n" " Read from standard input, writing first 100 lines to output1 file\n" " and all following lines to output2.\n\n" " %s -n 1000 -i infile.txt -a output1.txt -b -\n" " Read from infile.txt, writing the first 1000 lines to output1.txt\n" " and all following lines to standard output.\n\n" " %s -n 1 -i - -a - -b -\n" " Copy standard input to standard output.\n\n" , program_name, program_name, program_name); fprintf(fp, "\nReturns 0 on success, and 1 on failure.\n"); fprintf(fp, "\nReport bugs to " PACKAGE_BUGREPORT ".\n\n"); exit(fp == stderr ? EXIT_FAILURE : EXIT_SUCCESS); } int asplit(const char *in_file, const char *out_file_a, const char *out_file_b, const long int num_lines, long int *num_rem_lines) { long int curr_line_num; /* The current line number being processed. */ int curr_chr; /* The current character being processed. */ FILE *in_f, *out_f; /* Pointers to current input and output files. */ bool out_file_is_a; /* Whether current output file is out_file_a. */ /* Open in_file if not "-", returning unsuccessfully if unable to open. */ if ((in_f = open_infile(in_file)) == NULL) { return ASPLIT_INPUT_ERROR; } /* If num_lines is 0, then nothing goes into the first file.*/ if (num_lines == 0) { /* Open second file if not "-"; otherwise returning failure.*/ if ((out_f = open_outfile(out_file_b)) == NULL) { close_file(in_f); return ASPLIT_OUTPUT_B_ERROR; } out_file_is_a = false; } else { /* num_lines is greater than 0, so some output goes to first file. */ /* Open first file if not "-"; otherwise returning failure. */ if ((out_f = open_outfile(out_file_a)) == NULL) { close_file(in_f); return ASPLIT_OUTPUT_A_ERROR; } out_file_is_a = true; } /* Initial line number is 0, and increments after each newline char. */ curr_line_num = 0; while ((curr_chr = getc(in_f)) != EOF) { /* Output char to current output file. */ putc(curr_chr, out_f); /* * If we read a newline, and if we've just hit the maximum * allowed in the first file, we must close the first file and * open the second file, returning failure if unable to open. */ if (curr_chr == '\n'&& ++curr_line_num == num_lines) { if (close_file(out_f)) { return ASPLIT_OUTPUT_A_ERROR; } if ((out_f = open_outfile(out_file_b)) == NULL) { close_file(in_f); return ASPLIT_OUTPUT_B_ERROR; } out_file_is_a = false; } } /* Close input and output files if necessary. */ /* If unable to close either, we must return non-success code. */ if (close_file(in_f)) { close_file(out_f); /* No way to report 2nd file problem. */ return ASPLIT_INPUT_ERROR; } else if (close_file(out_f)) { return out_file_is_a ? ASPLIT_OUTPUT_A_ERROR : ASPLIT_OUTPUT_B_ERROR; } /* Set num_rem_lines, and return success. */ if (curr_line_num < num_lines) *num_rem_lines = 0; else *num_rem_lines = curr_line_num - num_lines; assert (*num_rem_lines >= 0); return ASPLIT_SUCCESS; } int main(int argc, char **argv) { program_name = *argv; if (argc == 1) usage(stdout); char *input_file = NULL; char *output_file_a = NULL; char *output_file_b = NULL; int num_lines = -1; bool verbose = false; /* Parse the commandline args */ for(int i = 1; i < argc; ++i) { if (strcmp(argv[i], "-h") == 0) { usage(stdout); } else if (strcmp(argv[i], "-v") == 0) { verbose = true; } else if (strcmp(argv[i], "-i") == 0) { if (argc == i+1) usage(stderr); input_file = argv[++i]; } else if (strcmp(argv[i], "-a") == 0) { if (argc == i+1) usage(stderr); output_file_a = argv[++i]; } else if (strcmp(argv[i], "-b") == 0) { if (argc == i+1) usage(stderr); output_file_b = argv[++i]; } else if (strcmp(argv[i], "-n") == 0) { if (argc == i+1) usage(stderr); char *end_ptr; num_lines = strtol(argv[++i], &end_ptr, 10); if (*end_ptr != '\0' || num_lines < 0) { fprintf(stderr, "-n arg must be a non-negative integer.\n"); usage(stderr); } } else if (strcmp(argv[i], "-V") == 0) { puts(PACKAGE_VERSION); exit(0); } else { /* We received an unexpected arg, so display usage and fail. */ usage(stderr); } } /* Verify that all required arguments were submitted. */ if (input_file == NULL || output_file_a == NULL || output_file_b == NULL || num_lines < 0) usage(stderr); /* Do the actual split. */ long int num_rem_lines = 0; status ret_status = asplit(input_file, output_file_a, output_file_b, num_lines, &num_rem_lines); /* Determine whether it terminated successfully, and handle accordingly.*/ switch (ret_status) { case ASPLIT_SUCCESS: if (verbose) printf("Output %li lines to 2nd file.\n", num_rem_lines); exit(EXIT_SUCCESS); case ASPLIT_INPUT_ERROR: fprintf(stderr, "Unable to open input file: %s\n", input_file); exit(EXIT_FAILURE); case ASPLIT_OUTPUT_A_ERROR: fprintf(stderr, "Unable to open output file: %s\n", output_file_a); exit(EXIT_FAILURE); case ASPLIT_OUTPUT_B_ERROR: fprintf(stderr, "Unable to open output file: %s\n", output_file_b); exit(EXIT_FAILURE); default: /* Shouldn't be possible. */ fprintf(stderr, "Error: unexpected return value: %d\n", ret_status); exit(EXIT_FAILURE); } } /** * Close the file referenced by the given file pointer if it is not one of * the standard system files. * * Returns 0 on success, and non-zero if unable to close file. */ int close_file(FILE * restrict f) { if (f != stdin&& f != stdout&& f != stderr) { return fclose(f); } return 0; } /** * Open the input file (mode 'r') referenced by the given pointer, * returning a pointer to the FILE. If fname is "-", return stdin. */ FILE * open_infile(const char * restrict fname) { return strcmp(fname, "-") == 0 ? stdin : fopen(fname, "r"); } /** * Open the output file (mode 'w') referenced by the given pointer, * returning a pointer to the FILE. If fname is "-", return stdout. */ FILE * open_outfile(const char * restrict fname) { return strcmp(fname, "-") == 0 ? stdout : fopen(fname, "w"); }