diff options
Diffstat (limited to 'ch1')
-rw-r--r-- | ch1/1-01_hello-world.c | 14 | ||||
-rw-r--r-- | ch1/1-02_escape-sequences.c | 15 | ||||
-rw-r--r-- | ch1/1-03_temp-table-header.c | 29 | ||||
-rw-r--r-- | ch1/1-04_celsius_converter.c | 27 | ||||
-rw-r--r-- | ch1/1-05_backwards-table.c | 29 | ||||
-rw-r--r-- | ch1/1-06_eof-test.c | 15 | ||||
-rw-r--r-- | ch1/1-07_eof-value.c | 13 | ||||
-rw-r--r-- | ch1/1-08_space-counter.c | 29 | ||||
-rw-r--r-- | ch1/1-09_single-spacing.c | 29 | ||||
-rw-r--r-- | ch1/1-10_literal-escapes.c | 33 | ||||
-rw-r--r-- | ch1/1-11_word-count.c | 37 | ||||
-rw-r--r-- | ch1/1-12_one-word-per-line.c | 41 | ||||
-rw-r--r-- | ch1/1-13_word-length-histogram.c | 76 | ||||
-rw-r--r-- | ch1/1-14_character-freq-histogram.c | 51 | ||||
-rw-r--r-- | ch1/1-15_temp-convert-func.c | 18 | ||||
-rw-r--r-- | ch1/1-16_longest-line.c | 52 | ||||
-rw-r--r-- | ch1/1-17_over-80.c | 47 | ||||
-rw-r--r-- | ch1/1-18_strip-blanks.c | 38 | ||||
-rw-r--r-- | ch1/1-19_reverse-lines.c | 54 | ||||
-rw-r--r-- | ch1/1-20_detab.c | 48 | ||||
-rw-r--r-- | ch1/1-21_entab.c | 57 | ||||
-rw-r--r-- | ch1/1-22_wordwrap.c | 75 | ||||
-rw-r--r-- | ch1/1-23_decomment.c | 84 | ||||
-rw-r--r-- | ch1/1-24_syntax-checker.c | 163 |
24 files changed, 1074 insertions, 0 deletions
diff --git a/ch1/1-01_hello-world.c b/ch1/1-01_hello-world.c new file mode 100644 index 0000000..e02c87a --- /dev/null +++ b/ch1/1-01_hello-world.c @@ -0,0 +1,14 @@ +#include <stdio.h> + +/* The C Programming Language, 2nd Edition + * + * Exercise 1-1: Run the "hello, world" program on your system. Experiment with + * leaving out parts of the program, to see what error messages you get. + * + * This is the typical first exercise. No need for me to putz with it. + */ + +int main(void) { + printf("Hello world!\n"); + return 0; +} diff --git a/ch1/1-02_escape-sequences.c b/ch1/1-02_escape-sequences.c new file mode 100644 index 0000000..8d122e0 --- /dev/null +++ b/ch1/1-02_escape-sequences.c @@ -0,0 +1,15 @@ +#include <stdio.h> + +/* The C Programming Language, 2nd Edition + * + * Exercise 1-2: Experiment to find out what happens when printf()'s argument + * string contains \c, where 'c' is some character that's not \, t, b, n, or ". + * + * Answer: This file will not (normally) compile because \d is not a valid + * escape sequence. Your compiler may ignore this, however. + */ + +int main(void) { + printf("Hello world! \d\n"); + return 0; +} diff --git a/ch1/1-03_temp-table-header.c b/ch1/1-03_temp-table-header.c new file mode 100644 index 0000000..d0d069c --- /dev/null +++ b/ch1/1-03_temp-table-header.c @@ -0,0 +1,29 @@ +#include <stdio.h> + +/* The C Programming Language, 2nd Edition + * + * Exercise 1-3: Modify the temperature conversion program to print a heading + * above the table. + * + * Answer: Just add a printf() or two before the table. + */ + +int main(void) { + float fahr, celsius; + int lower, upper, step; + + lower = 0; + upper = 300; + step = 20; + + fahr = lower; + + printf(" F | C\n"); + printf("------------\n"); + while (fahr <= upper) { + celsius = (5.0 / 9.0) * (fahr - 32.0); + printf(" %3.0f %6.1f\n", fahr, celsius); + fahr += step; + } + return 0; +} diff --git a/ch1/1-04_celsius_converter.c b/ch1/1-04_celsius_converter.c new file mode 100644 index 0000000..a397d9a --- /dev/null +++ b/ch1/1-04_celsius_converter.c @@ -0,0 +1,27 @@ +#include <stdio.h> + +/* The C Programming Language, 2nd Edition + * + * Exercise 1-4: Write a program to print the corresponding Celsius to + * Fahrenheit table. + */ + +int main(void) { + float fahr, celsius; + int lower, upper, step; + + lower = 0; + upper = 300; + step = 20; + + celsius = lower; + + printf(" C | F\n"); + printf("------------\n"); + while (celsius <= upper) { + fahr = (celsius * (9.0 / 5.0)) + 32.0; + printf(" %3.0f %6.1f\n", celsius, fahr); + celsius += step; + } + return 0; +} diff --git a/ch1/1-05_backwards-table.c b/ch1/1-05_backwards-table.c new file mode 100644 index 0000000..a09619a --- /dev/null +++ b/ch1/1-05_backwards-table.c @@ -0,0 +1,29 @@ +#include <stdio.h> + +/* The C Programming Language, 2nd Edition + * + * Exercise 1-5: Modify the temperature conversion program to print the table in + * reverse order, that is, from 300 degrees to 0. + * + * Answer: Ensure that fahr starts at upper and the while loop runs until zero. + */ + +int main(void) { + float fahr, celsius; + int lower, upper, step; + + lower = 0; + upper = 300; + step = 20; + + fahr = upper; + + printf(" F | C\n"); + printf("------------\n"); + while (fahr >= 0) { + celsius = (5.0 / 9.0) * (fahr - 32.0); + printf(" %3.0f %6.1f\n", fahr, celsius); + fahr -= step; + } + return 0; +} diff --git a/ch1/1-06_eof-test.c b/ch1/1-06_eof-test.c new file mode 100644 index 0000000..5214338 --- /dev/null +++ b/ch1/1-06_eof-test.c @@ -0,0 +1,15 @@ +#include <stdio.h> + +/* The C Programming Language, 2nd Edition + * + * Exercise 1-6: Verify that the expression 'getchar() != EOF' is 0 or 1. + * + * Answer: Easy. + */ + +int main(void) { + int c; + c = (getchar() != EOF); + printf("%d\n", c); + return 0; +} diff --git a/ch1/1-07_eof-value.c b/ch1/1-07_eof-value.c new file mode 100644 index 0000000..aaf838b --- /dev/null +++ b/ch1/1-07_eof-value.c @@ -0,0 +1,13 @@ +#include <stdio.h> + +/* The C Programming Language, 2nd Edition + * + * Exercise 1-7: Write a program to print the value of EOF. + * + * Answer: Just cast it to an integer placeholder in printf(). + */ + +int main(void) { + printf("%d\n", EOF); + return 0; +} diff --git a/ch1/1-08_space-counter.c b/ch1/1-08_space-counter.c new file mode 100644 index 0000000..0e00176 --- /dev/null +++ b/ch1/1-08_space-counter.c @@ -0,0 +1,29 @@ +#include <stdio.h> + +/* The C Programming Language, 2nd Edition + * + * Exercise 1-8: Write a program to count blanks, tabs, and newlines. + * + * Answer: Run a loop with getchar() and check its value. Then add to the + * counts and spit them out at the end of the loop. + */ + +int main(void) { + char c; + int blanks, tabs, nls = 0; + + while ((c = getchar()) != EOF) { + if (c == ' ') { + blanks++; + } + if (c == '\t') { + tabs++; + } + if (c == '\n') { + nls++; + } + } + + printf("%d blanks, %d tabs, and %d newlines.\n", blanks, tabs, nls); + return 0; +} diff --git a/ch1/1-09_single-spacing.c b/ch1/1-09_single-spacing.c new file mode 100644 index 0000000..4bf802a --- /dev/null +++ b/ch1/1-09_single-spacing.c @@ -0,0 +1,29 @@ +#include <stdio.h> + +/* The C Programming Language, 2nd Edition + * + * Exercise 1-9: Write a program to copy its input to its output, replacing each + * string of one or more blanks by a single blank. + * + * Answer: Run a loop with getchar() and check its value. If it's a space, count + * it, but prevent further spaces from being printed. Anything else should + * simply be spat out. + */ + +int main(void) { + char c; + int spaces = 0; + + while ((c = getchar()) != EOF) { + if (c == ' ') { + if (spaces == 0) { + putchar(c); + } + spaces++; + } else { + putchar(c); + } + } + + return 0; +} diff --git a/ch1/1-10_literal-escapes.c b/ch1/1-10_literal-escapes.c new file mode 100644 index 0000000..903fff4 --- /dev/null +++ b/ch1/1-10_literal-escapes.c @@ -0,0 +1,33 @@ +#include <stdio.h> + +/* The C Programming Language, 2nd Edition + * + * Exercise 1-10: Write a program to copy its input to its output, replacing + * each tab by '\t', each backspace by '\b', and each backslash by '\\'. This + * makes tabs and backspaces visible in an unambiguous way. + * + * Answer: Run a loop with getchar() and check its value. When you run into a + * tab, backspace, or backslash, just output the two characters and move on. + */ + +int main(void) { + char c; + + while ((c = getchar()) != EOF) { + if (c == '\t') { + printf("\\t"); + continue; + } + if (c == '\b') { + printf("\\b"); + continue; + } + if (c == '\\') { + printf("\\\\"); + continue; + } + putchar(c); + } + + return 0; +} diff --git a/ch1/1-11_word-count.c b/ch1/1-11_word-count.c new file mode 100644 index 0000000..1b4056c --- /dev/null +++ b/ch1/1-11_word-count.c @@ -0,0 +1,37 @@ +#include <stdio.h> + +/* The C Programming Language, 2nd Edition + * + * Exercise 1-11: How would you test the word count program? What kinds of + * input are most likely to uncover bugs if there are any? + * + * Answer: New lines (such as those caused by wrapping text) are most likely to + * introduce erroneous word counts, as well as symbols that are strung together + * but don't create actual words. + */ + +#define IN 1 +#define OUT 0 + +int main(void) { + int c, nl, nw, nc, state; + state = OUT; + nl = nw = nc = 0; + + while ((c = getchar()) != EOF) { + nc++; + if (c == '\n') { + nl++; + } + if (c == ' ' || c == '\n' || c == '\t') { + state = OUT; + } else if (state == OUT) { + state = IN; + nw++; + } + } + + printf("%d %d %d\n", nl, nw, nc); + + return 0; +} diff --git a/ch1/1-12_one-word-per-line.c b/ch1/1-12_one-word-per-line.c new file mode 100644 index 0000000..29a38df --- /dev/null +++ b/ch1/1-12_one-word-per-line.c @@ -0,0 +1,41 @@ +#include <stdio.h> + +/* The C Programming Language, 2nd Edition + * + * Exercise 1-12: Write a program that prints its input one word per line. + * + * Answer: Be sure to output letters when you're in a word, and a newline when + * you're out of one. + */ + +#define IN 1 +#define OUT 0 + +int main(void) { + int c, nl, nw, nc, state; + state = OUT; + nl = nw = nc = 0; + + while ((c = getchar()) != EOF) { + nc++; + if (c == '\n') { + nl++; + } + if (c == ' ' || c == '\n' || c == '\t') { + if (state == IN) { + state = OUT; + putchar('\n'); + } + } else if (state == OUT) { + state = IN; + nw++; + } + if (state == IN) { + putchar(c); + } + } + + printf("%d %d %d\n", nl, nw, nc); + + return 0; +} diff --git a/ch1/1-13_word-length-histogram.c b/ch1/1-13_word-length-histogram.c new file mode 100644 index 0000000..d3115a5 --- /dev/null +++ b/ch1/1-13_word-length-histogram.c @@ -0,0 +1,76 @@ +#include <stdio.h> +#define IN 1 +#define OUT 0 +#define MINWLENGTH 2 +#define MAXWLENGTH 20 + +int main(void) { + /* Rundown of variables: + * i, j = reusable placeholder variables + * state = inside or outside a word + * ltrs = letter count + * wrds = word count + * lines = you should be shot if you don't know + * lengths = an array that keeps track of how often words up to x chars long + * occur. + */ + + int state, ltrs, wrds, lines, wlen, i, j; + int lengths[MAXWLENGTH]; + for (i = 0; i <= MAXWLENGTH; ++i) { + lengths[i] = 0; + } + + ltrs = wrds = wlen = 0; + lines = 1; + state = OUT; + // Capture input until it ends + while ((i = getchar()) != EOF) { + // If it's whitespace, we've exited a word + if (i == '\n' || i == ' ' || i == '\t') { + if (state == IN) { + ++wrds; // ...and should increase the count. + state = OUT; + /* Check to see if the word is eligible to be counted. */ + if (wlen <= MAXWLENGTH) { + ++lengths[wlen]; + } + // Reset our word length now. + wlen = 0; + } + /* If it's a new line, we're still out of a word but need to increment the + line count */ + if (i == '\n') { + ++lines; + } + } else { + /* If nothing else, we know it's just a random character or a letter. */ + state = IN; + ++wlen; + } + /* Everything that's input counts as a letter. */ + ++ltrs; + } + + printf("\nWORD LENGTH FREQUENCY\n "); + for (i = 5; i < 80; i += 5) { + printf(" %2d", i); + } + + printf("\n"); // End the chart heading. + j = MINWLENGTH; + while (j <= MAXWLENGTH) { + i = lengths[j]; + if (i > 0) { + printf("%2d | ", j); + while (i > 0) { + printf("#"); + i = i-1; + } + printf("\n"); + } + ++j; + } + printf("%d words, %d chars, %d lines.\n", wrds, ltrs, lines); + return 0; +} diff --git a/ch1/1-14_character-freq-histogram.c b/ch1/1-14_character-freq-histogram.c new file mode 100644 index 0000000..14c4871 --- /dev/null +++ b/ch1/1-14_character-freq-histogram.c @@ -0,0 +1,51 @@ +#include <stdio.h> +int main(void) { + /* Rundown of variables: + c = current input char + ltrs = letter count + chars = string containing the characters the program will count + lengths = the counts for each character + */ + + int c; + int i = 0; + char chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + /* cnum is the number of characters found in the above string */ + int cnum = 0; + while (chars[i] != '\0') { + cnum += 1; + ++i; + } + + /* This array need its members to be initialized to zero. */ + int lengths[cnum]; + for (i = 0; i <= cnum; ++i) { + lengths[i] = 0; + } + + // Capture input until it ends + while ((c = getchar()) != EOF) { + for (i = 0; i < cnum; ++i) { + if (c == chars[i]) { + lengths[i] += 1; + } + } + } + // This is ugly and I wish I knew a better way to do it. + printf("\nCHARACTER FREQUENCY\n\n 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75\n"); + int iter = 0; + while (iter <= cnum) { + i = lengths[iter]; + if (i > 0) { + printf("%2c | ", chars[iter]); + while (i > 0) { + printf("#"); + i -= 1; + } + printf("\n"); + } + ++iter; + } + return 0; +} diff --git a/ch1/1-15_temp-convert-func.c b/ch1/1-15_temp-convert-func.c new file mode 100644 index 0000000..aa59cf6 --- /dev/null +++ b/ch1/1-15_temp-convert-func.c @@ -0,0 +1,18 @@ +#include <stdio.h> + +#define MAX_F 300.0 +#define STEP 20.0 + +float convert_to_c(float f) { + float celsius = (5.0 / 9.0) * (f - 32.0); + return celsius; +} + +int main() { + printf("FAHRENHEIT CELSIUS\n"); + float i; + for (i = 0.0; i <= MAX_F; i += STEP) { + printf("%4.0f %7.3f\n", i, convert_to_c(i)); + } + return 0; +} diff --git a/ch1/1-16_longest-line.c b/ch1/1-16_longest-line.c new file mode 100644 index 0000000..1f6e556 --- /dev/null +++ b/ch1/1-16_longest-line.c @@ -0,0 +1,52 @@ +#include <stdio.h> + +#define MAXLENGTH 100 + +int get_line(char s[], int lim) { + /* Put as much as possible into a temp string, and count its length */ + int c, i; + + for (i = 0; i < lim && (c = getchar()) != EOF && c != '\n'; ++i) { + s[i] = c; + } + if (c == '\n') { + s[i] = c; + ++i; + } + s[i] = '\0'; + return i; +} + +void copy(char from[], char to[]) { + int i = 0; + + while ((to[i] = from[i]) != '\0') { + ++i; + } +} + +int main() { + int len, max; + + char line[MAXLENGTH]; + char longest[MAXLENGTH]; + + max = 0; + while ((len = get_line(line, MAXLENGTH)) > 0) { + if (len > max) { + max = len; + copy(line, longest); + } + } + + printf("\nThe longest line is %3d characters long.\n", max); + printf("----------------------------------------\n"); + if (max > 0) { + printf("%-s", longest); + if (max == MAXLENGTH && longest[max - 1] != '\n') { + printf("\n"); + } + } + + return 0; +} diff --git a/ch1/1-17_over-80.c b/ch1/1-17_over-80.c new file mode 100644 index 0000000..53f00ce --- /dev/null +++ b/ch1/1-17_over-80.c @@ -0,0 +1,47 @@ +#include <stdio.h> + +#define MINLENGTH 80 + +int main() { + // longline is used as a boolean that tells us if it's a line worth printing + int longline = 0; + + // len is simply a character counter, while c is the character itself. + int len, c; + char buffer[MINLENGTH]; + + while ((c = getchar()) != EOF) { + buffer[len] = c; + + /* When we meet the end of the line, we need to print the rest of the line, + but only if we're already in a long line. Otherwise, reset our state. + */ + if (c == '\n') { + + if (longline == 1 && len < MINLENGTH - 1) { + buffer[len + 1] = '\0'; + printf("%-s", buffer); + } + + len = 0; + longline = 0; + continue; + } + + /* When the buffer has filled up, output its contents! */ + if (len == MINLENGTH) { + buffer[len + 1] = '\0'; + printf("%-s", buffer); + len = 0; + longline = 1; + continue; + } + + /* If neither of the above cases are caught, increment our counter and fetch + more data. + */ + ++len; + } + + return 0; +} diff --git a/ch1/1-18_strip-blanks.c b/ch1/1-18_strip-blanks.c new file mode 100644 index 0000000..c766445 --- /dev/null +++ b/ch1/1-18_strip-blanks.c @@ -0,0 +1,38 @@ +#include <stdio.h> + +#define MAXLINELENGTH 9001 +/* Write a program to remove trailing blanks and tabs from each line of input, + and to delete entirely blank lines. */ + +int get_line(char s[], int lim) { + int c, i; + + for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) { + s[i] = c; + } + if (c == '\n') { + s[i] = c; + ++i; + } + s[i + 1] = '\0'; + return i; +} + +int main(void) { + char buffer[MAXLINELENGTH]; + int c, i, len; + + /* Make sure every line is gone over */ + while (len = get_line(buffer, MAXLINELENGTH)) { + /* An empty for statement, simply to change the i variable. */ + for (i = len - 2; (i > 0) && (buffer[i] == ' ') || (buffer[i] == '\t'); --i); + + /* We've reached the end of the line's actual content. Terminate the line. */ + if (i >= 1) { + buffer[i + 1] = '\n'; + buffer[i + 2] = '\0'; + printf("%s", buffer); + } + } +} + diff --git a/ch1/1-19_reverse-lines.c b/ch1/1-19_reverse-lines.c new file mode 100644 index 0000000..d87b625 --- /dev/null +++ b/ch1/1-19_reverse-lines.c @@ -0,0 +1,54 @@ +#include <stdio.h> + +#define MAXLINELENGTH 9001 + +int get_line(char s[], int limit) { + int c, i; + + for (i = 0; i < limit && (c = getchar()) != EOF && c != '\n'; ++i) { + s[i] = c; + } + + s[i] = '\0'; + + /* If I don't include this check, I can't handle blank lines */ + if (c == EOF && i == 0) { + return -1; + } else { + return i; + } + +} + +/* Directly reverse a line's contents. */ +void reverse(char input[], int size) { + int tmp; + int i = 0; + size--; + + /* If len and i are the same, then there's no reason to proceed */ + while (size > i) { + // Store the first character in a temporary spot... + tmp = input[i]; + + // ... and swap! + input[i] = input[size]; + input[size] = tmp; + + // Bring our numbers closer together + ++i; + --size; + } +} + +int main(void) { + // An int and a string to store each line's data in + int line_len; + char buffer[MAXLINELENGTH]; + + while ((line_len = get_line(buffer, MAXLINELENGTH)) != -1) { + reverse(buffer, line_len); + printf("%s\n", buffer); + } + return 0; +} diff --git a/ch1/1-20_detab.c b/ch1/1-20_detab.c new file mode 100644 index 0000000..dc8f5b5 --- /dev/null +++ b/ch1/1-20_detab.c @@ -0,0 +1,48 @@ +#include <stdio.h> + +/* The C Programming Language: 2nd Edition + * Exercise 1-20: + * "Write a program `detab` that replaces tabs in the input with the proper + * number of blanks to space to the next tabstop. Assume a fixed set of + * tabstops, say every 'n' columns. Should 'n' be a variable or a symbolic + * parameter?" + * + * Answer: 'n' should be a symbolic parameter. It's more apparent what's being + * worked with and it's not susceptible to scope. Though, in this simple + * program it really doesn't matter. + * + * The "correct" solution uses the isprint() stdlib function, but it's not + * covered by this point in the book, so I did not use it. + */ + +#define TABWIDTH 8 + +int main(void) { + int column, c; + column = 0; + while ((c = getchar()) != EOF) { + // Be sure that the character is a tab + if (c == '\t') { + /* + * Divide a line by TABWIDTH and you have your tabstops. If you + * modulo by TABWIDTH and it equals 0, you've reached a tabstop and + * don't need to output more spaces! + */ + while (column % TABWIDTH != 0 && column != 0) { + putchar(' '); + ++column; + } + } else { + if (c == '\n') { + // Line-endings should reset the column counter after being output. + putchar(c); + column = 0; + } else { + // Now we can just output and increase column! + putchar(c); + ++column; + } + } + } + return 0; +} diff --git a/ch1/1-21_entab.c b/ch1/1-21_entab.c new file mode 100644 index 0000000..6037a14 --- /dev/null +++ b/ch1/1-21_entab.c @@ -0,0 +1,57 @@ +#include <stdio.h> + +/* The C Programming Language: 2nd Edition + * Exercise 1-21: + * "Write a program `entab` that replaces strings of blanks by the minimum + * number of tabs and blanks to achieve the same spacing. Use the same tab + * stops as for `detab`. When either a tab or a single blank would suffice to + * reach a tab stop, which should be given preference?" + * + * Answer: A blank. A tab character that comes after (tabstop - 1) blanks makes + * little-to-no sense and could mess up alignment in some environments. + * + */ + +#define TABWIDTH 8 + +int main(void) { + int column, c, spaces; + spaces = column = 0; + while ((c = getchar()) != EOF) { + // First thing's first, advance by a column. + column++; + + if (c == ' ') { + /* Add to 'spaces' immediately, we'll decide if it needs to be + * output later. + */ + spaces++; + + if (column % TABWIDTH == 0 && spaces > 0) { + putchar('\t'); + spaces = 0; // No spaces are left when we tab! + } + + } else { + /* Be sure to output any leftover spaces when we come across a + * non-space character. This should allow for spaces between words + * that don't fall along the tabstop lines. + */ + + while (spaces > 0) { + putchar(' '); + spaces--; + } + + // As usual, reset things on a newline. + if (c == '\n') { + column = 0; + spaces = 0; + } + + // Now we can output whatever it is. + putchar(c); + } + } + return 0; +} diff --git a/ch1/1-22_wordwrap.c b/ch1/1-22_wordwrap.c new file mode 100644 index 0000000..83acfd6 --- /dev/null +++ b/ch1/1-22_wordwrap.c @@ -0,0 +1,75 @@ +#include <stdio.h> + +/* The C Programming Language: 2nd Edition + * Exercise 1-22: + * "Write a program to 'fold' long input lines into two or more shorter lines + * after the last non-blank character that occurs before the n-th column of + * input. Make sure your program does something intelligent with very long + * lines, and if there are no blanks or tabs before the specified column." + * + * So... Quite a hefty requirement. In a nutshell, our goal is to create sane + * hard-wrapping. This is a common function in text editors, and it's + * important to get it right or the results are wonky. + * + * + * TODO: get_line() is not fully correct. When it hits a \t, it counts it as + * one character and has no concept of display count. I'll fix this later on. + */ + +// For tradition's sake, let's wrap at 80 columns +#define MAXLEN 80 + +char data[MAXLEN]; +int i, j, k; + +int get_line(char s[], int lim) { + /* Put as much as possible into a temp string, and count its length */ + int c, i; + + for (i = 0; i < lim && (c = getchar()) != EOF && c != '\n'; ++i) { + s[i] = c; + } + if (c == '\n') { + s[i] = c; + ++i; + } + s[i] = '\0'; + return i; +} + +/* Find the first blank character, starting from the end of the string. Returns + * the position of the blank, or -1 if one wasn't found. + */ +int b_find_blank(char s[], int lim) { + // Start at the end of the string and go backwards. + for (i = lim; i >= 0; i--) { + // Simply replace the first blank with a newline. + if (s[i] == ' ' || s[i] == '\t') { + return i; + } + } + return -1; +} + +int main() { + while (j = get_line(data, MAXLEN)) { + if (j == 80) { + // We know it's a long line now. Let's make sure we're breaking in + // the right place + k = b_find_blank(data, MAXLEN); + //printf("%d\n", k); + if (k > -1) { + data[k] = '\n'; + data[MAXLEN] = '\0'; + printf("%s", data); + continue; + } else { + data[MAXLEN] = '\0'; + printf("%s\n", data); + continue; + } + } + printf("%s", data); + } + +} diff --git a/ch1/1-23_decomment.c b/ch1/1-23_decomment.c new file mode 100644 index 0000000..63193cd --- /dev/null +++ b/ch1/1-23_decomment.c @@ -0,0 +1,84 @@ +#include <stdio.h> + +/* The C Programming Language: 2nd Edition + * Exercise 1-23: + * "Write a program to remove all comments from a C program. Don't forget to + * handle quoted strings and character constants properly. C comments do not + * nest." + * + * I'm not sure why the instructions tell the reader to account for quoted + * strings and character constants. Comments always begin with either // or + * /*, so that sets the boundary of state. + * + * The states for this program are IN_MULTI for a multi-line comment, + * IN_SINGLE for a single line comment, and OUT for being outside a comment. + * These three states are all I need to determine whether I should output the + * contents of the C file or not. Characters are only output when the state is + * OUT, and there are conditions for getting into and out of comment state. + * + * I don't know of any other way to do this without invoking some higher-level + * libraries. + */ + +#define IN_MULTI 1 +#define IN_SINGLE 2 +#define OUT 0 + +char c, p; +int status; + +int main() { + // Treat input like a stream, since that's all the K&R has explained so far. + while ((c = getchar()) != EOF) { + + // Check for the ways to open a comment, and set state accordingly + if (c == '/' && status == OUT) { + // Look ahead and store the character that's returned + p = getchar(); + + if (p == '*') { + status = IN_MULTI; + } else if (p == '/') { + status = IN_SINGLE; + } else { + putchar(c); + putchar(p); + continue; + } + } + + // Ignore everything in a single line comment until a newline + if (status == IN_SINGLE) { + + if (c == '\n') { + putchar(c); + status = OUT; + continue; + } + + } + + // Ignore everything until you reach the end of a multi comment + if (status == IN_MULTI && c == '*') { + p = getchar(); + + if (p == '/') { + status = OUT; + continue; + } + + } + + // Output everything when we're not in a comment! + if (status == OUT) { + putchar(c); + } + + } + + /* derpsauce + * + * + * Testing comment for the lulz. lolololol + */ +} diff --git a/ch1/1-24_syntax-checker.c b/ch1/1-24_syntax-checker.c new file mode 100644 index 0000000..019b9bc --- /dev/null +++ b/ch1/1-24_syntax-checker.c @@ -0,0 +1,163 @@ +#include <stdio.h> + +/* The C Programming Language: 2nd Edition + * Exercise 1-24: + * "Write a program to check a C program for rudimentary syntax errors like + * unbalanced parentheses, brackets, and braces. Don't forget about quotes, both + * single and double, escape sequences, and comments. (This program is hard if + * you do it in full generality.)" + * + * Proksima from Freenode's ##c helped me understand full generality where + * Ixquick, Wikipedia, and StackOverflow all failed: a program that has full + * generality handles all use cases. In this case, my program should report no + * errors from a well formed C source file, and return correct errors for every + * non-valid C source file. + * + * I can tackle this one the same way I tackled the previous exercise: with a + * FSM. The trick is in catching the mismatched levels. + * + * Post-solution note: switch() would've made this MUCH shorter... + */ + +char c; + +/* Set the state/count variables */ +int linenr, parens, brackets, braces, singqs, dubqs, escapes, mcomms, scomms = 0; + +int main() { + linenr = 1; + // Begin streaming! + while ((c = getchar()) != EOF) { + if (scomms == 1) { + if (c == '\n') { + scomms--; + } else { + continue; + } + } else if (mcomms == 1) { + if (c == '*') { + if (getchar() == '/') { + mcomms--; + } + } + } else { + // Check for escape sequences + if (c == '\\') { + escapes++; + c = getchar(); + // This does not detect all sequences; just the ones covered in Chapter 1. + if (c != '\\' && c != 't' && c != '\'' && c != '"' && c != 'n' && c != 'b' && c != '0') { + break; + } else { + escapes--; + } + } + // Newline behavior + if (c == '\n') { + if (singqs > 0 || dubqs > 0) { + break; + } + linenr++; + } + // Parentheses + if (c == '(') { + parens++; + } + if (c == ')') { + parens -= 1; + if (parens < 0) { + break; + } + } + // Brackets + if (c == '[') { + brackets++; + } + if (c == ']') { + brackets--; + if (brackets < 0) { + break; + } + } + // Braces + if (c == '{') { + braces++; + } + if (c == '}') { + braces--; + if (braces < 0) { + break; + } + } + // Double quotes + if (c == '"') { + if (dubqs == 0) { + dubqs++; + } else { + dubqs--; + } + } + // Single quotes, which are only checked outside of doubles. + if (c == '\'' && dubqs == 0) { + if (singqs == 0) { + singqs++; + } else { + singqs--; + } + } + // Comment detection + if (c == '/') { + if (getchar() == '*') { + mcomms++; + } + if (getchar() == '/') { + scomms++; + } + } + } + } + + if (escapes > 0) { + printf("Invalid escape sequence on line %d!\n", linenr); + return 1; + } + if (singqs > 0) { + printf("Unclosed single quote on line %d!\n", linenr); + return 1; + } + if (dubqs > 0) { + printf("Unclosed double quote on line %d!\n", linenr); + return 1; + } + if (brackets > 0) { + printf("Unclosed brackets on line %d!\n", linenr); + return 1; + } + if (brackets < 0) { + printf("Too many close brackets on line %d!\n", linenr); + return 1; + } + if (parens > 0) { + printf("Unclosed parenthesis on line %d!\n", linenr); + return 1; + } + if (parens < 0) { + printf("Too many close parentheses on line %d!\n", linenr); + return 1; + } + if (braces > 0) { + printf("Unclosed braces on line %d!\n", linenr); + return 1; + } + if (braces < 0) { + printf("Too many close braces on line %d!\n", linenr); + return 1; + } + if (mcomms == 1) { + printf("Unclosed comment at end of file!\n"); + return 1; + } + + printf("All clean.\n"); + return 0; +} |