The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 7.08 on page 165
Write a program to print a set of files, starting each new one on a new page, with a title and a running page count for each file.
Solution by Steven Huang
/* K&R Exercise 7-8 */ /* Steven Huang */ /* Limitation: This program doesn't wrap long lines. */ #include <stdio.h> #include <stdlib.h> #include <assert.h> #define LINES_PER_PAGE 10 #define TRUE 1 #define FALSE 0 void print_file(char *file_name) { FILE *f; int page_number = 1; int line_count; int c; int new_page = TRUE; assert(file_name != NULL); if ((f = fopen(file_name, "r")) != NULL) { while ((c = fgetc(f)) != EOF) { if (new_page) { /* print out the header */ printf("[%s] page %d starts\n", file_name, page_number); new_page = FALSE; line_count = 1; } putchar(c); if (c == '\n' && ++line_count > LINES_PER_PAGE) { /* print out the footer */ printf("[%s] page %d ends\n", file_name, page_number); /* skip another line so we can see it on screen */ putchar('\n'); new_page = TRUE; page_number++; } } if (!new_page) { /* file ended in the middle of a page, so we still need to print a footer */ printf("[%s] page %d ends\n", file_name, page_number); } /* skip another line so we can see it on screen */ putchar('\n'); fclose(f); } } int main(int argc, char *argv[]) { int i; if (argc < 2) { fputs("no files specified\n", stderr); return EXIT_FAILURE; } for (i = 1; i < argc; i++) { print_file(argv[i]); } return EXIT_SUCCESS; }
Solution by codybartfast (Category 0)
- Adds
\n
's to the last page of each file so the next file starts at the top of the next page. Each page ends
on a line number that is a multiple of the page length, here 20. E.g. the last file ends on line 160. - Doesn't print blank pages (e.g. if the last line of the file is on the last line of the page).
The following shows the result of paginating three source files with:
paginate ex-7-8-paginate.c linenum.c ex-7-6-comp-basic.c | linenum
1: # File: ex-7-8-paginate.c Page 1 2: 3: /* 4: * Exercice 7-8. 5: * 6: * Write a program to print a set of files, starting each new one one new page, 7: * with a title and a running page count for each file. 8: */ 9: 10: #include <stdio.h> 11: #include <stdlib.h> 12: #include <string.h> 13: 14: #define PAGELENGTH 20 15: #define MAXLINE 1000 16: 17: enum error { NO_ERROR = 0, FILE_OPEN_ERROR, FILE_READ_ERROR }; 18: 19: void printfile(char *); 20: int printpages(char *path, FILE *file, int pagenum); 21: # File: ex-7-8-paginate.c Page 2 22: 23: static FILE *chkfopen(char *file, char *modes); 24: static char *chkfgets(char *s, int n, FILE *iop, char *path); 25: 26: static char errormsg[MAXLINE]; 27: 28: int main(int argc, char *argv[]) 29: { 30: int i; 31: 32: for (i = 1; i < argc; i++) 33: printfile(argv[i]); 34: 35: exit(NO_ERROR); 36: } 37: 38: void printfile(char *path) 39: { 40: int lnidx, i; 41: # File: ex-7-8-paginate.c Page 3 42: 43: 44: if ((lnidx = printpages(path, chkfopen(path, "r"), 1))) 45: /* if not at top of page print '\n's to get to next page */ 46: for (i = PAGELENGTH - lnidx; i; i--) 47: putchar('\n'); 48: } 49: 50: int printpages(char *path, FILE *file, int pagenum) 51: { 52: static char line[MAXLINE]; 53: int lnidx = 2; 54: 55: /* Don't print blank pages! */ 56: if (chkfgets(line, MAXLINE, file, path) == NULL) 57: return 0; 58: 59: printf("# File: %-60.60s Page %4d\n\n", path, pagenum); 60: do { 61: # File: ex-7-8-paginate.c Page 4 62: 63: printf("%s", line); 64: } while (++lnidx < PAGELENGTH && chkfgets(line, MAXLINE, file, path)); 65: 66: /* Add final \n if the file did not end with \n */ 67: if (line[strlen(line) - 1] != '\n') 68: putchar('\n'); 69: 70: return lnidx < PAGELENGTH ? lnidx : printpages(path, file, pagenum + 1); 71: } 72: 73: FILE *chkfopen(char *path, char *modes) 74: { 75: FILE *fp = fopen(path, modes); 76: if (fp != NULL) 77: return fp; 78: sprintf(errormsg, "error: Failed to open file: '%s'", path); 79: perror(errormsg); 80: exit(FILE_OPEN_ERROR); 81: # File: ex-7-8-paginate.c Page 5 82: 83: } 84: 85: char *chkfgets(char *s, int n, FILE *iop, char *path) 86: { 87: char *r = fgets(s, n, iop); 88: if (!ferror(iop)) 89: return r; 90: sprintf(errormsg, "error: Failed to read file: '%s'", path); 91: perror(errormsg); 92: fclose(iop); 93: exit(FILE_READ_ERROR); 94: } 95: 96: 97: 98: 99: 100: 101: # File: linenum.c Page 1 102: 103: #include <stdio.h> 104: #define LEN 1000 105: 106: int main(void) 107: { 108: char line[LEN]; 109: int ln = 0; 110: 111: while (fgets(line, LEN, stdin)) 112: printf("%4d: %s", ++ln, line); 113: return 0; 114: } /* intentionally no final \n */ 115: 116: 117: 118: 119: 120: 121: # File: ex-7-6-comp-basic.c Page 1 122: 123: /* 124: * Exercise 7-6. 125: * 126: * Write a program to compare two files, printing the first line where they 127: * differ. 128: */ 129: 130: #include <stdio.h> 131: #include <string.h> 132: 133: #define MAXLINE (1 << 10) 134: 135: int main(int argc, char *argv[]) 136: { 137: char line1[MAXLINE], line2[MAXLINE]; 138: FILE *file1 = fopen(argv[1], "r"), *file2 = fopen(argv[2], "r"); 139: char *rslt1, *rslt2; 140: int num = 0; 141: # File: ex-7-6-comp-basic.c Page 2 142: 143: 144: do { 145: num++; 146: rslt1 = fgets(line1, MAXLINE, file1); 147: rslt2 = fgets(line2, MAXLINE, file2); 148: } while (rslt1 && rslt2 && !strcmp(rslt1, rslt2)); 149: 150: if (rslt1 != rslt2) 151: printf("line: %d\n< %s---\n> %s", num, 152: rslt1 ? rslt1 : "<EOF>\n", rslt2 ? rslt2 : "<EOF>\n"); 153: return 0; 154: } 155: 156: 157: 158: 159: 160:
Latest code on github
Solution by anonymous
I understood the exercise to be print a variable number of files specified by command line arguments. For each file, start by printing a title, and then print the page number and the contents of the file line by line. When printing the file contents, prefix the line with the line number. If the contents of the line are too long, wrap the line to the next line and update the line number accordingly. When a file finishes, print newlines until the page is fully printed, except if the next page would be completely empty. The goal is to simulate viewing a file in some kind of text viewing application.
With this understanding in mind, this is what I came up with:
#include <stdio.h> #include <string.h> #include <stdlib.h> /* Exercise 7-8. Write a program to print a set of files, starting each new one on a new page, with a title and a running page count for each file. */ #define MAXFILES 100 // number of files supported from command line arguments #define WIDTH 100 // width in chars for printing. Also maximum size a line can be when read. Actual chars read will be WIDTH - 5 due to line number printing #define LINESPERPAGE 15 // number of lines printed before starting a new page int getline(char *line, int max, FILE *fp); void printPageNum(int num); FILE *fp[MAXFILES] = { 0 }; // array of FILE pointers initialized to null int names[MAXFILES]; int fpIndex = 0; // print lines that match pattern. Source can be files or stdin int main(int argc, char *argv[]) { char line[WIDTH]; unsigned int lineNum = 1, pageNum = 1; for (int i = 1; i < argc; i++) if (fpIndex < MAXFILES) { if ((fp[fpIndex] = fopen(argv[i], "r")) == NULL) { fprintf(stderr, "can't open %s\n", argv[i]); exit(1); } names[fpIndex++] = i; // capture the index of the file name to use for printing later } else { fprintf(stderr, "too many input files, max supported is %d\n", MAXFILES); exit(1); } if (fpIndex == 0) // no input files { fprintf(stderr, "usage: print file1 [file2] [file3] [...]\n"); exit(1); } for (int i = 0; i < MAXFILES && fp[i] != NULL; i++) { // prints the title int slen = strlen(argv[names[i]]); for (int j = 0; j < WIDTH; j++) putchar('='); putchar('\n'); for (int j = 0; j < (WIDTH / 2) - (slen / 2); j++) putchar(' '); printf("%s", argv[names[i]]); putchar('\n'); for (int j = 0; j < WIDTH; j++) putchar('='); putchar('\n'); lineNum = 1; pageNum = 1; // prints the contents of the file while (getline(line, WIDTH - 4, fp[i]) != -1) // -4 for the 5 chars for printing line numbers and +1 for the '\0' { if ((lineNum - 1) % LINESPERPAGE == 0) // only print page number if more lines (if loop still running, there are more lines. If previous line needed page #, print it) printPageNum(pageNum++); printf("%03d: %s\n", lineNum++, line); } while((lineNum++ - 1) % LINESPERPAGE != 0) // start the next file on a new page putchar('\n'); } return 0; } // read a line, return length. Removes \n if it ends in it int getline(char *line, int max, FILE *fp) { if (fgets(line, max, fp) == NULL) return -1; if (line[strlen(line) - 1] == '\n') line[strlen(line) - 1] = '\0'; return strlen(line); } void printPageNum(int num) { putchar('\n'); for (int i = 0; i < (WIDTH / 2) - 4; i++) // page 001 == 8 chars (8 / 2 = 4) putchar('-'); printf("Page %03d", num); for (int i = WIDTH / 2 + 4; i < WIDTH; i++) putchar('-'); putchar('\n'); }