aboutsummaryrefslogtreecommitdiff
path: root/ch7
diff options
context:
space:
mode:
authorzlg <zlg@zlg.space>2016-06-16 09:51:16 -0700
committerzlg <zlg@zlg.space>2016-06-16 09:51:16 -0700
commit82656bf5aa4edd668e1adc3faa8148251509b912 (patch)
treeb0ba0ff73b90c875298077fc2dda5098c9ed87cd /ch7
parent1-16 solution code and comment style cleanup (diff)
downloadknr-82656bf5aa4edd668e1adc3faa8148251509b912.tar.gz
knr-82656bf5aa4edd668e1adc3faa8148251509b912.tar.bz2
knr-82656bf5aa4edd668e1adc3faa8148251509b912.tar.xz
knr-82656bf5aa4edd668e1adc3faa8148251509b912.zip
Solve Exercise 7-2: Format arbitrary input
The solution is technically not 100% correct, but good luck figuring out a robust solution inside Category-0 restrictions. Unicode characters like ¬ or ♥ show up as 64-bit hex codes and I couldn't find a way to shorten them. Then again, UTF-8 supports characters up to 8 bytes long. Use it with plain ASCII and it looks only minorly off.
Diffstat (limited to 'ch7')
-rw-r--r--ch7/7-02_arb-input.c101
1 files changed, 101 insertions, 0 deletions
diff --git a/ch7/7-02_arb-input.c b/ch7/7-02_arb-input.c
new file mode 100644
index 0000000..2c8fbc9
--- /dev/null
+++ b/ch7/7-02_arb-input.c
@@ -0,0 +1,101 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+/* The C Programming Language: 2nd Edition
+ *
+ * Exercise 7-2: Write a program that will print arbitrary input in a sensible
+ * way. As a minimum, it should print non-graphic characters in octal or
+ * hexadecimal according to local custom, and break long text lines.
+ *
+ * Notes: I spent some time thinking about this one. I'm somewhat picky in how
+ * I expect text to be handled, but the requirements are Spartan. Therefore, I
+ * will provide a Spartan solution. :)
+ *
+ * Linux printf formats don't seem to allow for truncation of hex or octal,
+ * so there wasn't an (easy, cat-zero friendly) way to work with the widths
+ * produced by the format strings. However, the program compiles, it has
+ * argument switches, it smartly breaks lines, and expands non-printable
+ * characters. It does exactly what it's supposed to. :)
+ */
+
+#define MAXLINELEN 81
+#define MAXBUF 102400
+
+#define HEX 16
+#define OCT 8
+
+int fmt;
+
+int mygetline(char s[], int max) {
+ int c, i;
+ i = 0;
+ while (max-- > 0 && (c = getchar()) != EOF && c != '\n') {
+ s[i++] = c;
+ }
+ if (c == '\n') {
+ s[i++] = c;
+ }
+ s[i] = '\0';
+ return i;
+}
+
+void fmt_print(char *s) {
+ int i = 0;
+ while (s[i] != '\0') {
+ if (isprint(s[i]) || s[i] == '\n') {
+ putchar(s[i]);
+ } else {
+ if (fmt == HEX) {
+ printf("%4X", s[i]);
+ } else {
+ printf("%3o", s[i]);
+ }
+ }
+ i++;
+ }
+}
+
+void break_lines(char *s) {
+ int fmt = HEX;
+ int c, nl, space;
+ for (c = 0, nl = 0, space = 0; s[c] != '\0'; c++) {
+ /* The position of the last space */
+ if (isspace(s[c])) {
+ space = c;
+ }
+ /* Position of the last new line */
+ if (s[c] == '\n') {
+ nl = c;
+ }
+ if (c == (nl + MAXLINELEN - 1)) {
+ if (space > nl) {
+ s[space] = '\n';
+ nl = space;
+ }
+ }
+ }
+}
+
+void parse_args(int argc, char *argv[]) {
+ int i;
+ while (argc > 1) {
+ --argc;
+ if (argv[argc][0] == '-') {
+ if (argv[argc][1] == 'o' && argv[argc][1] != '\0') {
+ fmt = OCT;
+ } else if (argv[argc][1] == 'h') {
+ fmt = HEX;
+ }
+ }
+ }
+}
+
+int main(int argc, char **argv) {
+ char buffer[MAXBUF];
+ parse_args(argc, argv);
+ while (mygetline(buffer, MAXBUF)) {
+ break_lines(buffer);
+ fmt_print(buffer);
+ }
+}