commit 9890fece0f441087c9c6d3996a908345e262fbe1 Author: Tibor Bizjak Date: Tue May 2 13:51:17 2023 +0200 Inital commit. Solutions to k&r chapter one diff --git a/k&r/01-a-tutorial-introduction/01-01.c b/k&r/01-a-tutorial-introduction/01-01.c new file mode 100644 index 0000000..d9c1144 --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-01.c @@ -0,0 +1,6 @@ +#include + +main() +{ + printf("hello world\n"); +} diff --git a/k&r/01-a-tutorial-introduction/01-03.c b/k&r/01-a-tutorial-introduction/01-03.c new file mode 100644 index 0000000..78113f2 --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-03.c @@ -0,0 +1,20 @@ +#include + +main() +{ + float fahr, celsius; + int lower, upper, step; + + lower = 0; + upper = 300; + step = 20; + + printf("Fahr\tCels\n"); + + fahr = lower; + while (fahr <= upper) { + celsius = (5.0 / 9.0) * (fahr - 32.0); + printf("%4.0f\t%3.1f\n", fahr, celsius); + fahr = fahr + step; + } +} diff --git a/k&r/01-a-tutorial-introduction/01-04.c b/k&r/01-a-tutorial-introduction/01-04.c new file mode 100644 index 0000000..49f3186 --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-04.c @@ -0,0 +1,20 @@ +#include + +main() +{ + int fahr, celsius; + int lower, upper, step; + + lower = 0; + upper = 300; + step = 20; + + printf("Cels\tFahr\n"); + + celsius = lower; + while (celsius <= upper) { + fahr = (celsius / 5) * 9 + 32; + printf("%4d\t%4d\n", celsius, fahr); + celsius = celsius + step; + } +} diff --git a/k&r/01-a-tutorial-introduction/01-05.c b/k&r/01-a-tutorial-introduction/01-05.c new file mode 100644 index 0000000..51359b2 --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-05.c @@ -0,0 +1,13 @@ +#include + +#define LOWER 0 +#define UPPER 300 +#define STEP 20 + +main() +{ + int fahr; + + for (fahr = UPPER; fahr >= LOWER; fahr = fahr - STEP) + printf("%3d %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32)); +} diff --git a/k&r/01-a-tutorial-introduction/01-06.c b/k&r/01-a-tutorial-introduction/01-06.c new file mode 100644 index 0000000..0f95eef --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-06.c @@ -0,0 +1,6 @@ +#include + +void main() +{ + printf("%d\n", getchar() != EOF); +} diff --git a/k&r/01-a-tutorial-introduction/01-07.c b/k&r/01-a-tutorial-introduction/01-07.c new file mode 100644 index 0000000..4cd1f71 --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-07.c @@ -0,0 +1,6 @@ +#include + +void main() +{ + printf("%d\n", EOF); +} diff --git a/k&r/01-a-tutorial-introduction/01-08.c b/k&r/01-a-tutorial-introduction/01-08.c new file mode 100644 index 0000000..52d642e --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-08.c @@ -0,0 +1,20 @@ +#include + +void main() +{ + int c; + long bl, tb, nl; + + bl = tb = nl = 0; + + while ((c = getchar()) != EOF) + { + if (c == ' ') + ++bl; + if (c == '\t') + ++tb; + if (c == '\n') + ++nl; + } + printf("blanks : %ld\ntabs : %ld\nnewlines : %ld\n", bl, tb, nl); +} diff --git a/k&r/01-a-tutorial-introduction/01-09.c b/k&r/01-a-tutorial-introduction/01-09.c new file mode 100644 index 0000000..fc66efc --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-09.c @@ -0,0 +1,14 @@ +#include + +#define BLANK ' ' + +void main() +{ + int c, prev = -1; + + while ((c = getchar()) != EOF) { + if (prev != BLANK || c != BLANK) + putchar(c); + prev = c; + } +} diff --git a/k&r/01-a-tutorial-introduction/01-10.c b/k&r/01-a-tutorial-introduction/01-10.c new file mode 100644 index 0000000..123071f --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-10.c @@ -0,0 +1,17 @@ +#include + +void main() +{ + int c; + + while ((c = getchar()) != EOF) { + if (c == '\t') + printf("\\t"); + else if (c == '\b') + printf("\\b"); + else if (c == '\\') + printf("\\\\"); + else + putchar(c); + } +} diff --git a/k&r/01-a-tutorial-introduction/01-12.c b/k&r/01-a-tutorial-introduction/01-12.c new file mode 100644 index 0000000..3d311b8 --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-12.c @@ -0,0 +1,24 @@ +#include + +#define IN 0 +#define OUT 1 + +void main() +{ + int c, state = OUT; + + while ((c = getchar()) != EOF) { + if (c == ' ' || c == '\n' || c =='\t') { + if (state == IN) { + putchar('\n'); + state = OUT; + } + continue; + } + + putchar(c); + state = IN; + } + if (state == IN) + putchar('\n'); +} diff --git a/k&r/01-a-tutorial-introduction/01-13.c b/k&r/01-a-tutorial-introduction/01-13.c new file mode 100644 index 0000000..c97c0a1 --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-13.c @@ -0,0 +1,67 @@ +#include + +#define MAXLEN 10 + +/* print historgram of word lengths */ +void main() +{ + int c, i, len; + /* no word has len = 0; we subscribe with count-1 */ + /* the last element is the count of words wiht len > MAXLEN */ + int counts[MAXLEN + 1]; + + /* initializing */ + len = 0; + for (i = 0; i <= MAXLEN; ++i) + counts[i] = 0; + + /* counting */ + while ((c = getchar()) != EOF) { + if (c == ' ' || c == '\n' || c == '\t') { + if (len > 0) { + if (len > MAXLEN) + ++counts[MAXLEN]; + else + ++counts[len-1]; + len = 0; + } + continue; + } + ++len; + } + + int j, max, bound; + + bound = max = 0; + + for (i = 0; i <= MAXLEN; ++i) + if (counts[i] > 0) { + bound = i; + if (counts[i] > max) + max = counts[i]; + } + + /* printing horizontal */ + printf("----- horizontal -----\n"); + for (i = 0; i <= bound; ++i) { + if (i == MAXLEN) + printf(">%2d ", MAXLEN); + else + printf("%3d ", i + 1); + for (j = 0; j < counts[i]; ++j) + putchar('#'); + putchar('\n'); + } + + printf("\n\n"); + /* printing vertical */ + printf("----- vertical -----\n"); + for (i = max; i > 0; --i) { + for (j = 0; j <= bound; ++j) + if (counts[j] >= i) + putchar('#'); + else + putchar(' '); + putchar('\n'); + } +} diff --git a/k&r/01-a-tutorial-introduction/01-14.c b/k&r/01-a-tutorial-introduction/01-14.c new file mode 100644 index 0000000..3580b8c --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-14.c @@ -0,0 +1,41 @@ +#include + +#define CHARS 254 + +/* prints character frequency histogram */ +void main() +{ + int i, c; + int counts[CHARS]; + + /* initializing */ + for (i = 0; i < CHARS; ++i) + counts[i] = 0; + + /* counting */ + while ((c = getchar()) != EOF) { + ++counts[c]; + } + + /* printing */ + for (c = 0; c < CHARS; ++c) { + if (counts[c] == 0) + continue; + + if (c == '\n') + printf("\\n "); + else if (c == ' ') + printf("' ' "); + else if (c == '\t') + printf("\\t "); + else if (c == '\b') + printf("\\b "); + else + printf("%c ", c); + + for (i = 0; i < counts[c]; ++i) + putchar('#'); + + putchar('\n'); + } +} diff --git a/k&r/01-a-tutorial-introduction/01-15.c b/k&r/01-a-tutorial-introduction/01-15.c new file mode 100644 index 0000000..4d889e5 --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-15.c @@ -0,0 +1,22 @@ +#include + +#define LOWER 0 +#define UPPER 300 +#define STEP 20 + +float cels_of_fahr(float fahr); + +int main() +{ + int fahr; + + printf("%3c %6c\n", 'F', 'C'); + for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP) + printf("%3d %6.1f\n", fahr, cels_of_fahr(fahr)); + return 0; +} + +float cels_of_fahr(float fahr) +{ + return (5.0/9.0) * (fahr-32); +} diff --git a/k&r/01-a-tutorial-introduction/01-16.c b/k&r/01-a-tutorial-introduction/01-16.c new file mode 100644 index 0000000..3649280 --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-16.c @@ -0,0 +1,63 @@ +#include +#define MAXLINE 1000 + +int mygetline(char line[], int maxline); +void copy(char to[], char from[]); + +/* print longest input line */ +int main() +{ + int len, max; /* current, max lenght */ + char line[MAXLINE+1], longest[MAXLINE+1]; /* current, longest line; +1 for \0 */ + + max = 0; + while ((len = mygetline(line, MAXLINE)) > 0) + if (len > max) { + max = len; + copy(longest, line); + } + + if (max > 0) { + printf("%d ", max); + if (max <= MAXLINE && longest[max-1] == '\n') { + longest[max-1] = '\\'; + printf("\n%sn\n", longest); + } else if (max <= MAXLINE) { + printf("\n%s\n", longest); + } else + printf("(first %d chars)\n%s...\n", MAXLINE, longest); + } else + printf("No lines found\n"); + return 0; +} + +/* mygetline : read part of line smaller than lim-1 into s, return length */ +int mygetline(char s[], int maxline) +{ + int c, i; + + for (i=0; (c=getchar()) != EOF; ++i) { + if (i < maxline) + s[i] = c; + if (c == '\n') { + ++i; + break; + } + } + + if (i < maxline) + s[i] = '\0'; + else + s[maxline] = '\0'; + + return i; +} + +/* copy : copy 'from' to 'to'; assume 'to' is big enough */ +void copy(char to[], char from[]) +{ + int i; + + for(i = 0; (to[i] = from[i]) != '\0'; ++i) + ; +} diff --git a/k&r/01-a-tutorial-introduction/01-17.c b/k&r/01-a-tutorial-introduction/01-17.c new file mode 100644 index 0000000..7c97909 --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-17.c @@ -0,0 +1,30 @@ +#include + +/* print line number and lines longer than 80 chars (excluding \n) */ +int main() +{ + int c, i, ln; + char buff[81]; /* +1 for \0 */ + + ln = 1; + buff[80] = '\0'; + + for (i = 0; (c = getchar()) != EOF; ++i) { + if (c == '\n') { + if (i > 80) + putchar('\n'); + ++ln; + i = -1; + } else if (i < 80) { + buff[i] = c; + } else { + if (i == 80) + printf("%d %s", ln, buff); + putchar(c); + } + } + + if (i > 80 && c != '\n') + putchar('\n'); +} + diff --git a/k&r/01-a-tutorial-introduction/01-18.c b/k&r/01-a-tutorial-introduction/01-18.c new file mode 100644 index 0000000..b3ba2f7 --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-18.c @@ -0,0 +1,37 @@ +#include +#define MAXBUFF 1000 + +/* removes trailing spaces and tabs */ +int main() +{ + int i, c; + char buff[MAXBUFF+1]; /* +1 fo \0 */ + + i = 0; + buff[MAXBUFF] = '\0'; + + while ((c = getchar()) != EOF) { + if (c == ' ' || c == '\t') { + if (i < MAXBUFF) { + buff[i] = c; + ++i; + } else { + /* gracefully handle overflow */ + printf("%s", buff); + i = 0; + } + } else if (c == '\n') { + buff[0] = c; + i = 1; + } else { + if (i > 0) { + buff[i] = '\0'; + printf("%s", buff); + i = 0; + } + putchar(c); + } + } + return 0; +} + diff --git a/k&r/01-a-tutorial-introduction/01-19.c b/k&r/01-a-tutorial-introduction/01-19.c new file mode 100644 index 0000000..676f442 --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-19.c @@ -0,0 +1,61 @@ +#include +#define MAXLINE 1000 + +void reverse(char s[]); +int mystrlen(char s[]); +int get_line(char s[], int maxline); + +/* reverses input line by line */ +int main() +{ + int end = 0; + char line[MAXLINE + 1]; + + while (end != EOF) { + end = get_line(line, MAXLINE); + reverse(line); + printf("%s", line); + if (end != EOF) + putchar(end); + } +} + +/* reverses string 's' in place */ +void reverse(char s[]) +{ + char c; + int i, len = mystrlen(s); + + for (i = 0; i < len / 2; ++i) { + c = s[i]; + s[i] = s[len-i-1]; + s[len-i-1] = c; + } +} + +/* returns length of string 's' */ +int mystrlen(char s[]) +{ + int len; + for (len = 0; s[len] != '\0'; ++len) + ; + return len; +} + +/* reads (possibly truncated) line from input to s, returns \n or EOF */ +int get_line(char s[], int maxline) +{ + int c, i; + + for (i=0; (c=getchar()) != EOF && c != '\n'; ++i) { + if (i < maxline) + s[i] = c; + } + + if (i < maxline) + s[i] = '\0'; + else + s[maxline] = '\0'; + + return c; +} diff --git a/k&r/01-a-tutorial-introduction/01-20-detab.c b/k&r/01-a-tutorial-introduction/01-20-detab.c new file mode 100644 index 0000000..b20c037 --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-20-detab.c @@ -0,0 +1,48 @@ +#include +#include +#define TABSTOP 4 + +void detab(int tabstop); +int fill(int indent, int tabstop); + +/* detab input, takes one optional argument for tabstop width */ +int main(int argc, char *argv[]) +{ + int tabstop; + + if (argc < 2 || !(tabstop = atoi(argv[1]))) + detab(TABSTOP); + else + detab(tabstop); + + return 0; +} + + +/* detabs input with spaces, tab stop width is 'tabstop' */ +void detab(int tabstop) +{ + int c, indent = 0; /* indent starts at 0 */ + + while ((c = getchar()) != EOF) + if (c == '\t') + indent = fill(indent, tabstop); + else { + putchar(c); + indent = (c == '\n') ? 0 : indent + 1; + } +} + +/* fills output with spaces from indent to next tabstop */ +/* returns new indent */ +int fill(int indent, int tabstop) +{ + int w = tabstop - indent % tabstop; + + for (; w > 0; --w) { + putchar(' '); + ++indent; + } + + return indent; +} diff --git a/k&r/01-a-tutorial-introduction/01-21-entab.c b/k&r/01-a-tutorial-introduction/01-21-entab.c new file mode 100644 index 0000000..7007d7c --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-21-entab.c @@ -0,0 +1,44 @@ +#include +#include +#define TABSTOP 4 + +void entab(int tabstop); + +/* entab input, takes one optional argument for tabstop width */ +int main(int argc, char *argv[]) +{ + int tabstop; + + if (argc < 2 || !(tabstop = atoi(argv[1]))) + entab(TABSTOP); + else + entab(tabstop); + + return 0; +} + + +/* entabs input with spaces, tab stop width is 'tabstop' */ +void entab(int tabstop) +{ + int c, indent; /* indent starts at 1 */ + int bn = 0; /* current number of blanks */ + + for (indent = 1; (c = getchar()) != EOF; ++indent) + if (c == ' ') { + if (indent % tabstop || bn == 0) + /* dont entab single spaces */ + ++bn; + else { + bn = 0; + putchar('\t'); + } + } else { + for(; bn > 0; --bn) + putchar(' '); + putchar(c); + if (c == '\n') + indent = 0; + } +} + diff --git a/k&r/01-a-tutorial-introduction/01-22-fold.c b/k&r/01-a-tutorial-introduction/01-22-fold.c new file mode 100644 index 0000000..9c75ae1 --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-22-fold.c @@ -0,0 +1,117 @@ +#include +#include +#include + +#define TABSTOP 8 +#define WIDTH 80 +// the RATIO can't be less than 1/2 +#define RATIO 2.0/3.0 +#define BREAKSTR "-" + +int bstrlen = strlen(BREAKSTR); + +void fold(int width, int maxind); +void flush_wbuff(int wbi, char wbuff[]); +int insert_break(char buff[], int wbi); +int is_blank(char c); +int mv_cur(int n, char c); + +/* fold input, takes one optional argument for max line width (excl \n) */ +int main(int argc, char *argv[]) +{ + int w; + + if (argc < 2 || (w = atoi(argv[1])) < 2) + fold(WIDTH, RATIO * WIDTH); + else + fold(w, RATIO * w); + + return 0; +} + +/* folds input lines to length, where minw <= length <= 'width' + * if a line has no blank spaces after 'minw' chars a BREAKSTR is inserted */ +void fold(int width, int minw) +{ + int c, n = 0; // n=0 is start of line + int wbi = 0; // word buffer index + char wbuff[width - minw]; // word buffer for words over minw + + while ((c = getchar()) != EOF) { + n = mv_cur(n, c); + + if (c == '\n' || (n >= width+1 && is_blank(c))) { + // handle newlines + flush_wbuff(wbi, wbuff); + putchar('\n'); + n = wbi = 0; + continue; + } + + if (n <= minw) // always print chars before minw + putchar(c); + else if (n <= width) { + /* add word (optionaly leading blank) after minw to wbuff + * empty buffer if new blank is found */ + if (is_blank(c)) { + flush_wbuff(wbi, wbuff); + wbi = 0; + } + wbuff[wbi] = c; + ++wbi; + } else { // one char over edge, c != ' ' + n = insert_break(wbuff, wbi) + 1; + putchar(c); + wbi = 0; + } + } +} + +void flush_wbuff(int wbi, char wbuff[]) +{ + printf("%.*s", wbi, wbuff); +} + +/* print buff and insert line break where its most fitting, + * return new cursor position + * '\n' is inserted before the last word or 'BREAKSTR\n' is inserted */ +int insert_break(char wbuff[], int wbi) +{ + int i; + + if (wbi == 0) { + putchar('\n'); + return 0; + } + + if (is_blank(wbuff[0])) { + putchar('\n'); + for (i=1; i < wbi; ++i) + putchar(wbuff[i]); + return wbi - 1; + } + + + + flush_wbuff(wbi-bstrlen, wbuff); + printf("%s\n", BREAKSTR); + for (i = wbi-bstrlen; i < wbi; ++i) + putchar(wbuff[i]); + return bstrlen; +} + +/* return new cursor position */ +int mv_cur(int n, char c) +{ + if (c == '\t') + return n + (TABSTOP - n % TABSTOP); + else if (c == '\b') + return n; + return n+1; +} + +int is_blank(char c) +{ + return (c == ' ' || c == '\t'); +} + diff --git a/k&r/01-a-tutorial-introduction/01-23-decomm.c b/k&r/01-a-tutorial-introduction/01-23-decomm.c new file mode 100644 index 0000000..cb571fa --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-23-decomm.c @@ -0,0 +1,139 @@ +#include +#define MAXBUFF 100 + +void ignore_line(void); +int ignore_multiline(void); +int pass_char_const(char fst); +int pass_string(char fst); + +int flush_buff(char buff[], int bi); +int is_blank(char c); + +/* remove comments from input; remove empty lines and trailing spaces + * left by removed comments; Check for the following errors: + * - invalid string literals + * - invalid char constants + * - non-terminating multi line comments */ +int main() +{ + int a, b; + + int bi = 0; // blank buffer index + char blank_buff[MAXBUFF]; // blank buffer + + for (a = getchar(); (b = getchar()) != EOF; a = b) + if (a == '/' && b == '*') { + bi = 0; + if (ignore_multiline()) { + printf("\nerror : no terminator for multiline comment\n"); + return 1; + } + b = getchar(); + } else if (a == '/' && b == '/') { + bi = 0; + ignore_line(); + putchar('\n'); + b = getchar(); + } else if (a == '\'') { + bi = flush_buff(blank_buff, bi); + putchar('\''); + if (pass_char_const(b)) { + printf("\nerror : invalid character constant\n"); + return 1; + } + b = getchar(); + } else if (a == '"') { + bi = flush_buff(blank_buff, bi); + putchar('"'); + if (pass_string(b)) { + printf("\nerror : invalid string literal\n"); + return 1; + } + b = getchar(); + } else if (is_blank(a)) { + if (bi >= MAXBUFF) + bi = flush_buff(blank_buff, bi); + blank_buff[bi] = a; + ++bi; + } else if (a == '\n') { + flush_buff(blank_buff, bi); + blank_buff[0] = a; + bi = 1; + } else { + bi = flush_buff(blank_buff, bi); + putchar(a); + } + + if (a != EOF) + putchar(a); + + return 0; +} + +/* ignore line of input */ +void ignore_line() +{ + int c; + while ((c = getchar()) != EOF && c != '\n') + ; +} + +/* ignore input until comment termination occurs + * return 1 if error otherwise 0 */ +int ignore_multiline() +{ + int a, b; + for (a = getchar(); (b = getchar()) != EOF; a = b) + if (a == '*' && b == '/') + return 0; + return 1; +} + +/* pass the tail of a character constant from input to ouput + * return 1 if error otherwise 0 */ +int pass_char_const(char fst) +{ + int c; + + putchar(fst); + if (fst == '\\') { + if ((c = getchar()) == EOF) + return 1; + putchar(c); + } + if ((c = getchar()) == EOF || c != '\'') + return 1; + putchar(c); + return 0; +} + +/* pass the tail (wihtout fst element) of a + * C string literal from input to output. + * 'fst' is the first character of the string tail + * return 1 if error otherwise 0 */ +int pass_string(char fst) +{ + int snd; + + putchar(fst); + for (; (snd = getchar()) != EOF; fst = snd) { + putchar(snd); + if (fst == '\n') + break; + if (snd == '"' && fst != '\\') + return 0; + } + return 1; +} + +/* print buffer 'buff' and return 0 */ +int flush_buff(char buff[], int bi) +{ + printf("%.*s", bi, buff); + return 0; +} + +int is_blank(char c) +{ + return c == ' ' || c == '\t' || c == '\b'; +} diff --git a/k&r/01-a-tutorial-introduction/01-24-check.c b/k&r/01-a-tutorial-introduction/01-24-check.c new file mode 100644 index 0000000..089e2c4 --- /dev/null +++ b/k&r/01-a-tutorial-introduction/01-24-check.c @@ -0,0 +1,242 @@ +#include +#include + +#define TABSTOP 8 +#define BUFFSIZE 100 +#define ESCAPED "0bnt\\" +#define LINEN 5 + +#define ERRMSG_SIZE 100 +#define ERR_ESC_EOF "expected escape character, got EOF" +#define ERR_ESC_INV "invalid escape character" +#define ERR_STR_TRM "expected string termination" +#define ERR_CHAR_EOF "expected character constant, got EOF" +#define ERR_CHAR_TRM "expected character constant termination" +#define ERR_COMM_TRM "expected multi-line comment termination" + +/* global input line and character counting */ +int ln = 1; +int cn = 0; +int ln_getchar() +{ + int c; + if ((c = getchar()) == '\n' || c == EOF) { + cn = 0; + ++ln; + } else + ++cn; + return c; +} +#define getchar() ln_getchar() + +int print_context(); + +int check_escaped(char errmsg[], int d); +int check_multiline(char errmsg[]); +int check_char_const(char errmsg[]); +int check_string(char errmsg[]); + +int getcharb(); +int ithchar(int i); +int prevchar(); +int print_buff(int n); + +void print_line(); +void ignore_line(); +void copy(char from[], char to[]); + +/* check c program on input for unabalanced parantheses + * string termination, comment termination and char constant + * validity */ +int main() +{ + int i, c; + int err = 0; + char errmsg[ERRMSG_SIZE]; + + + while (!err && (c = getcharb()) != EOF) { + if (c == '"' && check_string(errmsg)) { + err = 1; + } else if (c == '\'' && check_char_const(errmsg)) { + err = 2; + } else if (prevchar() == '/' && c == '*' && check_multiline(errmsg)) { + err = 3; + } else if (c == '/' && prevchar() == '/') { + ignore_line(); + } + } + + if (!err) + return 0; + + int li = print_context(); + printf("%d:%d: error: %s\n", ln-1, li+1, errmsg); + return 0; +} + + + +/* print context of error and arrow pointing to cursor + * position where error was raised */ +int print_context() +{ + int i, li = print_buff(LINEN); + + if (li < 0) { + for (i = li; i < -1; ++i) + putchar(' '); + } else { + print_line(); + for (i = li; i > 0; --i) + putchar(' '); + } + printf("^\n"); + return li; +} + + +// ---------------------- validation functions ------------------------ +/* check multiline comment termination */ +int check_multiline(char errmsg[]) +{ + int c; + while ((c = getcharb()) != EOF) + if (prevchar() == '*' && c == '/') + return 0; + copy(ERR_COMM_TRM, errmsg); + return 1; +} + +/* check char constant validity and termination */ +int check_char_const(char errmsg[]) +{ + int c; + + if ((c = getcharb()) == EOF) { + copy(ERR_CHAR_EOF, errmsg); + return 1; + } + if (c == '\\' && check_escaped(errmsg, '\'')) + return 1; + if ((c = getcharb()) == EOF || c != '\'') { + copy(ERR_CHAR_TRM, errmsg); + return 1; + } + return 0; +} + +/* check string termination */ +int check_string(char errmsg[]) +{ + int c; + while ((c = getcharb()) != EOF && c != '\n') { + if (c == '\\' && check_escaped(errmsg, '"')) + return 1; + else if (c == '"') + return 0; + } + copy(ERR_STR_TRM, errmsg); + return 1; +} + +/* check if \(next input char) makes a valid escape char + * \'d' is considered valid, pass negative 'd' to make optional */ +int check_escaped(char errmsg[], int d) +{ + int i, c = getcharb(); + + if (c == EOF) { + copy(ERR_ESC_EOF, errmsg); + return 1; + } + if (c == d) + return 0; + for (i = 0; i < strlen(ESCAPED); ++i) + if (ESCAPED[i] == c) + return 0; + copy(ERR_ESC_INV, errmsg); + return 1; +} + +// ------------------------- buffered input ------------------------- +int ibi = 0; +int iblen = 0; +int ibtrunc = 0 +/* cyclic input stream buffer + * 'ibi' is the current buffer index + * 'iblen' is the buffer length + * 'ibtrunc' is the number of characters truncated + * from first line in buffer */ +char in_buff[BUFFSIZE]; + + + +/* get char from input, put it in buffer and return it + * otherwise same as getchar */ +int getcharb() +{ + int c; + if ((c = getchar()) == EOF) + return EOF; + ibtrunc = (iblen == BUFFSIZE && in_buff[ibi] == '\n') ? 0 : ibtrunc + 1; + in_buff[ibi] = c; + ++ibi; + if (iblen < BUFFSIZE) + ++iblen; + else if (ibi >= BUFFSIZE) + ibi = 0; + + return c; +} + +/* return ith char of buffer indexed from 'ibi' + * doesnt check validity of i */ +int ithchar(int i) +{ + int ri = ibi + i; // real index + return (ri < iblen) ? in_buff[ri] : in_buff[ri - iblen]; +} + +/* returns second last buffered input char + * if there is none return -1 */ +int prevchar() +{ + return (iblen > 1) ? ithchar(iblen-2) : -1; +} + + +/* return the (relative) index of the start of relevant context in buffer */ +int context_start(int n) +{ + +} + + +// ----------------------- utility functions -------------------------- +/* ignore line of input */ +void ignore_line() +{ + int c; + while ((c = getcharb()) != EOF && c != '\n') + ; +} + +/* print first line of input */ +void print_line() +{ + int c; + while ((c = getchar()) != EOF && c != '\n') + putchar(c); + putchar('\n'); +} + +/* copy from 'from' to 'to' + * 'to' must be large enough */ +void copy(char from[], char to[]) +{ + int i; + for (i = 0; from[i] != '\0'; ++i) + to[i] = from[i]; + to[i] = '\0'; +}