aboutsummaryrefslogtreecommitdiff
path: root/ch1
diff options
context:
space:
mode:
authorzlg <zlg@zlg.space>2013-02-13 20:48:44 -0600
committerzlg <zlg@zlg.space>2013-02-13 20:48:44 -0600
commit5018e06c580dd21c958ec1672c26a3448faf0c55 (patch)
treecebbb56dad0a6b821cad3712c7977f6f9b0086ab /ch1
parentFix 1-09's solution (diff)
downloadknr-5018e06c580dd21c958ec1672c26a3448faf0c55.tar.gz
knr-5018e06c580dd21c958ec1672c26a3448faf0c55.tar.bz2
knr-5018e06c580dd21c958ec1672c26a3448faf0c55.tar.xz
knr-5018e06c580dd21c958ec1672c26a3448faf0c55.zip
Add license file, reorganize project
Diffstat (limited to 'ch1')
-rw-r--r--ch1/1-01_hello-world.c14
-rw-r--r--ch1/1-02_escape-sequences.c15
-rw-r--r--ch1/1-03_temp-table-header.c29
-rw-r--r--ch1/1-04_celsius_converter.c27
-rw-r--r--ch1/1-05_backwards-table.c29
-rw-r--r--ch1/1-06_eof-test.c15
-rw-r--r--ch1/1-07_eof-value.c13
-rw-r--r--ch1/1-08_space-counter.c29
-rw-r--r--ch1/1-09_single-spacing.c29
-rw-r--r--ch1/1-10_literal-escapes.c33
-rw-r--r--ch1/1-11_word-count.c37
-rw-r--r--ch1/1-12_one-word-per-line.c41
-rw-r--r--ch1/1-13_word-length-histogram.c76
-rw-r--r--ch1/1-14_character-freq-histogram.c51
-rw-r--r--ch1/1-15_temp-convert-func.c18
-rw-r--r--ch1/1-16_longest-line.c52
-rw-r--r--ch1/1-17_over-80.c47
-rw-r--r--ch1/1-18_strip-blanks.c38
-rw-r--r--ch1/1-19_reverse-lines.c54
-rw-r--r--ch1/1-20_detab.c48
-rw-r--r--ch1/1-21_entab.c57
-rw-r--r--ch1/1-22_wordwrap.c75
-rw-r--r--ch1/1-23_decomment.c84
-rw-r--r--ch1/1-24_syntax-checker.c163
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;
+}