├── README.md ├── build └── CMakeLists.txt ├── chapter01 ├── 1-1.c ├── 1-10.c ├── 1-11.c ├── 1-12.c ├── 1-13.c ├── 1-13a.c ├── 1-14.c ├── 1-14a.c ├── 1-15.c ├── 1-16.c ├── 1-17.c ├── 1-18.c ├── 1-19.c ├── 1-2.c ├── 1-20.c ├── 1-21.c ├── 1-22.c ├── 1-23.c ├── 1-24.c ├── 1-3.c ├── 1-4.c ├── 1-5.c ├── 1-6.c ├── 1-7.c ├── 1-8.c └── 1-9.c ├── chapter02 ├── 2-1.c ├── 2-10.c ├── 2-2.c ├── 2-3.c ├── 2-4.c ├── 2-5.c ├── 2-6.c ├── 2-7.c ├── 2-8.c └── 2-9.c ├── chapter03 ├── 3-1.c ├── 3-2.c ├── 3-3.c ├── 3-4.c ├── 3-4a.c ├── 3-5.c └── 3-6.c ├── chapter04 ├── 4-1.c ├── 4-10.c ├── 4-11.c ├── 4-12.c ├── 4-13.c ├── 4-14.c ├── 4-2.c ├── 4-3.c ├── 4-4.c ├── 4-5.c ├── 4-6.c ├── 4-7.c ├── 4-8.c └── 4-9.c ├── chapter05 ├── 5-1.c ├── 5-10.c ├── 5-11 │ ├── detab.c │ └── entab.c ├── 5-12 │ ├── detab.c │ └── entab.c ├── 5-13.c ├── 5-14.c ├── 5-15.c ├── 5-16.c ├── 5-17.c ├── 5-18.c ├── 5-19.c ├── 5-2.c ├── 5-20.c ├── 5-3.c ├── 5-4.c ├── 5-5.c ├── 5-6 │ ├── Makefile │ ├── functions.c │ ├── functions.h │ └── main.c ├── 5-7-alloc.c ├── 5-7.c ├── 5-8.c └── 5-9.c ├── chapter06 ├── 6-1.c ├── 6-2.c ├── 6-3.c ├── 6-4.c ├── 6-5.c └── 6-6.c ├── chapter07 ├── 7-1.c ├── 7-2.c ├── 7-3.c ├── 7-4.c ├── 7-5.c ├── 7-5a.c ├── 7-6.c ├── 7-7.c ├── 7-8.c └── 7-9.c └── chapter08 ├── 8-1.c ├── 8-2.c ├── 8-3.c ├── 8-4-linebuf.c ├── 8-4.c ├── 8-5.c ├── 8-6.c ├── 8-7.c ├── 8-8.c └── dirent.h /README.md: -------------------------------------------------------------------------------- 1 | # Answers to The C Programming Language (Second Edition) by Brian W. Kernighan & Dennis M. Ritchie. 2 | 3 | These are the answers to the exercises in K&R's The C Programming Language 4 | (second Edition). 5 | 6 | Please let me know if you found a mistake or a bug. 7 | 8 | Cheers! 9 | 10 | ### Contents 11 | 12 | - [Chapter 1. A Tutorial Introduction](chapter01/) 13 | - [Chapter 2. Types, Operators, and Expressions](chapter02) 14 | - [Chapter 3. Control Flow](chapter03) 15 | - [Chapter 4. Functions and Program Structure](chapter04) 16 | - [Chapter 5. Pointers and Arrays](chapter05) 17 | - [Chapter 6. Structures](chapter06) 18 | - [Chapter 7. Input and Output](chapter07) 19 | - [Chapter 8. The UNIX System Interface](chapter08) 20 | -------------------------------------------------------------------------------- /chapter01/1-1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-1. Run the "hello, world" program on your system. Experiment with 3 | * leaving parts of the program, to see what error messages you get. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | int main(void) 11 | { 12 | printf("hello, world\n"); 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /chapter01/1-10.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-10. Write a program to copy its input to its output, replacing 3 | * each tab by \t, each backspace by \b, and each backslash by \\. This makes 4 | * tabs and backspaces visible in an unambiguous way. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | 11 | #define YES 1 12 | #define NO 0 13 | 14 | int main(void) 15 | { 16 | int c, charReplaced; 17 | 18 | while ((c = getchar()) != EOF) { 19 | charReplaced = NO; 20 | if (c == '\t') { 21 | printf("\\t"); 22 | charReplaced = YES; 23 | } 24 | if (c == '\b') { 25 | printf("\\b"); 26 | charReplaced = YES; 27 | } 28 | if (c == '\\') { 29 | printf("\\\\"); 30 | charReplaced = YES; 31 | } 32 | if (charReplaced == NO) 33 | putchar(c); 34 | } 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /chapter01/1-11.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-11. How would you test the word count program? What kinds of input 3 | * are most likely to uncover bugs if there are any? 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | /* 9 | * Answer: According to this definition of a word (characters grouped together), 10 | * means that inputs such as hyphenated words, word's with apostrophise, etc., 11 | * will be counted as one word. 12 | */ 13 | 14 | #include 15 | 16 | #define IN 1 /* inside a word */ 17 | #define OUT 0 /* outside a word */ 18 | 19 | int main(void) 20 | { 21 | int c, nl, nw, nc, state; 22 | 23 | state = OUT; 24 | nl = nw = nc = 0; 25 | while ((c = getchar()) != EOF) { 26 | ++nc; 27 | if (c == '\n') 28 | ++nl; 29 | if (c == ' ' || c == '\n' || c == '\t') 30 | state = OUT; 31 | else if (state == OUT) { 32 | state = IN; 33 | ++nw; 34 | } 35 | } 36 | printf("lines: %d words: %d character: %d\n", nl, nw, nc); 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /chapter01/1-12.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-12. Write a program that prints its input one word per line. 3 | * 4 | * By Faisal Saadatmand 5 | */ 6 | 7 | #include 8 | 9 | int main(void) 10 | { 11 | int c; 12 | 13 | while ((c = getchar()) != EOF) 14 | if (c == ' ' || c == '\n' || c == '\t') 15 | putchar('\n'); 16 | else 17 | putchar(c); 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /chapter01/1-13.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-13. Write a program to print a histogram of the lengths of words 3 | * in its input. It is easy to draw the histogram with the bars horizontal; a 4 | * vertical orientation is more challenging. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | /* Horizontal Histogram. See 1-13a.c for a vertical histogram implementation */ 10 | 11 | #include 12 | 13 | #define SIZE 5 /* size of lengths array */ 14 | #define SCALE 1 /* adjust to accommodate large input */ 15 | #define OUT 1 /* outside of a word */ 16 | #define IN 0 /* inside of a word */ 17 | 18 | int main(void) 19 | { 20 | int c, i, j, count, state; 21 | int lengths[SIZE]; /* words length ranges */ 22 | 23 | for (i = 0; i <= SIZE; ++i) 24 | lengths[i] = 0; 25 | 26 | state = OUT; 27 | count = 0; 28 | while ((c = getchar()) != EOF) { 29 | 30 | if (c == ' ' || c == '\t' || c == '\n') 31 | state = OUT; 32 | else 33 | state = IN; 34 | 35 | if (state == IN) 36 | ++count; 37 | 38 | if (state == OUT) { 39 | if (count < 4) 40 | ++lengths[0]; 41 | else if (count >= 4 && count < 8) 42 | ++lengths[1]; 43 | else if (count >= 8 && count < 12) 44 | ++lengths[2]; 45 | else if (count >= 12 && count < 14) 46 | ++lengths[3]; 47 | if (count >= 14) 48 | ++lengths[4]; 49 | count = 0; 50 | } 51 | } 52 | 53 | printf("\nHorizontal Histogram\n"); 54 | for (i = 0; i < SIZE; ++i) { 55 | printf(" %i\t", lengths[i]); 56 | for (j = 0; j < lengths[i] / SCALE; ++j) 57 | printf(" *"); 58 | printf("\n"); 59 | } 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /chapter01/1-13a.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-13. Write a program to print a histogram of the lengths of words 3 | * in its input. It is easy to draw the histogram with the bars horizontal; a 4 | * vertical orientation is more challenging. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | /* Vertical Histogram. See 1-13.c for a horizontal histogram implementation */ 10 | 11 | #include 12 | 13 | #define SIZE 5 /* size of lengths array */ 14 | #define SCALE 1 /* adjust to accommodate large input */ 15 | #define OUT 1 /* outside of a word */ 16 | #define IN 0 /* inside of a word */ 17 | 18 | int main(void) 19 | { 20 | int c, i, count, state, longestBar; 21 | int lengths[SIZE]; /* words length ranges */ 22 | 23 | for (i = 0; i <= SIZE; ++i) 24 | lengths[i] = 0; 25 | 26 | state = OUT; 27 | count = 0; 28 | while ((c = getchar()) != EOF) { 29 | 30 | if (c == ' ' || c == '\t' || c == '\n') 31 | state = OUT; 32 | else 33 | state = IN; 34 | 35 | if (state == IN) 36 | ++count; 37 | 38 | if (state == OUT) { 39 | if (count < 4) 40 | ++lengths[0]; 41 | else if (count >= 4 && count < 8) 42 | ++lengths[1]; 43 | else if (count >= 8 && count < 12) 44 | ++lengths[2]; 45 | else if (count >= 12 && count < 14) 46 | ++lengths[3]; 47 | if (count >= 14) 48 | ++lengths[4]; 49 | count = 0; 50 | } 51 | } 52 | 53 | printf("\nVertical Histogram:\n"); 54 | longestBar = lengths[0]; 55 | for (i = 0; i < SIZE; ++i) /* find the longestBar */ 56 | if (lengths[i] > longestBar ) 57 | longestBar = lengths[i]; 58 | longestBar /= SCALE; 59 | 60 | /* print vertical histogram */ 61 | while (longestBar > 0) { 62 | for (i = 0; i < SIZE; ++i) 63 | if (lengths[i] != 0) { 64 | if (lengths[i] / SCALE < longestBar) 65 | printf(" %2c", ' '); 66 | else 67 | printf(" %2c", '*'); 68 | } 69 | printf("\n"); 70 | --longestBar; 71 | } 72 | 73 | /* print value of each element (bar) */ 74 | for (i = 0; i < SIZE; ++i) 75 | if (lengths[i] != 0) 76 | printf(" %2i", lengths[i]); 77 | printf("\n"); 78 | 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /chapter01/1-14.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-14. Write a program to print a histogram of the frequencies of 3 | * different characters in its input. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | /* Horizontal Histogram. See 1-14a.c for a vertical histogram implementation */ 9 | 10 | #include 11 | 12 | #define SIZE 93 /* size of characters array */ 13 | #define SCALE 1 /* adjust to accommodate large input */ 14 | 15 | int main(void) 16 | { 17 | int c, i, j, count; 18 | int characters[SIZE]; 19 | 20 | /* initialize elements' values to 0 */ 21 | for (i = 0; i < SIZE; ++i) 22 | characters[i] = 0; 23 | 24 | count = 0; 25 | while ((c = getchar()) != EOF) 26 | if (c >= '!' && c <= '~') { /* graphical characters only (ASCII table) */ 27 | ++characters[c - '!']; 28 | ++count; /* number of matched characters */ 29 | } 30 | 31 | if (!count) 32 | return -1; 33 | 34 | printf("\nHorizontal Histogram: (scale 1:%i)\n", SCALE); 35 | for (i = 0; i < SIZE; ++i) 36 | if (characters[i] != 0) { /* skip if no data */ 37 | printf(" %c", i + '!'); /* labels */ 38 | for (j = 1; j <= characters[i] / SCALE; ++j) 39 | printf(" *"); 40 | printf("\n"); 41 | } 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /chapter01/1-14a.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-14. Write a program to print a histogram of the frequencies of 3 | * different characters in its input. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | /* Vertical Histogram. See 1-14.c for a horizontal histogram implementation */ 9 | 10 | #include 11 | 12 | #define SIZE 93 /* size of characters array */ 13 | #define SCALE 1 /* adjust to accommodate large input */ 14 | 15 | int main(void) 16 | { 17 | int c, i, count, longestBar; 18 | int characters[SIZE]; 19 | 20 | /* initialize elements' values to 0 */ 21 | for (i = 0; i < SIZE; ++i) 22 | characters[i] = 0; 23 | 24 | count = 0; 25 | while ((c = getchar()) != EOF) 26 | /* match graphical characters only (ASCII table) */ 27 | if (c >= '!' && c <= '~') { 28 | ++characters[c - '!']; 29 | ++count; /* number of matched characters */ 30 | } 31 | 32 | if (!count) 33 | return -1; 34 | 35 | printf("\nVertical Histogram:\n"); 36 | 37 | /* find the longestBar */ 38 | longestBar = 0; 39 | for (i = 0; i < SIZE; ++i) 40 | if (characters[i] > longestBar) 41 | longestBar = characters[i]; 42 | longestBar /= SCALE; 43 | 44 | /* print vertical histogram */ 45 | while (longestBar > 0) { 46 | for (i = 0; i < SIZE; ++i) 47 | if (characters[i] != 0) { /* skip if no data */ 48 | if (characters[i] / SCALE < longestBar) 49 | printf(" %c", ' '); 50 | else 51 | printf(" %c", '*'); 52 | } 53 | printf("\n"); 54 | --longestBar; 55 | } 56 | 57 | /* print labels */ 58 | for (i = 0; i < SIZE; ++i) 59 | if (characters[i] != 0) /* skip if no data */ 60 | printf (" %c", i + '!'); 61 | printf("\n"); 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /chapter01/1-15.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1.15. Rewrite the temperature conversion program of Section 1.2 to 3 | * use a function for conversion. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | /* functions */ 11 | float toCelsius(float); 12 | 13 | float toCelsius(float f) 14 | { 15 | return (5.0 / 9.0) * (f - 32.0); 16 | } 17 | 18 | int main(void) 19 | { 20 | float fahr, celsius; 21 | float lower, upper, step; 22 | 23 | lower = 0; /* lower limit of temperature scale */ 24 | upper = 300; /* upper limit */ 25 | step = 20; /* step size */ 26 | 27 | fahr = lower; 28 | printf("Fahrenheit\tCelsius\n"); 29 | while (fahr <= upper) { 30 | celsius = toCelsius(fahr); 31 | printf("%10.0f\t%7.1f\n", fahr, celsius); 32 | fahr = fahr + step; 33 | } 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /chapter01/1-16.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-16. Revise the main routine of the longest-line program so it 3 | * will correctly print the length of arbitrary long input lines, and print as 4 | * much as possible of the text. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | 11 | #define MAXLEN 1000 /* maximum input line length */ 12 | 13 | /* functionst */ 14 | int getLine(char [], int); 15 | void copy(char [], char []); 16 | 17 | /* getLine function: read a line into s, return length */ 18 | int getLine(char s[], int lim) 19 | { 20 | int c, i; 21 | 22 | for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) 23 | s[i] = c; 24 | 25 | if (c == '\n') { 26 | s[i] = c; 27 | ++i; 28 | } 29 | 30 | s[i] = '\0'; 31 | 32 | return i; 33 | } 34 | 35 | /* copy function: copy 'from' into 'to'; assume to is big enough */ 36 | void copy(char to[], char from[]) 37 | { 38 | int i; 39 | 40 | i = 0; 41 | while ((to[i] = from[i]) !='\0') 42 | ++i; 43 | } 44 | 45 | int main(void) 46 | { 47 | int len; /* current line length */ 48 | int nextLen; /* next line length */ 49 | int max; /* maximum length seen so far */ 50 | char line[MAXLEN]; /* current input line */ 51 | char nextLine[MAXLEN]; /* next input line */ 52 | char longest[MAXLEN]; /* longest line saved here */ 53 | 54 | max = 0; 55 | while ((len = getLine(line, MAXLEN)) > 0) { 56 | if (len == MAXLEN - 1) { /* is line longer than buffer size? */ 57 | line[MAXLEN - 1] = '\n'; 58 | nextLen = len; 59 | while (nextLen == MAXLEN - 1) { /* find the line's length */ 60 | nextLen = getLine(nextLine, MAXLEN); 61 | len += nextLen; 62 | } 63 | } 64 | if (len > max) { 65 | max = len; 66 | copy(longest, line); 67 | } 68 | } 69 | 70 | if (max > 0) /* there was a line */ 71 | printf("%s -> %i\n", longest, max); 72 | 73 | return 0; 74 | } 75 | -------------------------------------------------------------------------------- /chapter01/1-17.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-17. Write a program to print all input lines that are longer than 3 | * 80 characters. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | #define MAXLEN 1000 /* maximum input line length */ 11 | #define NCHARS 80 /* number of characters per line, 12 | including the newline character */ 13 | 14 | /* functions */ 15 | int getLine(char [], int); 16 | 17 | /* getLine function: read a line into s, return length */ 18 | int getLine(char s[], int lim) 19 | { 20 | int c, i; 21 | 22 | for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) 23 | s[i] = c; 24 | 25 | if (c == '\n') { 26 | s[i] = c; 27 | ++i; 28 | } 29 | 30 | s[i] = '\0'; 31 | 32 | return i; 33 | } 34 | 35 | int main(void) 36 | { 37 | int len; /* current line length */ 38 | char line[MAXLEN]; /* current input line */ 39 | 40 | while ((len = getLine(line, MAXLEN)) > 0) 41 | if (len > NCHARS) 42 | printf("%s", line); 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /chapter01/1-18.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-18. Write a program to remove trailing blanks and tabs from each 3 | * line of input, and to delete entirely blank lines. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | #define MAXLINE 1000 /* maximum input line length */ 11 | 12 | /* functions */ 13 | int getLine(char [], int); 14 | int rightTrim(char [], int); 15 | 16 | /* getLine function: read a line into s, return length */ 17 | int getLine(char s[], int lim) 18 | { 19 | int c, i; 20 | 21 | for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) 22 | s[i] = c; 23 | 24 | if (c == '\n') { 25 | s[i] = c; 26 | ++i; 27 | } 28 | 29 | s[i] = '\0'; 30 | 31 | return i; 32 | } 33 | 34 | int rightTrim(char s[], int len) 35 | { 36 | int nw = 0; 37 | 38 | if (s[--len] == '\n') { 39 | s[len] = '\0'; /* remove newline character */ 40 | nw = 1; /* set flag */ 41 | } 42 | 43 | while (--len >= 0 && (s[len] == ' ' || s[len] == '\t')) 44 | s[len] = '\0'; 45 | ++len; 46 | 47 | if (nw) 48 | s[len] = '\n'; /* add back the newline character */ 49 | 50 | return ++len; 51 | } 52 | 53 | int main(void) 54 | { 55 | int len; /* current line length */ 56 | char line[MAXLINE]; /* current input line */ 57 | 58 | while ((len = getLine(line, MAXLINE)) > 0) { 59 | len = rightTrim(line, len); 60 | if (len == 1 && line[0] == '\n') /* delete if line is empty */ 61 | line[0] = '\0'; 62 | printf("%s", line); 63 | } 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /chapter01/1-19.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-19. Write a function reverse(s) that reverses the character 3 | * string s. Use it to write a program that reverses its input a line at a 4 | * time. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | 11 | #define MAXLINE 1000 /* maximum input line length */ 12 | 13 | /* functions */ 14 | int getLine(char [], int); 15 | void reverse(char []); 16 | 17 | /* getLine function: read a line into s, return length */ 18 | int getLine(char s[], int lim) 19 | { 20 | int c, i; 21 | 22 | for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) 23 | s[i] = c; 24 | 25 | if (c == '\n') { 26 | s[i] = c; 27 | ++i; 28 | } 29 | 30 | s[i] = '\0'; 31 | 32 | return i; 33 | } 34 | 35 | /* reverse: reverses s's charaters in-place. */ 36 | void reverse(char s[]) 37 | { 38 | int i, j, len, temp; 39 | 40 | len = 0; 41 | for (i = 0; s[i] != '\0'; ++i) 42 | ++len; 43 | 44 | for (i = 0, j = len - 1; i < len / 2; ++i, --j) { 45 | temp = s[i]; 46 | s[i] = s[j]; 47 | s[j] = temp; 48 | } 49 | } 50 | 51 | int main(void) 52 | { 53 | int len; /* current line length */ 54 | char line[MAXLINE]; /* current input line */ 55 | 56 | while ((len = getLine(line, MAXLINE)) > 0) { 57 | line[--len] = '\0'; /* remove newline character at the end */ 58 | reverse(line); 59 | printf("%s\n", line); 60 | } 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /chapter01/1-2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-2. Experiment to find out what happens when prints' arguments 3 | * string contains \c, where c is some character not listed above. 4 | * 5 | * by Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | int main(void) 11 | { 12 | printf("\"hello,\t world\"\n"); 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /chapter01/1-20.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-20. Write a program detab function that replaces tabs in the 3 | * input with the proper number of blanks to space to the next tab stop. Assume 4 | * a fixed set of tab stops, say every n columns. Should n be a variable or a 5 | * symbolic parameter. 6 | * 7 | * By Faisal Saadatmand 8 | */ 9 | 10 | /* 11 | * Answer: Here, n should be a symbolic constant, for the value of n 12 | * should remain constant throughout the duration of the program. It could also 13 | * be a const int. 14 | */ 15 | 16 | #include 17 | 18 | #define MAXLEN 1000 19 | #define N 4 /* tabstop for every n columns */ 20 | 21 | /* functions */ 22 | int getLine(char [], int); 23 | 24 | /* getLine function: read a line into s, return length */ 25 | int getLine(char s[], int lim) 26 | { 27 | int c, i; 28 | 29 | for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) 30 | s[i] = c; 31 | if (c == '\n') { 32 | s[i] = c; 33 | ++i; 34 | } 35 | s[i] = '\0'; 36 | return i; 37 | } 38 | 39 | /* detab function: replaces tabs with the proper number of blanks */ 40 | void detab(char in[], char out[]) 41 | { 42 | int i; /* index for read line */ 43 | int j; /* index for modified (written) line */ 44 | int nblanks; /* number of blanks to the next tab stop */ 45 | 46 | for (i = j = 0; in[i] != '\0'; ++i) 47 | if (in[i] == '\t') { 48 | nblanks = N - (j % N); 49 | while (nblanks-- > 0) 50 | out[j++] = ' '; 51 | } else 52 | out[j++] = in[i]; 53 | out[j] = '\0'; 54 | } 55 | 56 | int main(void) 57 | { 58 | char in[MAXLEN]; /* currently read line */ 59 | char out[MAXLEN]; /* modified line */ 60 | 61 | while (getLine(in, MAXLEN) > 0) { 62 | detab(in, out); 63 | printf("%s", out); 64 | } 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /chapter01/1-21.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-21. Write a program entab that replaces strings of blanks by the 3 | * minimum number of tabs and blanks to achieve the same spacing. When either a 4 | * tab or a single blank would suffice to reach a tab stop, which should be give 5 | * preference? 6 | * 7 | * By Faisal Saadatmand 8 | */ 9 | 10 | /* 11 | * Answer: with this implementation a tab is inserted when only 1 space is 12 | * needed to the next tab stop. See line 51 below. This way we don't have worry 13 | * about any special cases. 14 | */ 15 | 16 | #include 17 | 18 | #define MAXLEN 1000 19 | #define N 4 /* default tabstop for every n columns */ 20 | 21 | /* functions */ 22 | int getLine(char [], int); 23 | void entab(char [], char []); 24 | 25 | /* getLine function: read a line into s, return length */ 26 | int getLine(char s[], int lim) 27 | { 28 | int c, i; 29 | 30 | for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) 31 | s[i] = c; 32 | if (c == '\n') { 33 | s[i] = c; 34 | ++i; 35 | } 36 | s[i] = '\0'; 37 | return i; 38 | } 39 | 40 | /* entab function: replaces blanks with the minimum number of tabs and blanks */ 41 | void entab(char in[], char out[]) 42 | { 43 | int i; /* index for read line */ 44 | int j; /* index for modified (written) line */ 45 | int nblanks; /* number of required blanks */ 46 | int ntabs; /* number of required tabs */ 47 | 48 | for (i = j = 0; in[i] != '\0'; ++i) { 49 | if (in[i] == ' ') { 50 | for (nblanks = ntabs = 0; in[i] == ' '; ++i) { /* count blanks */ 51 | if ((i + 1) % N == 0) { /* replace every N blanks with a tab */ 52 | ++ntabs; 53 | nblanks = 0; /* reset */ 54 | } else 55 | ++nblanks; 56 | } 57 | --i; /* adjust position after the loop */ 58 | while (ntabs-- > 0) /* insert tabs */ 59 | out[j++] = '\t'; 60 | while (nblanks-- > 0) /* insert remaining blanks */ 61 | out[j++] = ' '; 62 | } else 63 | out[j++] = in[i]; /* copy all other characters */ 64 | } 65 | out[j] = '\0'; 66 | } 67 | 68 | int main(void) 69 | { 70 | char in[MAXLEN]; /* currently read line */ 71 | char out[MAXLEN]; /* modified line */ 72 | 73 | while (getLine(in, MAXLEN) > 0) { 74 | entab(in, out); 75 | printf("%s", out); 76 | } 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /chapter01/1-22.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-22. Write a program to "fold" long input lines into two or more 3 | * shorter lines after the last non-blank character that occurs before the n-th 4 | * column of input. Make sure your program does something intelligent with very 5 | * long lines, and if there are no blanks or tabs before the specified column. 6 | * 7 | * By Faisal Saadatmand 8 | */ 9 | 10 | #include 11 | 12 | #define MAXLEN 1000 /* maximum input line length */ 13 | #define LIMIT 79 /* maximum output line length */ 14 | 15 | /* functions */ 16 | int getLine(char [], int); 17 | int skipBlanks(char [], int); 18 | void foldLine(char [], char [], int); 19 | 20 | /* getLine function: read a line into s, return length */ 21 | int getLine(char s[], int lim) 22 | { 23 | int c, i; 24 | 25 | for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) 26 | s[i] = c; 27 | 28 | if (c == '\n') { 29 | s[i] = c; 30 | ++i; 31 | } 32 | 33 | s[i] = '\0'; 34 | 35 | return i; 36 | } 37 | 38 | int skip_blanks(char str[], int pos) 39 | { 40 | while (str[pos] == ' ' || str[pos] == '\t') 41 | ++pos; 42 | return --pos; 43 | } 44 | 45 | /* foldLine function: breaks line to a specified length limit and stores 46 | * results in output */ 47 | void fold_line(char line[], char output[], int limit) 48 | { 49 | int i; /* position of currently read char */ 50 | int nChar; /* number of chars read from each fold point */ 51 | int lastBlank; /* position of the last whitespace */ 52 | int inBlank; 53 | 54 | nChar = lastBlank = inBlank = 0; 55 | for (i = 0; line[i] != '\0'; ++i) { 56 | output[i] = line[i]; 57 | ++nChar; 58 | 59 | if (line[i] == ' ' || line[i] == '\t') { 60 | if (!inBlank) 61 | lastBlank = i; /* keep track of the blank position */ 62 | inBlank = 1; 63 | } else 64 | inBlank = 0; 65 | 66 | if (nChar >= limit && lastBlank != 0) { /* slip if no lastBank */ 67 | output[lastBlank] = '\n'; /* break line (foldpoint) */ 68 | i = lastBlank; /* read next charchter from foldpoint */ 69 | i = skip_blanks(line, i); /* consume leading blanks after foldpoint */ 70 | nChar = lastBlank = inBlank = 0; /* rest */ 71 | } 72 | } 73 | output[i] = '\0'; 74 | } 75 | 76 | int main(void) 77 | { 78 | int len; /* current line length */ 79 | char line[MAXLEN]; /* current input line */ 80 | char foldedLine[MAXLEN]; /* folded input line */ 81 | 82 | while ((len = getLine(line, MAXLEN)) > 0) { 83 | if (len > LIMIT) { 84 | fold_line(line, foldedLine, LIMIT); 85 | printf("%s", foldedLine); 86 | } else 87 | printf("%s", line); 88 | } 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /chapter01/1-23.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-23. Write a program to remove all comments from a C program. 3 | * Don't forget to handle quoted strings and character constants properly. C 4 | * comments don't nest. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | /* 10 | * NOTE: In keeping with the C standard the book follows, this program does not 11 | * delete single-line comments, i.e. lines beginning with //, because their 12 | * addition into the language came after ANSI C (C89). 13 | */ 14 | 15 | #include 16 | 17 | #define IN 1 18 | #define OUT 0 19 | 20 | /* isQuotationMark: return true if c is a valid beginning (or end) of a string 21 | * literal, otherwise return 0 */ 22 | int isQuotationMark(char prev, char c) 23 | { 24 | return prev != '\\' && prev != '\'' && c == '\"'; 25 | } 26 | 27 | int main(void) 28 | { 29 | int prevC; /* previously read character from input */ 30 | int c; /* currently read character from input */ 31 | int comment, quote; /* flag variables */ 32 | 33 | comment = quote = OUT; 34 | prevC = getchar(); /* get the first character */ 35 | while ((c = getchar()) != EOF) { /* get the next character */ 36 | if (isQuotationMark(prevC, c)) { 37 | if (quote == IN) 38 | quote = OUT; /* the end of quote */ 39 | else if (comment == OUT) 40 | quote = IN; /* the beginning of a quote */ 41 | } 42 | if (quote == OUT && prevC == '/' && c == '*') 43 | comment = IN; /* the beginning of a comment */ 44 | if (comment == OUT) 45 | putchar(prevC); /* print previously read character */ 46 | if (comment == IN && prevC == '*' && c == '/') { 47 | c = getchar(); /* skip '/' character */ 48 | comment = OUT; /* the end of a comment */ 49 | } 50 | prevC = c; /* store c */ 51 | } 52 | putchar(prevC); /* print the last previously read character */ 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /chapter01/1-24.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-24. Write a program to check a C program for rudimentary syntax 3 | * errors like unmatched parentheses, brackets and braces. Don't forget about 4 | * quotes, both single and double, escape sequences, and comments. (This 5 | * program is hard if you do it in full generality.) 6 | * 7 | * By Faisal Saadatmand 8 | */ 9 | 10 | 11 | /* NOTE: this is not done in full generality */ 12 | 13 | #include 14 | 15 | #define YES 1 16 | #define NO 0 17 | 18 | /* globals */ 19 | int leftParens = 0; 20 | int rightParens = 0; 21 | int leftBrackets = 0; 22 | int rightBrackets = 0; 23 | int leftBraces = 0; 24 | int rightBraces = 0; 25 | 26 | /* functions */ 27 | void printInfo(); 28 | int skipChar(int); 29 | void checkSymbolsBallance(void); 30 | void countSymbols(void); 31 | int skipComment(int); 32 | int skipQuote(int); 33 | 34 | /* skipChar: skips n characters in the input stream */ 35 | int skipChar(int n) 36 | { 37 | int c; 38 | 39 | while (n--) 40 | c = getchar(); 41 | return c ; 42 | } 43 | 44 | /* skipComment: skip characters in the input stream until encountered the 45 | * ending symbol of a c-style comment */ 46 | int skipComment(int c) 47 | { 48 | int stop = NO; 49 | 50 | while (stop == NO && (c = getchar()) != EOF) 51 | if (c == '*' && (c = getchar()) == '/') 52 | stop = YES; 53 | return c; 54 | } 55 | 56 | /* skipComment: skip characters in the input stream until encountered the 57 | * ending character of a c-style quote (single or double) */ 58 | int skipQuote(int type) 59 | { 60 | int c, stop = NO, step = 2; 61 | 62 | while (stop == NO && (c = getchar()) != EOF) { 63 | if (c == '\\') 64 | c = skipChar(step); 65 | if (c == type) 66 | stop = YES; 67 | } 68 | return c; 69 | } 70 | 71 | /* countSymbols: count c-style demarcating symbols for comments and quote */ 72 | void countSymbols(void) { 73 | extern int leftParens, rightParens, leftBrackets, rightBrackets, 74 | leftBraces, rightBraces; 75 | int c; 76 | 77 | while ((c = getchar()) != EOF) { 78 | if (c == '/' && (c = getchar()) == '*') /* skip comments */ 79 | c = skipComment(c); 80 | if (c == '"') /* skip double quotes */ 81 | c = skipQuote(c); 82 | if (c == '\'') /* slip single quotes */ 83 | c = skipQuote(c); 84 | if (c == '(') 85 | ++leftParens; 86 | if (c == ')') 87 | ++rightParens; 88 | if (c == '[') 89 | ++leftBrackets; 90 | if (c == ']') 91 | ++rightBrackets; 92 | if (c == '{') 93 | ++leftBraces; 94 | if (c == '}') 95 | ++rightBraces; 96 | } 97 | } 98 | 99 | /* checkSymbolsBallance: check if number of c-style demarcating symbols for 100 | * comments and quotes are balanced. Print an error message if not. */ 101 | void checkSymbolsBallance(void) 102 | { 103 | extern int leftParens, rightParens, leftBrackets, rightBrackets, 104 | leftBraces, rightBraces; 105 | 106 | if (leftParens - rightParens < 0) 107 | printf("Error: missing '('\n"); 108 | else if (leftParens - rightParens > 0) 109 | printf("Error: missing ')'\n"); 110 | if (leftBrackets - rightBrackets < 0) 111 | printf("Error: missing '['\n"); 112 | else if (leftBrackets - rightBrackets > 0) 113 | printf("Error: missing ']'\n"); 114 | if (leftBraces - rightBraces < 0) 115 | printf("Error missing '{'\n"); 116 | else if (leftBraces - rightBraces > 0) 117 | printf("Error missing '}'\n"); 118 | } 119 | 120 | /* printInfo: print the number of demarcating symbols for comments and quotes */ 121 | void printInfo(void) 122 | { 123 | extern int leftParens, rightParens, leftBrackets, rightBrackets, 124 | leftBraces, rightBraces; 125 | 126 | printf("'(': %i ')': %i Total: %i\n", 127 | leftParens, rightParens, leftParens + rightParens); 128 | printf("'[': %i ']': %i Total: %i\n", 129 | leftBrackets, rightBrackets, leftBrackets + rightBrackets); 130 | printf("'{': %i '}': %i Total: %i\n", 131 | leftBraces, rightBraces, leftBraces + rightBraces); 132 | } 133 | 134 | int main(void) 135 | { 136 | countSymbols(); 137 | printInfo(); 138 | checkSymbolsBallance(); 139 | return 0; 140 | } 141 | -------------------------------------------------------------------------------- /chapter01/1-3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-3. Modify the temperature conversion program to print a heading 3 | * above the table. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | int main(void) 11 | { 12 | float fahr, celsius; 13 | float lower, upper, step; 14 | 15 | lower = 0; /* lower limit of temperature scale */ 16 | upper = 300; /* upper limit */ 17 | step = 20; /* step size */ 18 | 19 | fahr = lower; 20 | 21 | printf("Fahrenheit\tCelsius\n"); 22 | 23 | while (fahr <= upper) { 24 | celsius = (5.0/9.0) * (fahr-32.0); 25 | printf("%10.0f\t%7.1f\n", fahr, celsius); 26 | fahr = fahr + step; 27 | } 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /chapter01/1-4.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-4. Write a program to print the corresponding Celsius to 3 | * Fahrenheit table. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | int main(void) 11 | { 12 | float fahr, celsius; 13 | float lower, upper, step; 14 | 15 | lower = 0; /* lower limit of temperature scale */ 16 | upper = 300; /* upper limit */ 17 | step = 20; /* step size */ 18 | 19 | fahr = celsius = lower; 20 | 21 | printf("Celsius\t\tFahrenheit\n"); 22 | 23 | while (celsius <= upper) { 24 | fahr = celsius * 9 / 5 + 32; 25 | printf("%7.0f\t%18.0f\n", celsius, fahr); 26 | fahr = fahr + step; 27 | celsius = celsius + step; 28 | } 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /chapter01/1-5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-5. Modify the temperature conversion program to print the table 3 | * in reverse order, that is, from 300 degrees to 0. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | int main(void) 11 | { 12 | int fahr; 13 | 14 | for (fahr = 300; fahr >= 0; fahr = fahr - 20) 15 | printf("%3d %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32)); 16 | } 17 | -------------------------------------------------------------------------------- /chapter01/1-6.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-6. Verify that the expression getchar() != EOF is 0 or 1. 3 | * 4 | * By Faisal Saadatmand 5 | */ 6 | 7 | #include 8 | 9 | int main(void) 10 | { 11 | printf("%i\n", (getchar()) != EOF); 12 | 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /chapter01/1-7.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-7. Write a program to print the value of EOF. 3 | * 4 | * By Faisal Saadatmand 5 | */ 6 | 7 | #include 8 | 9 | int main(void) 10 | { 11 | printf("value of EOF is %i\n", EOF); /* print the value of EOF */ 12 | 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /chapter01/1-8.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-8. Write a program to count blanks, tabs, and newlines. 3 | * 4 | * by Faisal Saadatmand 5 | */ 6 | 7 | #include 8 | 9 | int main(void) 10 | { 11 | int c, blanks, tabs, newlines; 12 | 13 | blanks = tabs = newlines = 0; 14 | 15 | while ((c = getchar()) != EOF) { 16 | if (c == ' ') 17 | ++blanks; 18 | if (c == '\t') 19 | ++tabs; 20 | if (c == '\n') 21 | ++newlines; 22 | } 23 | 24 | printf("%d blanks, %d tabs, %d newlines\n", blanks, tabs, newlines); 25 | 26 | return 0; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /chapter01/1-9.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 1-9. Write a program to copy its input to its output, replacing 3 | * each string of one or more blanks by a single blank. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | #define YES 1 11 | #define NO 0 12 | 13 | int main(void) 14 | { 15 | int c; /* current input character */ 16 | int blank; /* flag for the status of the previous character */ 17 | 18 | blank = NO; 19 | while ((c = getchar()) != EOF) { 20 | if (c != ' ') 21 | blank = NO; 22 | if (!blank) 23 | putchar(c); 24 | if (c == ' ') 25 | blank = YES; 26 | } 27 | return 0; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /chapter02/2-10.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 2-10. Rewrite the function lower, which converts upper case letters 3 | * to lower case, with a conditional expression instead of if-else. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | /* functions */ 11 | int lower(char); 12 | 13 | /* lower: convert c to lower case; ASCII only */ 14 | int lower(char c) 15 | { 16 | return (c >= 'A' && c <= 'Z') ? c + 'a' - 'A' : c; 17 | } 18 | 19 | int main(void) 20 | { 21 | char c; 22 | 23 | while ((c = getchar()) != EOF) 24 | printf("%c", lower(c)); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /chapter02/2-2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 2-2. Write a loop equivalent to the for loop above without using && 3 | * or ||. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | /* 9 | * for (i=0; i < lim-1 && (c=getchar()) != '\n' && c != EOF; ++i) 10 | * s[i] = c; 11 | * 12 | * NOTE: expressions connected by logical operators are evaluated left to 13 | * right. 14 | */ 15 | #include 16 | 17 | #define MAXLINE 1000 /* maximum input line length */ 18 | 19 | /* functions */ 20 | int getLine(char [], int); 21 | 22 | /* getLine function: read a line into s, return length */ 23 | int getLine(char s[], int lim) 24 | { 25 | int c, i, halt; 26 | 27 | halt = 0; 28 | for (i = 0; !halt; ++i) { 29 | if (i > lim - 1) 30 | halt = 1; 31 | else if ((c = getchar()) == '\n') 32 | halt = 1; 33 | else if (c == EOF) 34 | halt = 1; 35 | else 36 | s[i] = c; 37 | } 38 | --i; 39 | 40 | if (c == '\n') 41 | s[i++] = c; 42 | 43 | s[i] = '\0'; 44 | 45 | return i; 46 | } 47 | 48 | int main(void) 49 | { 50 | char line[MAXLINE]; 51 | 52 | while (getLine(line, MAXLINE) > 0) 53 | printf("%s", line); 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /chapter02/2-3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 2-3. Write a function htoi(s), which converts a string of 3 | * hexadecimal digits (including an optional 0x or 0X) into its equivalent 4 | * integer value. The allowable digits are 0 through 9, a through f, and A 5 | * through F. 6 | * 7 | * By Faisal Saadatmand 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #define MAXLEN 1000 14 | 15 | /* functions */ 16 | int htoi(char []); 17 | 18 | int htoi(char s[]) 19 | { 20 | int i, hexDigit, intValue; 21 | 22 | /* detect optional 0x or 0X prefix */ 23 | i = 0; 24 | if (s[0] == '0' && tolower(s[1]) == 'x' && s[2] != '\0') 25 | i = 2; 26 | 27 | hexDigit = intValue = 0; 28 | for ( ; s[i] != '\0'; ++i) { 29 | if (!isdigit(s[i]) && (tolower(s[i]) < 'a' || tolower(s[i]) > 'f')) 30 | return -1; /* invalid input, exit early */ 31 | if (isdigit(s[i])) 32 | hexDigit = s[i] - '0'; /* convert digits to hexadecimal*/ 33 | else 34 | hexDigit = tolower(s[i]) - 'a' + 10; /* convert letters hexadecimal */ 35 | intValue = 16 * intValue + hexDigit; /* convert hexadecimal to decimal*/ 36 | } 37 | 38 | return intValue; 39 | } 40 | 41 | int main(void) 42 | { 43 | int result; 44 | char s[MAXLEN]; 45 | 46 | printf("Enter a hexadecimal string: "); 47 | scanf("%s", s); 48 | 49 | if ((result = htoi(s)) < 0) 50 | return -1; /* not a hexadecimal number */ 51 | 52 | printf("%i\n", result); 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /chapter02/2-4.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 2-4. Write an alternative version of squeeze(s1,s2) that deletes 3 | * each character in s1 that matches any character in the string s2. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | #define MAXCHAR 1000 11 | 12 | /* functions */ 13 | void squeeze(char [], char []); 14 | 15 | void squeeze(char s1[], char s2[]) 16 | { 17 | int i, j, k; 18 | 19 | k = 0; 20 | for (i = 0; s1[i] != '\0'; ++i) { 21 | for (j = 0; s2[j] != '\0' && s1[i] != s2[j]; ++j) 22 | ; 23 | if (s2[j] == '\0') /* match not found */ 24 | s1[k++] = s1[i]; 25 | } 26 | s1[k] = '\0'; 27 | } 28 | 29 | int main(void) 30 | { 31 | char string1[MAXCHAR] = { "clean* *this- *str*-ing *** ---" }; 32 | char string2[MAXCHAR] = { "*-" }; 33 | 34 | squeeze(string1, string2); 35 | printf("%s\n", string1); 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /chapter02/2-5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 2-5. Write the function any(s1,s2), which returns the first 3 | * location in a string s1 where any character from the string s2 occurs, or -1 4 | * if s1 contains no characters from s2. (The standard library function strpbrk 5 | * does the same job but returns a pointer to the location.) 6 | * 7 | * By Faisal Saadatmand 8 | */ 9 | 10 | #include 11 | 12 | #define MAXCHAR 1000 13 | 14 | /* functions */ 15 | int any(char [], char []); 16 | 17 | int any(char s1[], char s2[]) 18 | { 19 | int i, j; 20 | 21 | for (i = 0; s1[i] != '\0'; ++i) 22 | for (j = 0; s2[j] != '\0'; ++j) 23 | if (s1[i] == s2[j]) 24 | return i; 25 | return -1; 26 | } 27 | 28 | int main(void) 29 | { 30 | char string1[MAXCHAR] = { "search me please" }; 31 | char string2[MAXCHAR] = { "wmp" }; 32 | 33 | printf("%i\n", any(string1, string2)); 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /chapter02/2-6.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 2-6. Write a function setbits(x,p,n,y) that returns x with the n 3 | * bits that begin at position p set to the rightmost n bits of y, leaving the 4 | * other bits unchanged. 5 | * 6 | * by Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | 11 | /* functions */ 12 | unsigned int setbits(unsigned, int, int, unsigned); 13 | 14 | unsigned int setbits(unsigned x, int p, int n, unsigned y) 15 | { 16 | unsigned nbits; 17 | 18 | nbits = ~(~0 << n); /* mask to extract rightmost n bits */ 19 | 20 | return (x & ~(nbits << p)) | ((y & nbits) << p); 21 | } 22 | 23 | int main(void) 24 | { 25 | unsigned x = 0XF8FF; 26 | unsigned y = 0XF0A2; 27 | int p = 8; /* starting position of bits */ 28 | int n = 8; /* number of bits to set */ 29 | 30 | printf("%x\n", setbits(x, p, n, y)); 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /chapter02/2-7.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 2-7. Write a function invert(x,p,n) that returns x with the n bits 3 | * that begin at position p inverted (i.e., 1 changed into 0 and vice versa), 4 | * leaving the others unchanged. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | 11 | /* functions */ 12 | unsigned int invert(unsigned int, int, int); 13 | 14 | unsigned int invert(unsigned int x, int p, int n) 15 | { 16 | unsigned nbits; 17 | 18 | nbits = ~(~0 << n); 19 | 20 | return (x & ~(nbits << p)) | (nbits & ~(x & nbits << p)); 21 | } 22 | 23 | int main(void) 24 | { 25 | unsigned int x = 0XFFFE; 26 | int p = 0; /* starting position of bits */ 27 | int n = 4; /* number of bits to set */ 28 | 29 | printf("%x\n", invert(x, p, n)); 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /chapter02/2-8.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 2-8. Write a function rightrot(x,n) that returns the value of the 3 | * integer x rotated to right by n positions. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | /* functions */ 11 | unsigned rightrot(unsigned, int); 12 | size_t int_size(); 13 | 14 | /* int_size: determine the size of an integer storged on this machine */ 15 | size_t int_size() 16 | { 17 | int x = ~0; 18 | size_t i = 0; 19 | 20 | while (x != 0) { 21 | x <<= 1; 22 | ++i; 23 | } 24 | 25 | return i; 26 | } 27 | 28 | unsigned rightrot(unsigned x, int n) 29 | { 30 | unsigned size, bits; 31 | 32 | size = int_size(); /* never assume size of an int */ 33 | n = n % size; /* scale down the shift count to a defined range */ 34 | bits = x & ~(~0 << n); /* extract the n bits to be rotated */ 35 | 36 | return (x >> n) | bits << (size - n); 37 | } 38 | 39 | int main(void) 40 | { 41 | unsigned x = 0XAF14E5CB; 42 | int n = 8; 43 | 44 | printf("%x\n", rightrot(x, n)); 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /chapter02/2-9.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 2-9. In a two's complement number system, x &= (x-1) deletes the 3 | * rightmost 1-bit in x. Explain why. Use this observation to write a faster 4 | * version of bitcount. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | /* 10 | * ANSWER: 11 | * 12 | * It has to do with the way binary subtraction works in two's complement 13 | * number system, which is similar to base 10 subtraction. 14 | * 15 | * The bits are subtracted from each other starting from the least to the most 16 | * significant bit: 17 | * 18 | * 0xFF - 0x01 = 0xFE 19 | * 20 | * Or 21 | * 22 | * 1111 1111 - 0000 0001 = 1111 1110 23 | * 24 | * However, we cannot subtract a higher binary number from a lesser one, i.e. 25 | * subtracting 1 from 0. In this case, we "borrow" 1 from the next available 26 | * 1-bit to left (from the rightmost 1-bit). We then add 2 or 10b (base^n) to 27 | * the binary number we were trying to subtract from. This has the effect of 28 | * turning off the next available 1-bit and turning on any 0-bits after it. In 29 | * other words, all the bits starting with the rightmost 1-bit will be 30 | * inverted: 31 | * 32 | * 0xF8 - 0x01 = 0xF7 33 | * 34 | * or 35 | * 36 | * 1111 1000 - 0000 0001 = 11111 0111 37 | * 38 | * Notice the 4 least significant bits are inverted. 39 | * 40 | * The result of the subtraction, then, produces a mask that when ANDed with 41 | * the original value x will turn off the rightmost 1-bit, leaving the other 42 | * bits unchanged. 43 | * 44 | * Given this observation, we can use x &= (x - 1) instead of x >>= 1 as the 45 | * increment expression in the for loop. This would allow us to omit the test 46 | * for the least significant bit inside the loop. 47 | */ 48 | 49 | #include 50 | 51 | /* functions */ 52 | int bitcount(unsigned); 53 | 54 | int bitcount(unsigned x) 55 | { 56 | int b; 57 | 58 | for (b = 0; x != 0; x &= (x - 1)) 59 | ++b; 60 | return b; 61 | } 62 | 63 | int main(void) 64 | { 65 | unsigned y = 0x2; 66 | 67 | printf("%i 1-bit(s)\n", bitcount(y)); 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /chapter03/3-1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 3-1. Our binary search makes two tests inside the loop, when one 3 | * would suffice (at the price of more tests outside.) Write a version with 4 | * only one test inside the loop and measure the difference in run-time. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | /* 10 | * Answer: this version is faster because it has one less comparison in the 11 | * loop; However, it doesn't stop the loop as soon as the match is found. 12 | * the loop. 13 | */ 14 | 15 | #include 16 | 17 | #define SIZE 12 18 | 19 | /* functions */ 20 | int binSearch(int, int [], int); 21 | 22 | /* binSearch: find x in value[0] <= value[1] <= ... <= value[n - 1] */ 23 | int binSearch(int x, int v[], int n) 24 | { 25 | int low, high, mid; 26 | 27 | low = 0; 28 | high = n - 1; 29 | while (low < high) { 30 | mid = (low + high) / 2; 31 | if (v[mid] < x) 32 | low = mid + 1; 33 | else 34 | high = mid; 35 | } 36 | if (v[low] == x) 37 | return low; /* found match */ 38 | return -1; /* no match */ 39 | } 40 | 41 | int main(void) 42 | { 43 | int array[SIZE] = { 2, 4, 5, 6, 8, 9, 12, 16, 20, 32, 40, 78 }; 44 | int input, number; 45 | 46 | while (1) { 47 | printf("Enter number to search in the array: "); 48 | input = scanf("%i", &number); 49 | if (!input || input == EOF) 50 | return -1; 51 | printf("%i\n", binSearch(number, array, SIZE)); 52 | } 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /chapter03/3-2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 3-2. Write a function escape(s,t) that converts characters like 3 | * newline and tab into visible escape sequences like \n and \t as it copies 4 | * the string t to s. Use a switch. Write a function for the other direction as 5 | * well, converting escape sequences into the real characters. 6 | * 7 | * By Faisal Saadatmand 8 | */ 9 | 10 | #include 11 | 12 | #define MAXLEN 1000 /* maximum input line length */ 13 | 14 | /* functions */ 15 | int getLine(char [], int); 16 | void escape(char [], char []); 17 | void escapeToChar(char [], char []); 18 | 19 | /* getLine function: read a line into s, return length */ 20 | int getLine(char s[], int lim) 21 | { 22 | int c, i; 23 | 24 | for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) 25 | s[i] = c; 26 | if (c == '\n') 27 | s[i++] = c; 28 | s[i] = '\0'; 29 | return i; 30 | } 31 | 32 | /* escape: copy string t to s and convert characters like newline and tab into 33 | * visible escape sequences */ 34 | void escape(char s[], char t[]) 35 | { 36 | int i, j; 37 | 38 | for (i = j = 0; t[i] != '\0'; ++i, ++j) 39 | switch (t[i]) { 40 | case '\t': 41 | s[j] = '\\'; 42 | s[++j] = 't'; 43 | break; 44 | case '\n': 45 | s[j] = '\\'; 46 | s[++j] = 'n'; 47 | break; 48 | default: 49 | s[j] = t[i]; 50 | break; 51 | } 52 | s[j] = '\0'; 53 | } 54 | 55 | /* escapeRev: copy string t to s and convert escape sequences like \n and \t 56 | * into newline and tab characters. */ 57 | void escapeToChar(char s[], char t[]) 58 | { 59 | int i, j; 60 | 61 | for (i = j = 0; t[i] != '\0'; ++i, ++j) 62 | switch (t[i]) { 63 | case '\\': 64 | switch (t[++i]) { 65 | case 't': 66 | s[j] = '\t'; 67 | break; 68 | case 'n': 69 | s[j] = '\n'; 70 | break; 71 | default: 72 | s[j] = t[i]; 73 | break; 74 | } 75 | break; 76 | default: 77 | s[j] = t[i]; 78 | break; 79 | } 80 | s[j] = '\0'; 81 | } 82 | 83 | int main(void) 84 | { 85 | char line[MAXLEN], modLine[MAXLEN]; 86 | 87 | while (getLine(line, MAXLEN) > 0) { 88 | escape(modLine, line); 89 | printf("%s", modLine); 90 | // escapeToChar(modLine, line); 91 | // printf("%s", modLine); 92 | } 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /chapter03/3-3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 3-3. Write a function expand(s1, s2) that expands shorthand 3 | * notations like a-z in the string s1 into the equivalent complete list 4 | * abc...xyz in s2. Allow for letters of either case and digits, and be 5 | * prepared to handle cases like a-b-c and a-z0-9 and -a-z. Arrange that a 6 | * leading or trailing - is taking literally. 7 | * 8 | * By Faisal Saadatmand 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #define MAXLEN 1000 15 | 16 | /* funstions */ 17 | int getLine(char [], int); 18 | int isValidRange(char, char); 19 | void expand(char [], char []); 20 | 21 | /* getLine function: read a line into s, return length */ 22 | int getLine(char s[], int lim) 23 | { 24 | int c, i; 25 | 26 | for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) 27 | s[i] = c; 28 | 29 | if (c == '\n') { 30 | s[i] = c; 31 | ++i; 32 | } 33 | 34 | s[i] = '\0'; 35 | 36 | return i; 37 | } 38 | 39 | /* isValidRange: check if begin and end make a valid range: begin is less than 40 | * end, and the range is between letters of either case and between digits */ 41 | int isValidRange(char begin, char end) 42 | { 43 | return begin < end && ((isdigit(begin) && isdigit(end)) 44 | || (isalpha(begin) && isalpha(end) 45 | && ((islower(begin) && islower(end)) 46 | || (isupper(begin) && isupper(end))))); 47 | } 48 | 49 | void expand(char s1[], char s2[]) 50 | { 51 | int i, j, k, dist; 52 | 53 | for (i = j = 0; s1[i] != '\0' ; ++i) 54 | if (i != 0 && s1[i] == '-' && isValidRange(s1[i - 1], s1[i + 1])) { 55 | dist = s1[i + 1] - s1[i - 1]; 56 | for (k = 1; k < dist; ++k, ++j) 57 | s2[j] = s1[i - 1] + k; /* expand the shorthand */ 58 | } else 59 | s2[j++] = s1[i]; /* copy the character */ 60 | s2[j] = '\0'; 61 | } 62 | 63 | int main(void) 64 | { 65 | char line[MAXLEN]; 66 | char modLine[MAXLEN]; 67 | 68 | while (getLine(line, MAXLEN) > 0) { 69 | expand(line, modLine); 70 | printf("%s", modLine); 71 | } 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /chapter03/3-4.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 3-4. In a two's complement number representation, our version of 3 | * itoa does not handle the largest negative number, that is, the value of n 4 | * equal to -(2^wordsize-1). Explain why not. Modify it to print that value 5 | * correctly, regardless of the machine on which it runs. 6 | * 7 | * By Faisal Saadatmand 8 | */ 9 | 10 | /* 11 | * ANSWER: 12 | * In two's complement number representation, the range of values an int can 13 | * hold is from -(2^wordsize-1) to (2^wordsize-1)-1. The most significant bit 14 | * is the sign bit; however, it also holds a value for negative numbers; thus, 15 | * making the negative limit larger than the positive limit by a value of 1. 16 | * When we negate the largest negative number, -(2^wordsize-1), we get a number 17 | * that is equal to 2^wordsize-1, which is larger than the largest positive 18 | * number, (2^wordsize-1)-1. This will overflow a signed int and cause 19 | * unexpected results. To overcome this, we can use an unsigned int for n. The 20 | * check for whether n is negative or not is taking care of with the assigned 21 | * of n to the int variable sign, in which n is convert to a signed int. 22 | * 23 | * Alternatively, we could avoid negating n altogether and use the abs function 24 | * from stdlib.h to extract the digits. See 3-4a.c. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #define MAXLEN 1000 32 | 33 | void itoa(unsigned, char []); 34 | void reverse(char []); 35 | 36 | /* itoa: convert n to characters in s */ 37 | void itoa(unsigned n, char s[]) 38 | { 39 | int i, sign; 40 | 41 | if ((sign = n) < 0) /* record sign */ 42 | n = -n; /* make n positive */ 43 | 44 | i = 0; 45 | do { /* generate digits in revered order */ 46 | s[i++] = n % 10 + '0'; /* get next digit */ 47 | } while ((n /= 10) != 0); /* delete it */ 48 | if (sign < 0) 49 | s[i++] = '-'; 50 | s[i] = '\0'; 51 | reverse(s); 52 | } 53 | 54 | /* reverse: reverse the order of the characters in the string s */ 55 | void reverse(char s[]) 56 | { 57 | int c, i, j; 58 | 59 | for (i = 0, j = strlen(s) - 1; i < j; ++i, --j) { 60 | c = s[i]; 61 | s[i] = s[j]; 62 | s[j] = c; 63 | } 64 | } 65 | 66 | int main(void) 67 | { 68 | char str[MAXLEN]; 69 | 70 | itoa(0, str); /* do-while loop is necessary for 0 */ 71 | printf("%i -> %s\n", 0, str); 72 | itoa(INT_MAX, str); 73 | printf("%i -> %s\n", INT_MAX, str); 74 | itoa(INT_MIN, str); 75 | printf("%i -> %s\n", INT_MIN, str); 76 | 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /chapter03/3-4a.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 3-4. In a two's complement number representation, our version of 3 | * itoa does not handle the largest negative number, that is, the value of n 4 | * equal to -(2^wordsize-1). Explain why not. Modify it to print that value 5 | * correctly, regardless of the machine on which it runs. 6 | * 7 | * By Faisal Saadatmand 8 | */ 9 | 10 | /* 11 | * ANSWER: 12 | * In two's complement number representation, the range of values an int can 13 | * hold is from -(2^wordsize-1) to (2^wordsize-1)-1. The most significant bit 14 | * is the sign bit; however, it also holds a value for negative numbers; thus, 15 | * making the negative limit larger than the positive limit by a value of 1. 16 | * When we negate the largest negative number, -(2^wordsize-1), we get a number 17 | * that is equal to 2^wordsize-1, which is larger than the largest positive 18 | * number, (2^wordsize-1)-1. This will overflow a signed int and cause 19 | * unexpected results. To overcome this, we can use an unsigned int for n. The 20 | * check for whether n is negative or not is taking care of with the assigned 21 | * of n to the int variable sign, in which n is convert to a signed int. 22 | * 23 | * Alternatively, we could avoid negating n altogether and use the abs function 24 | * from stdlib.h to extract the digits. See 3-4a.c. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define MAXLEN 1000 33 | 34 | void itoa(int, char []); 35 | void reverse(char []); 36 | 37 | /* itoa: convert n to characters in s--abs version */ 38 | void itoa(int n, char s[]) 39 | { 40 | int i, sign; 41 | 42 | sign = n; 43 | i = 0; 44 | do { /* generate digits in revered order */ 45 | s[i++] = abs(n % 10) + '0'; /* get next digit */ 46 | } while ((n /= 10) != 0); 47 | if (sign < 0) 48 | s[i++] = '-'; 49 | s[i] = '\0'; 50 | reverse(s); 51 | } 52 | 53 | /* reverse: reverse the order of the characters in the string s */ 54 | void reverse(char s[]) 55 | { 56 | int c, i, j; 57 | 58 | for (i = 0, j = strlen(s) - 1; i < j; ++i, --j) { 59 | c = s[i]; 60 | s[i] = s[j]; 61 | s[j] = c; 62 | } 63 | } 64 | 65 | int main(void) 66 | { 67 | char str[MAXLEN]; 68 | 69 | itoa(0, str); /* do-while loop is necessary for 0 */ 70 | printf("%i -> %s\n", 0, str); 71 | itoa(INT_MAX, str); 72 | printf("%i -> %s\n", INT_MAX, str); 73 | itoa(INT_MIN, str); 74 | printf("%i -> %s\n", INT_MIN, str); 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /chapter03/3-5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 3-5. Write the function itob(n,s,b) that converts the integer n 3 | * into a base b character representation in the string s. In particular, 4 | * itob(n,s,16) formats s as a hexadecimal integer in s. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #define MAXLEN 1000 14 | 15 | /* functions */ 16 | void reverse(char []); 17 | void itob(unsigned, char [], int); 18 | 19 | /* reverse: reverse string s in place */ 20 | void reverse(char s[]) 21 | { 22 | int c, i, j; 23 | 24 | for (i = 0, j = strlen(s) - 1; i < j; i++, j--) { 25 | c = s[i]; 26 | s[i] = s[j]; 27 | s[j] = c; 28 | } 29 | } 30 | 31 | /* itob: converts the integer n into a base b character representation in the 32 | * string s. */ 33 | void itob(unsigned n, char s[], int b) 34 | { 35 | int i, sign, digit; 36 | const char baseDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', 37 | '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 38 | 39 | if (b == 10 && (sign = n) < 0) 40 | n = -n; /* or you can use ~n + 1 */ 41 | 42 | i = 0; 43 | do { /* generate digits in reverse order */ 44 | digit = n % b; /* get next digit */ 45 | s[i++] = baseDigits[digit]; 46 | } while ((n /= b) > 0); 47 | 48 | if (b == 10 && sign < 0) /* add sign symbol for base 10 */ 49 | s[i++] = '-'; 50 | 51 | s[i] = '\0'; 52 | 53 | reverse(s); 54 | } 55 | 56 | int main(void) 57 | { 58 | int i; 59 | char str[MAXLEN]; 60 | 61 | printf("converting %i and %i to\n", INT_MIN, INT_MAX); 62 | for (i = 2; i <= 16; ++i) { 63 | printf("Base% i:\n", i); 64 | itob(INT_MIN, str, i); 65 | printf(" %s\n", str); 66 | itob(INT_MAX, str, i); 67 | printf(" %s\n", str); 68 | } 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /chapter03/3-6.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 3-6. Write a version of itoa that accepts three arguments instead 3 | * of two. The third argument is a minimum field width; the converted number 4 | * must be padded with blanks on the left if necessary to make it wide enough. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #define MAXLEN 1000 14 | 15 | /* functions */ 16 | void reverse(char []); 17 | void itoa(unsigned, char [], int); 18 | 19 | /* reverse function: reverse string s in place */ 20 | void reverse(char s[]) 21 | { 22 | int c, i, j; 23 | 24 | for (i = 0, j = strlen(s) - 1; i < j; i++, j --) { 25 | c = s[i]; 26 | s[i] = s[j]; 27 | s[j] = c; 28 | } 29 | } 30 | 31 | /* itoa: convert n to characters in s */ 32 | void itoa(unsigned n, char s[], int w) 33 | { 34 | int i, sign; 35 | 36 | if ((sign = n) < 0) /* record sign */ 37 | n = -n; /* make n positive */ 38 | 39 | i = 0; 40 | do { /* generate digits in revered order */ 41 | s[i++] = n % 10 + '0'; /* get next digit */ 42 | } while ((n /= 10) > 0); /* delete it */ 43 | 44 | if (sign < 0) 45 | s[i++] = '-'; 46 | 47 | /* left padding */ 48 | while (i < w) 49 | s[i++] = ' '; 50 | 51 | s[i] = '\0'; 52 | 53 | reverse(s); 54 | } 55 | 56 | int main(void) 57 | { 58 | int intValue, width; 59 | char str[MAXLEN]; 60 | 61 | printf("Enter integer to convert to a string: "); 62 | scanf("%i", &intValue); 63 | 64 | printf("Enter minimum field width: "); 65 | scanf("%i", &width); 66 | 67 | itoa(intValue, str, width); 68 | 69 | printf("%s\n", str); 70 | 71 | return 0; 72 | } 73 | -------------------------------------------------------------------------------- /chapter04/4-1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 4-1. Write the function strindex(s,t) which returns the position of 3 | * the rightmost occurrence of t in s, or -1 if there is none. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #define MAXLEN 1000 /* maximum input line length */ 12 | 13 | /* functions */ 14 | int getLine(char [], int); 15 | int strindex(char [], const char []); 16 | 17 | /* getLine: get line into s, return */ 18 | int getLine(char s[], int lim) 19 | { 20 | int c, i; 21 | 22 | i = 0; 23 | while (--lim > 0 && (c = getchar()) != EOF && c != '\n') 24 | s[i++] = c; 25 | 26 | if ( c == '\n') 27 | s[i++] = c; 28 | 29 | s[i] = '\0'; 30 | 31 | return i; 32 | } 33 | 34 | /* strindex: return index of t in s, -1 if none */ 35 | int strindex(char s[], const char t[]) 36 | { 37 | int i, j, k; 38 | 39 | for (i = strlen(s) - strlen(t); i >= 0; --i) { 40 | for (j = i, k = 0; t[k] != '\0' && s[j] == t[k]; ++j, ++k) 41 | ; 42 | if (k > 0 && t[k] == '\0') 43 | return i; 44 | } 45 | return -1; 46 | } 47 | 48 | int main(void) 49 | { 50 | char line[MAXLEN]; 51 | const char pattern[] = "ould"; 52 | int pos; 53 | 54 | while (getLine(line, MAXLEN) > 0) 55 | if ((pos = strindex(line, pattern)) < 0) 56 | printf("Not found\n"); 57 | else 58 | printf("%i\n", pos); 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /chapter04/4-12.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 4-12: Adapt the idea of printd to write a recursive version of 3 | * itoa; that is, convert an integer into a string by calling a recursive 4 | * routine. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | /* 10 | * NOTE: We could have made i a static variable; however, that is problematic 11 | * because its value will persists throughout the entire life of the program, and 12 | * thus, multiple calls to itoar will use the value of i from the previous call, 13 | * which will result in an undesirable outcome. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #define MAXLEN 1000 21 | 22 | /* functions */ 23 | void itoa(int, char []); 24 | int itoar(int, char [], int); 25 | 26 | /* itoa: interface function to itoar */ 27 | void itoa(int n, char s[]) 28 | { 29 | itoar(n, s, 0); 30 | } 31 | 32 | /* itoar: convert n to characters in s, recursive version */ 33 | int itoar(int n, char s[], int i) 34 | { 35 | if (n < 0) { 36 | s[i++] = '-'; 37 | n = -n; 38 | } 39 | if (n / 10) 40 | i = itoar(n / 10, s, i); /* recursive call */ 41 | s[i++] = n % 10 + '0'; 42 | s[i] = '\0'; 43 | return i; /* return the updated value of i */ 44 | } 45 | 46 | int main(void) 47 | { 48 | char str[MAXLEN]; 49 | 50 | itoa(996, str); 51 | printf("%s\n", str); 52 | 53 | itoa(-2345, str); 54 | printf("%s\n", str); 55 | 56 | itoa(INT_MAX, str); 57 | printf("%s\n", str); 58 | 59 | itoa(INT_MIN, str); 60 | printf("%s\n", str); 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /chapter04/4-13.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 4-13. Write a recursive version of the function reverse(s), which 3 | * reverses the string s in place. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #define MAXLEN 1000 12 | 13 | /* functions */ 14 | void reverse(char s[]); 15 | void reverse_inplace(char str[], int begin, int end); 16 | void swap(char arr[], size_t i, size_t j); 17 | 18 | /* reverse: interface function to reverse_inplace */ 19 | void reverse(char s[]) 20 | { 21 | reverse_inplace(s, 0, strlen(s) - 1); 22 | } 23 | 24 | /* reverse_inplace: reverse string s in place, recursive version */ 25 | void reverse_inplace(char str[], int begin, int end) 26 | { 27 | if (begin > end) /* exit condition */ 28 | return; 29 | swap(str, begin, end); 30 | reverse_inplace(str, ++begin, --end); 31 | } 32 | 33 | /* swap: interchange v[i] and v[j] */ 34 | void swap(char v[], size_t i, size_t j) 35 | { 36 | char tmp; 37 | 38 | tmp = v[i]; 39 | v[i] = v[j]; 40 | v[j] = tmp; 41 | } 42 | 43 | int main(void) 44 | { 45 | char str[MAXLEN]; 46 | 47 | printf("Enter a string to reverse: "); 48 | scanf("%s", str); 49 | reverse(str); 50 | printf("%s\n", str); 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /chapter04/4-14.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 4-14. Define a macro swap(t, x, y) that interchanges two arguments 3 | * of type t. (Block structure will help). 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | #define SWAP(t, x, y) if (sizeof((x)) == sizeof(t) && sizeof((y)) == sizeof(t)) \ 11 | {t temp = ((x)); ((x)) = ((y)), ((y)) = temp;} 12 | 13 | /* 14 | * Alternatively we could use bitwise XOR for the swap, but it is considered 15 | * bad practice. 16 | * 17 | * #define SWAP(t, x, y) if (sizeof((x)) == sizeof(t) && sizeof((y)) == sizeof(t)) \ 18 | * {(x) ^= (y), (y) ^= (x), (x) ^= (y);} 19 | */ 20 | 21 | int main(void) 22 | { 23 | int a = 6, b = 3; 24 | char c = 99; /* 'c' */ 25 | float f = 42.0f ; 26 | long int l = 25; 27 | 28 | printf("int and int: types match\n"); 29 | printf("Before SWAP\n a = %i b = %i\n", a, b); 30 | SWAP(int, a, b); 31 | printf("After Swap\n a = %i b = %i\n", a, b); 32 | 33 | printf("\nchar and float: types don't match\n"); 34 | printf("Before SWAP\n a = %c b = %2f\n", c, f); 35 | SWAP(int, a, b); 36 | printf("After Swap\n a = %c b = %2f\n", c, f); 37 | 38 | printf("\nlong and int: types don't match,\ 39 | but implicit conversion takes place\n"); 40 | printf("Before SWAP\n a = %i b = %li\n", a, l); 41 | SWAP(int, a, b); 42 | printf("After Swap\n a = %i b = %li\n", a, l); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /chapter04/4-2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 4-2. Extend atof to handle scientific notation of the form 3 | * 4 | * 123.45e-6 5 | * 6 | * where a floating-point number may be followed by e or E and an optionally 7 | * signed exponent. 8 | * 9 | * By Faisal Saadatmand 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | /* functions */ 16 | double atof(char []); 17 | 18 | /* atof: convert string s to double */ 19 | double atof(char s[]) 20 | { 21 | int i, sign, expSign; 22 | double val, power, exponent; 23 | 24 | for (i = 0; isspace(s[i]); ++i) /* skip whitespace */ 25 | ; 26 | 27 | sign = (s[i] == '-') ? -1 : 1; 28 | if (s[i] == '+' || s[i] == '-') 29 | ++i; 30 | 31 | for (val = 0.0; isdigit(s[i]); ++i) 32 | val = 10.0 * val + (s[i] - '0'); 33 | 34 | if (s[i] == '.') 35 | ++i; 36 | 37 | for (power = 1.0; isdigit(s[i]); ++i) { 38 | val = 10.0 * val + (s[i] - '0'); 39 | power *= 10.0; 40 | } 41 | 42 | if (tolower(s[i]) == 'e') /* handle scientific notation */ 43 | ++i; 44 | 45 | expSign = (s[i] == '-') ? -1 : 1; /* record exponent's sign */ 46 | if (s[i] == '+' || s[i] == '-') 47 | ++i; 48 | 49 | for (exponent = 0.0; isdigit(s[i]); i++) /* extract the exponent */ 50 | exponent = 10.0 * exponent + (s[i] - '0'); 51 | 52 | while (exponent-- != 0) /* adjust power according to exponent */ 53 | power = (expSign > 0) ? power / 10: power * 10; 54 | 55 | return sign * val / power; 56 | } 57 | 58 | int main(void) 59 | { 60 | char number[] = "123.45"; 61 | char number2[] = "123.45e-6"; 62 | char number3[] = "123.45e+6"; 63 | char number4[] = "-123.45E+6"; 64 | 65 | printf("%f\n", atof(number)); 66 | printf("%f\n", atof(number2)); 67 | printf("%f\n", atof(number3)); 68 | printf("%f\n", atof(number4)); 69 | 70 | return 0; 71 | } 72 | -------------------------------------------------------------------------------- /chapter04/4-3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 4-3. Given the basic framework, it's straightforward to extend the 3 | * calculator. Add the modulus (%) operator and provisions for negative numbers 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | #include /* for atof() */ 10 | #include 11 | 12 | #define MAXOP 100 /* max size of operand or operator */ 13 | #define NUMBER '0' /* signal that a number was found */ 14 | #define MAXVAL 100 /* maximum depth of val stack */ 15 | #define BUFSIZE 100 16 | 17 | /* functions */ 18 | int getop(char []); 19 | void push(double); 20 | double pop(void); 21 | int getch(void); 22 | void ungetch(int); 23 | 24 | /* globals */ 25 | int sp; /* next free stack position */ 26 | double val[MAXVAL]; /* value stack */ 27 | char buf[BUFSIZE]; /* buffer from ungetch */ 28 | int bufp; /* next free position in buf */ 29 | 30 | /* push: push f onto value stack */ 31 | void push(double f) 32 | { 33 | if (sp < MAXVAL) 34 | val[sp++] = f; 35 | else 36 | printf("error: stack full, can't push %g\n", f); 37 | } 38 | 39 | /* pop: pop and return top value from stack */ 40 | double pop(void) 41 | { 42 | if (sp > 0) 43 | return val[--sp]; 44 | else { 45 | printf("error: stack empty\n"); 46 | return 0.0; 47 | } 48 | } 49 | 50 | /* getop: get next operator or numeric operand */ 51 | int getop(char s[]) 52 | { 53 | int i, c; 54 | 55 | while ((s[0] = c = getch()) == ' ' || c == '\t') 56 | ; 57 | s[1] = '\0'; 58 | 59 | i = 0; 60 | if (c == '-') /* check sign */ 61 | if (!isdigit(s[++i] = c = getch())) { 62 | ungetch(c); 63 | c = s[0]; /* not a sign */ 64 | } 65 | 66 | if (!isdigit(c) && c != '.') 67 | return c; /* not a number */ 68 | 69 | if (isdigit(c)) 70 | while (isdigit(s[++i] = c = getch())) 71 | ; 72 | if( c == '.') /* collect fraction part */ 73 | while (isdigit(s[++i] = c = getch())) 74 | ; 75 | s[i] = '\0'; 76 | if (c != EOF) 77 | ungetch(c); 78 | return NUMBER; 79 | } 80 | 81 | /* getch: get a (possibly pushed back) character */ 82 | int getch(void) 83 | { 84 | return (bufp > 0) ? buf[--bufp] : getchar(); 85 | } 86 | 87 | /* ungetch: push character back on input */ 88 | void ungetch(int c) 89 | { 90 | if (bufp >= BUFSIZE) 91 | printf("ungetch: too many characters\n"); 92 | else 93 | buf[bufp++] = c; 94 | } 95 | 96 | /* reverse Polish Calculator */ 97 | int main(void) 98 | { 99 | int type; 100 | double op2; 101 | char s[MAXOP]; 102 | 103 | while ((type = getop(s)) != EOF) { 104 | switch (type) { 105 | case NUMBER: 106 | push(atof(s)); 107 | break; 108 | case '+': 109 | push(pop() + pop()); 110 | break; 111 | case '*': 112 | push(pop() * pop()); 113 | break; 114 | case '-': 115 | op2 = pop(); 116 | push(pop() - op2); 117 | break; 118 | case '/': 119 | op2 = pop(); 120 | if (op2 != 0.0) 121 | push(pop() / op2); 122 | else 123 | printf("error: zero divisor\n"); 124 | break; 125 | case '%': 126 | op2 = pop(); 127 | if (op2 != 0.0) 128 | push((long) pop() % (long) op2); 129 | else 130 | printf("error: zero divisor\n"); 131 | break; 132 | case '\n': 133 | printf("\t%.8g\n", pop()); 134 | break; 135 | default: 136 | printf("error: unknown command %s\n", s); 137 | break; 138 | } 139 | } 140 | return 0; 141 | } 142 | -------------------------------------------------------------------------------- /chapter04/4-4.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 4-4. Add commands to print the top element of the stack without 3 | * popping, to duplicate it, and to swap the top two elements. Add command to 4 | * clear the stack. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | #include /* for atof() */ 11 | #include 12 | 13 | #define MAXOP 100 /* max size of operand or operator */ 14 | #define NUMBER '0' /* signal that a number was found */ 15 | #define MAXVAL 100 /* max depth of val stack */ 16 | #define BUFSIZE 100 17 | 18 | /* functions */ 19 | int getop(char []); 20 | void push(double); 21 | double pop(void); 22 | int getch(void); 23 | void ungetch(int); 24 | void printTOP(void); 25 | void duplicateTop(void); 26 | void swapTopTwo(void); 27 | void clearStack(void); 28 | 29 | /* globals */ 30 | int sp; /* next free stack position */ 31 | double val[MAXVAL]; /* value stack */ 32 | char buf[BUFSIZE]; /* buffer from ungetch */ 33 | int bufp; /* next free position in buf */ 34 | 35 | /* push: push f onto value stack */ 36 | void push(double f) 37 | { 38 | if (sp < MAXVAL) 39 | val[sp++] = f; 40 | else 41 | printf("error: stack full, can't push %g\n", f); 42 | } 43 | 44 | /* pop: pop and return top value from stack */ 45 | double pop(void) 46 | { 47 | if (sp > 0) 48 | return val[--sp]; 49 | else { 50 | printf("error: stack empty\n"); 51 | return 0.0; 52 | } 53 | } 54 | 55 | /* getop: get next operator or numeric operand */ 56 | int getop(char s[]) 57 | { 58 | int i, c; 59 | 60 | while ((s[0] = c = getch()) == ' ' || c == '\t') 61 | ; 62 | s[1] = '\0'; 63 | 64 | i = 0; 65 | if (c == '-') /* check sign */ 66 | if (!isdigit(s[++i] = c = getch())) { 67 | ungetch(c); 68 | c = s[0]; /* not a sign */ 69 | } 70 | 71 | if (!isdigit(c) && c != '.') 72 | return c; /* not a number */ 73 | 74 | if (isdigit(c)) 75 | while (isdigit(s[++i] = c = getch())) 76 | ; 77 | if( c == '.') /* collect fraction part */ 78 | while (isdigit(s[++i] = c = getch())) 79 | ; 80 | s[i] = '\0'; 81 | 82 | if (c != EOF) 83 | ungetch(c); 84 | return NUMBER; 85 | } 86 | 87 | /* getch: get a (possibly pushed back) character */ 88 | int getch(void) 89 | { 90 | return (bufp > 0) ? buf[--bufp] : getchar(); 91 | } 92 | 93 | /* ungetch: push character back on input */ 94 | void ungetch(int c) 95 | { 96 | if (bufp >= BUFSIZE) 97 | printf("ungetch: too many characters\n"); 98 | else 99 | buf[bufp++] = c; 100 | } 101 | 102 | /* printTOP: print top of the stack without pop */ 103 | void printTOP(void) 104 | { 105 | double top; 106 | 107 | top = pop(); 108 | printf("\t%.8g\n", top); 109 | push(top); 110 | } 111 | 112 | /* duplicateTop: duplicate the top element in the stack */ 113 | void duplicateTop(void) 114 | { 115 | double top; 116 | 117 | push(top = pop()); 118 | push(top); 119 | } 120 | 121 | /* swapTopTwo: swaps top two elements */ 122 | void swapTopTwo(void) 123 | { 124 | double top1, top2; 125 | 126 | top1 = pop(); 127 | top2 = pop(); 128 | push(top1); 129 | push(top2); 130 | } 131 | 132 | /* clearStacK: clear the stack */ 133 | void clearStack(void) 134 | { 135 | sp = 0; 136 | } 137 | 138 | /* reverse Polish Calculator */ 139 | int main(void) 140 | { 141 | int type; 142 | double op2; 143 | char s[MAXOP]; 144 | 145 | while ((type = getop(s)) != EOF) { 146 | switch (type) { 147 | case NUMBER: 148 | push(atof(s)); 149 | break; 150 | case '+': 151 | push(pop() + pop()); 152 | break; 153 | case '*': 154 | push(pop() * pop()); 155 | break; 156 | case '-': 157 | op2 = pop(); 158 | push(pop() - op2); 159 | break; 160 | case '/': 161 | op2 = pop(); 162 | if (op2 != 0.0) 163 | push(pop() / op2); 164 | else 165 | printf("error: zero divisor\n"); 166 | break; 167 | case '%': 168 | op2 = pop(); 169 | if (op2 != 0.0) 170 | push((long) pop() % (long) op2); 171 | else 172 | printf("error: zero divisor\n"); 173 | break; 174 | case '?': 175 | printTOP(); 176 | break; 177 | case 'd': 178 | duplicateTop(); 179 | break; 180 | case 's': 181 | swapTopTwo(); 182 | break; 183 | case 'c': 184 | clearStack(); 185 | break; 186 | case '\n': 187 | printf("\t%.8g\n", pop()); 188 | break; 189 | default: 190 | printf("error: unknown command %s\n", s); 191 | break; 192 | } 193 | } 194 | return 0; 195 | } 196 | -------------------------------------------------------------------------------- /chapter04/4-5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 4-5. Add access to library functions like sin, exp and pow. see 3 | * in Appendix B, Section 4. 4 | * 5 | Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | #include /* for atof() */ 10 | #include 11 | #include /* for strcmp() */ 12 | #include /* for math commands */ 13 | 14 | #define MAXOP 100 /* max size of operand or operator */ 15 | #define NUMBER '0' /* signal that a number was found */ 16 | #define NAME '1' /* signal that a string command was found */ 17 | #define MAXVAL 100 /* maximum depth of val stack */ 18 | #define BUFSIZE 100 19 | 20 | /* functions */ 21 | int getop(char []); 22 | void push(double); 23 | double pop(void); 24 | int getch(void); 25 | void ungetch(int); 26 | void printTOP(void); 27 | void duplicateTop(void); 28 | void swapTopTwo(void); 29 | int mathfunction(char []); 30 | 31 | /* globals */ 32 | int sp; /* next free stack position */ 33 | double val[MAXVAL]; /* value stack */ 34 | char buf[BUFSIZE]; /* buffer from ungetch */ 35 | int bufp; /* next free position in buf */ 36 | 37 | /* push: push f onto value stack */ 38 | void push(double f) 39 | { 40 | if (sp < MAXVAL) 41 | val[sp++] = f; 42 | else 43 | printf("error: stack full, can't push %g\n", f); 44 | } 45 | 46 | /* pop: pop and return top value from stack */ 47 | double pop(void) 48 | { 49 | if (sp > 0) 50 | return val[--sp]; 51 | else { 52 | printf("error: stack empty\n"); 53 | return 0.0; 54 | } 55 | } 56 | 57 | /* getop: get next operator or numeric operand */ 58 | int getop(char s[]) 59 | { 60 | int i, c; 61 | 62 | while ((s[0] = c = getch()) == ' ' || c == '\t') 63 | ; 64 | s[1] = '\0'; 65 | 66 | i = 0; 67 | if (c == '-') /* check sign */ 68 | if (!isdigit(s[++i] = c = getch())) { 69 | ungetch(c); 70 | c = s[0]; /* not a sign */ 71 | } 72 | 73 | if (isalpha(c)) { /* string command */ 74 | while (isalpha(s[++i] = c = getch())) 75 | ; 76 | s[i] = '\0'; 77 | ungetch(c); 78 | return NAME; 79 | } 80 | 81 | if (!isdigit(c) && c != '.') 82 | return c; /* not a number */ 83 | 84 | if (isdigit(c)) 85 | while (isdigit(s[++i] = c = getch())) 86 | ; 87 | if( c == '.') /* collect fraction part */ 88 | while (isdigit(s[++i] = c = getch())) 89 | ; 90 | s[i] = '\0'; 91 | 92 | if (c != EOF) 93 | ungetch(c); 94 | return NUMBER; 95 | } 96 | 97 | /* getch: get a (possibly pushed back) character */ 98 | int getch(void) 99 | { 100 | return (bufp > 0) ? buf[--bufp] : getchar(); 101 | } 102 | 103 | /* ungetch: push character back on input */ 104 | void ungetch(int c) 105 | { 106 | if (bufp >= BUFSIZE) 107 | printf("ungetch: too many characters\n"); 108 | else 109 | buf[bufp++] = c; 110 | } 111 | 112 | /* printTOP: print top of the stack without pop */ 113 | void printTOP(void) 114 | { 115 | double top; 116 | 117 | top = pop(); 118 | printf("\t%.8g\n", top); 119 | push(top); 120 | } 121 | 122 | /* duplicateTop: duplicate the top element in the stack */ 123 | void duplicateTop(void) 124 | { 125 | double top; 126 | 127 | push(top = pop()); 128 | push(top); 129 | } 130 | 131 | /* swapTopTwo: swaps top two elements */ 132 | void swapTopTwo(void) 133 | { 134 | double top1, top2; 135 | 136 | top1 = pop(); 137 | top2 = pop(); 138 | push(top1); 139 | push(top2); 140 | } 141 | 142 | /* clearStack: clear the stack */ 143 | void clearStack(void) 144 | { 145 | sp = 0; 146 | } 147 | 148 | /* mathfunction: call the appropriate math function according to value of s, 149 | * return 1 on success 0 on failure. */ 150 | int mathfunction(char s[]) 151 | { 152 | double op2; 153 | 154 | if (!strcmp(s, "sin")) 155 | push(sin(pop())); 156 | else if (!strcmp(s, "cos")) 157 | push(cos(pop())); 158 | else if (!strcmp(s, "exp")) 159 | push(exp(pop())); 160 | else if (!strcmp(s, "sqrt")) 161 | push(sqrt(pop())); 162 | else if (!strcmp(s, "pow")) { 163 | op2 = pop(); 164 | push(pow(pop(), op2)); 165 | } else 166 | return 0; 167 | return 1; 168 | } 169 | 170 | /* reverse Polish Calculator */ 171 | int main(void) 172 | { 173 | int type; 174 | double op2; 175 | char s[MAXOP]; 176 | 177 | while ((type = getop(s)) != EOF) { 178 | switch (type) { 179 | case NUMBER: 180 | push(atof(s)); 181 | break; 182 | case NAME: 183 | if (!mathfunction(s)) 184 | printf("error: unkown command %s\n", s); 185 | break; 186 | case '+': 187 | push(pop() + pop()); 188 | break; 189 | case '*': 190 | push(pop() * pop()); 191 | break; 192 | case '-': 193 | op2 = pop(); 194 | push(pop() - op2); 195 | break; 196 | case '/': 197 | op2 = pop(); 198 | if (op2 != 0.0) 199 | push(pop() / op2); 200 | else 201 | printf("error: zero divisor\n"); 202 | break; 203 | case '%': 204 | op2 = pop(); 205 | if (op2 != 0.0) 206 | push((long) pop() % (long) op2); 207 | else 208 | printf("error: zero divisor\n"); 209 | break; 210 | case '?': 211 | printTOP(); 212 | break; 213 | case 'd': 214 | duplicateTop(); 215 | break; 216 | case 's': 217 | swapTopTwo(); 218 | break; 219 | case 'c': 220 | clearStack(); 221 | break; 222 | case '\n': 223 | printf("\t%.8g\n", pop()); 224 | break; 225 | default: 226 | printf("error: unknown command %s\n", s); 227 | break; 228 | } 229 | } 230 | return 0; 231 | } 232 | -------------------------------------------------------------------------------- /chapter04/4-7.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 4-7. Write a routine ungets(s) that will push back an entire string 3 | * onto the input. Should ungets know about buf and bufp, or should it just use 4 | * ungetch? 5 | * 6 | * Answer: on one hand, the only clear advantage in ungets gaining direct 7 | * access to buf and bufp seems to be the ability to print the error message 8 | * directly rather than through ungetch. On the other, since ungetc is just 9 | * ungetch wrapped in a for loop, it makes sense to use ungetch and reduce code 10 | * duplication. 11 | * 12 | * Faisal Saadatmand 13 | */ 14 | 15 | #include 16 | #include /* for strlen() */ 17 | 18 | #define BUFSIZE 100 19 | #define MAXLEN 10000 20 | 21 | /* functions */ 22 | int getch(void); 23 | void ungetch(int); 24 | void ungets(char []); 25 | 26 | /* globals */ 27 | char buf[BUFSIZE]; /* buffer from ungetch */ 28 | int bufp = 0; /* next free position in buf */ 29 | 30 | /* getch: get a (possibly pushed back) character */ 31 | int getch(void) 32 | { 33 | return (bufp > 0) ? buf[--bufp] : getchar(); 34 | } 35 | 36 | /* ungerch: push character back on input */ 37 | void ungetch(int c) 38 | { 39 | if (bufp >= BUFSIZE) 40 | printf("ungetch: too many characters\n"); 41 | else 42 | buf[bufp++] = c; 43 | } 44 | 45 | /* ungets: push back s onto the input */ 46 | void ungets(char s[]) 47 | { 48 | int i; 49 | 50 | for (i = strlen(s) - 1; i >= 0 ; --i) 51 | ungetch(s[i]); 52 | } 53 | 54 | /* test ungets */ 55 | int main(void) 56 | { 57 | int c, i; 58 | char s[MAXLEN]; 59 | 60 | printf("Enter string to test ungets function:\n"); 61 | for (i = 0; (s[i] = getch()) != '\n'; i++) 62 | ; 63 | s[i++] ='\n'; 64 | s[i] = '\0'; 65 | ungets(s); 66 | 67 | while ((c = getch()) != EOF) 68 | putchar(c); 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /chapter04/4-8.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 4-8. Suppose that there will never be more than one character of 3 | * pushback. Modify getch and ungetch accordingly. 4 | * 5 | * Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | /* functions */ 11 | int getch(void); 12 | void ungetch(int); 13 | 14 | /* globals */ 15 | int buf; /* buffer from ungetch */ 16 | 17 | /* getch: get a (possibly pushed back) character */ 18 | int getch(void) 19 | { 20 | int c; 21 | 22 | if (buf) { 23 | c = buf; 24 | } else 25 | c = getchar(); 26 | buf = 0; 27 | return c; 28 | } 29 | 30 | /* ungerch: push character back on input */ 31 | void ungetch(int c) 32 | { 33 | if (buf) 34 | printf("ungetch: too many characters\n"); 35 | else 36 | buf = c; 37 | } 38 | 39 | /* test getch */ 40 | int main(void) 41 | { 42 | ungetch('A'); 43 | ungetch('B'); /* should give error */ 44 | printf ("%c\n", getch()); 45 | ungetch('C'); 46 | printf ("%c\n", getch()); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /chapter04/4-9.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 4-9. Our getch and ungetch do not handle a pushed-back EOF 3 | * correctly. Decide what their properties ought to be if an EOF is pushed 4 | * back, then implement your design. 5 | * 6 | * NOTE: the conversion of EOF (-1) from int to char and back to int may not 7 | * preserve negative value of EOF on some machines. Thus, we should declare buf[] 8 | * as int but[] to avoid conversion from from int to char to int of EOF. 9 | * 10 | * Faisal Saadatmand 11 | */ 12 | 13 | #include 14 | #include 15 | 16 | #define BUFSIZE 100 17 | 18 | /* functions */ 19 | int getch(void); 20 | void ungetch(int); 21 | void ungets(char []); 22 | 23 | /* globals */ 24 | int buf[BUFSIZE]; /* buffer from ungetch */ 25 | int bufp; /* next free position in buf */ 26 | 27 | /* getch: get a (possibly pushed back) character */ 28 | int getch(void) 29 | { 30 | return (bufp > 0) ? buf[--bufp] : getchar(); 31 | } 32 | 33 | /* ungerch: push character back on input */ 34 | void ungetch(int c) 35 | { 36 | if (bufp >= BUFSIZE) 37 | printf("ungetch: too many characters\n"); 38 | else 39 | buf[bufp++] = c; 40 | } 41 | 42 | /* test ungets */ 43 | int main(void) 44 | { 45 | int c; 46 | 47 | ungetch(EOF); 48 | ungetch('A'); 49 | ungetch('B'); 50 | ungetch('C'); 51 | ungetch('D'); 52 | 53 | while ((c = getch()) != EOF) 54 | putchar(c); 55 | printf("\n"); 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /chapter05/5-1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-1. As written, getint treats a + or - followed by a digit as a 3 | * valid representation of zero. Fix it to push such a character back on the 4 | * input. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #define BUFSIZE 100 13 | 14 | /* functions */ 15 | int getch(void); 16 | void ungetch(int); 17 | int getint(int *); 18 | 19 | /* global */ 20 | int buf[BUFSIZE]; /* buffer from ungetch (can handle EOF push back) */ 21 | int bufp = 0; /* next free position in buf */ 22 | 23 | int getch(void) /* get a (possibly pushed back) character */ 24 | { 25 | return (bufp > 0) ? buf[--bufp] : getchar(); 26 | } 27 | 28 | void ungetch(int c) /* push character back on input */ 29 | { 30 | if (bufp >= BUFSIZE) 31 | printf("ungetch: too many characters\n"); 32 | else 33 | buf[bufp++] = c; 34 | } 35 | 36 | /* getint: get next integer from input into *pn */ 37 | int getint(int *pn) 38 | { 39 | int c, sign; 40 | 41 | while (isspace(c = getch())) /* skip white space */ 42 | ; 43 | if (!isdigit(c) && c != '+' && c != '-') { 44 | ungetch(c); /* it is not a number */ 45 | return 0; 46 | } 47 | sign = (c == '-') ? -1 : 1; 48 | if (c == '+' || c == '-') 49 | if (!isdigit((c = getch()))) /* sign followed by a digit? */ 50 | return 0; /* not a number */ 51 | for (*pn = 0; isdigit(c); c = getch()) 52 | *pn = 10 * *pn + (c - '0'); 53 | *pn *= sign; 54 | ungetch(c); 55 | return c; 56 | } 57 | 58 | int main(void) 59 | { 60 | int input, num; 61 | 62 | while ((input = getint(&num)) && input != EOF) 63 | printf("%i\n", num); 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /chapter05/5-10.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-10. Write the program expr, which evaluates a reverse Polish 3 | * expression from the command line, where each operator or operand is a 4 | * separate argument. For example, 5 | * 6 | * expr 2 3 4 + * 7 | * 8 | * evaluates 2 * (3 + 4). 9 | * 10 | * By Faisal Saadatmand 11 | */ 12 | 13 | /* NOTE: * and % must be included in equation marks at the command prompt, 14 | * otherwise they will be interpreted by the shell as wildcard characters */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #define NUMBER '0' 21 | #define MAXVAL 100 22 | 23 | /* functions */ 24 | void push(double); 25 | double pop(void); 26 | 27 | /* globals */ 28 | int sp = 0; /* next free stack position */ 29 | double val[MAXVAL]; /* value stack */ 30 | 31 | /* push: push f onto value stack */ 32 | void push(double f) 33 | { 34 | if (sp < MAXVAL) 35 | val[sp++] = f; 36 | else 37 | printf("error: stack full, can't push %g\n", f); 38 | } 39 | 40 | /* pop: pop and return top value from stack */ 41 | double pop(void) 42 | { 43 | if (sp > 0) { 44 | return val[--sp]; 45 | } else { 46 | printf("error: stack empty\n"); 47 | return 0.0; 48 | } 49 | } 50 | 51 | int main(int argc, char *argv[]) 52 | { 53 | int type; 54 | double num, op2; 55 | 56 | if (argc < 4) { 57 | printf("Usage: %s ...\n", *argv); 58 | return -1; 59 | } 60 | 61 | while (--argc > 0) { 62 | ++argv; /* skip program name */ 63 | if ((num = atof(*argv))) 64 | type = NUMBER; 65 | else 66 | type = **argv; /* first character in *argv string */ 67 | switch (type) { 68 | case NUMBER: 69 | push(num); 70 | break; 71 | case '*': 72 | push(pop() * pop()); 73 | break; 74 | case '+': 75 | push(pop() + pop()); 76 | break; 77 | case '-': 78 | op2 = pop(); 79 | push(pop() - op2); 80 | break; 81 | case '/': 82 | op2 = pop(); 83 | if (op2 != 0.0) 84 | push(pop() / op2); 85 | else { 86 | printf("error: zero divisor\n"); 87 | argc = 1; 88 | } 89 | break; 90 | case '%': 91 | op2 = pop(); 92 | if (op2 != 0.0) 93 | push((long) pop() % (long) op2); 94 | else { 95 | printf("error: zero divisor\n"); 96 | argc = 1; 97 | } 98 | break; 99 | default: 100 | printf("error: unknown command %s\n", *argv); 101 | argc = 1; 102 | break; 103 | } 104 | } 105 | printf(" %.8g\n", pop()); /* print result */ 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /chapter05/5-11/detab.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-11. Modify the program and detab (written in exercises in Chapter 3 | * 1) to accept a list of tab stops as arguments. Use the default tab setting 4 | * if there are no arguments. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #define MAXLEN 1000 /* max length of input/output line */ 14 | #define MAXTABS 100 /* max number of tab stop positions */ 15 | #define N 4 /* default tabstops (every n columns) */ 16 | 17 | /* functions */ 18 | int getLine(char *, int); 19 | int setTabList(int, char **, int *); 20 | int ToTabStop(int, int *, int); 21 | void detab(char *, char *, int *, int); 22 | 23 | /* getLine function: read a line into s, return length */ 24 | int getLine(char *s, int lim) 25 | { 26 | int c; 27 | char *len; 28 | 29 | len = s; 30 | while (--lim > 0 && (c = getchar()) != EOF && c != '\n') 31 | *s++ = c; 32 | if (c == '\n') 33 | *s++ = c; 34 | *s = '\0'; 35 | return strlen(len); 36 | } 37 | 38 | /* setTabStops: extracts topstops positions from command line arguments and 39 | * store them in tabList. Return size of tabList, 0 if no tabstop list was 40 | * provided, or -1 if arg is an invalid tabstop value. */ 41 | int setTabList(int count, char **arg, int *tabList) 42 | { 43 | int i; 44 | 45 | for (i = 0; --count > 0; ++i) 46 | if (!(tabList[i] = atoi(*++arg)) || tabList[i] < 0) { 47 | printf("Invalid tabstop: %s\n", *arg); 48 | return -1; 49 | } 50 | return i; /* i is 0 if no tablist */ 51 | } 52 | 53 | /* toNextTab: return the number of blanks to the next tab stop position. */ 54 | int ToTabStop(int column, int *tabList, int tlSize) 55 | { 56 | if (!tlSize) 57 | return N - (column % N); 58 | /* if list exists, find nearest tab stop position in it */ 59 | while (tlSize != 0 && column >= *tabList) { 60 | ++tabList; 61 | --tlSize; 62 | } 63 | return *tabList - column; 64 | } 65 | 66 | /* detab function: replaces tabs with the proper number of blanks */ 67 | void detab(char *in, char *out, int *tabList, int tlSize) 68 | { 69 | int i; /* index for read line */ 70 | int j; /* index for modified (written) line */ 71 | int nblanks; /* number of required blanks */ 72 | 73 | for (i = j = 0; in[i] != '\0'; ++i) 74 | if (in[i] == '\t') { 75 | nblanks = ToTabStop(j, tabList, tlSize); 76 | while (nblanks-- > 0) 77 | out[j++] = ' '; 78 | } else 79 | out[j++] = in[i]; 80 | out[j] = '\0'; 81 | } 82 | 83 | int main(int argc, char *argv[]) 84 | { 85 | char in[MAXLEN]; /* currently read line */ 86 | char out[MAXLEN]; /* modified line */ 87 | int tabList[MAXTABS]; /* a list of tab stop positions */ 88 | int tlSize; /* size of tablist */ 89 | 90 | if ((tlSize = setTabList(argc, argv, tabList)) >= 0) 91 | while (getLine(in, MAXLEN) > 0) { 92 | detab(in, out, tabList, tlSize); 93 | printf("%s", out); 94 | } 95 | return 0; 96 | } 97 | -------------------------------------------------------------------------------- /chapter05/5-11/entab.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-11. Modify the program and detab (written in exercises in Chapter 3 | * 1) to accept a list of tab stops as arguments. Use the default tab setting 4 | * if there are no arguments. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #define MAXLEN 1000 /* max length of input/output line */ 14 | #define MAXTABS 100 /* max number of tab stop positions */ 15 | #define N 4 /* default tabstops (every n columns) */ 16 | 17 | /* functions */ 18 | int getLine(char *, int); 19 | void entab(char *, char *, int *, int); 20 | int setTabList(int, char **, int *); 21 | 22 | /* getLine function: read a line into s, return length */ 23 | int getLine(char *s, int lim) 24 | { 25 | int c; 26 | char *len; 27 | 28 | len = s; 29 | while (--lim > 0 && (c = getchar()) != EOF && c != '\n') 30 | *s++ = c; 31 | if (c == '\n') 32 | *s++ = c; 33 | *s = '\0'; 34 | return strlen(len); 35 | } 36 | 37 | /* setTabStops: extracts topstops positions from command line arguments and 38 | * store them in tabList. Return size of tabList, 0 if no tabstop list was 39 | * provided, or -1 if arg is an invalid tabstop value. */ 40 | int setTabList(int count, char **arg, int *tabList) 41 | { 42 | int i; 43 | 44 | for (i = 0; --count > 0; ++i) 45 | if (!(tabList[i] = atoi(*++arg)) || tabList[i] < 0) { 46 | printf("Invalid tabstop: %s\n", *arg); 47 | return -1; 48 | } 49 | return i; /* i is 0 if no tablist */ 50 | } 51 | 52 | /* tabStopPos: return the next tab stop position from tabList, or if not 53 | * tablist, return default tab stop. */ 54 | int tabStopPos(int column, int *tabList, int tlSize) 55 | { 56 | if (!tlSize) 57 | return N; 58 | /* if list exists, find the next tab stop position in it */ 59 | while (column >= *tabList) { 60 | ++tabList; 61 | --tlSize; 62 | } 63 | return *tabList - column; 64 | } 65 | 66 | /* entab function: replaces blanks with the minimum number of tabs and blanks */ 67 | void entab(char *in, char *out, int *tabList, int tlSize) 68 | { 69 | int i; /* index for read line */ 70 | int j; /* index for modified (written) line */ 71 | int nblanks; /* number of required blanks */ 72 | int ntabs; /* number of required tabs */ 73 | int tabStop; /* next tabstop position */ 74 | 75 | for (i = j = 0; in[i] != '\0'; ++i) { 76 | if (in[i] == ' ') { 77 | for (nblanks = ntabs = 0; in[i] == ' '; ++i) { /* count blanks */ 78 | tabStop = tabStopPos(i, tabList, tlSize); 79 | if ((i + 1) % tabStop == 0) { /* replace every N blanks with a tab */ 80 | ++ntabs; 81 | nblanks = 0; /* reset */ 82 | } else 83 | ++nblanks; 84 | } 85 | --i; /* adjust position after the loop */ 86 | while (ntabs-- > 0) /* insert tabs */ 87 | out[j++] = '\t'; 88 | while (nblanks-- > 0) /* insert remaining blanks */ 89 | out[j++] = ' '; 90 | } else 91 | out[j++] = in[i]; /* copy all other characters */ 92 | } 93 | out[j] = '\0'; 94 | } 95 | 96 | int main(int argc, char *argv[]) 97 | { 98 | char in[MAXLEN]; /* currently read line */ 99 | char out[MAXLEN]; /* modified line */ 100 | int tabList[MAXTABS]; /* a list of tab stop positions */ 101 | int tlSize; /* size of tablist */ 102 | 103 | if ((tlSize = setTabList(argc, argv, tabList)) < 0) 104 | return 1; 105 | while (getLine(in, MAXLEN) > 0) { 106 | entab(in, out, tabList, tlSize); 107 | printf("%s", out); 108 | } 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /chapter05/5-12/detab.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-12. Extend entab and detab to accept the shorthand 3 | * 4 | * entab -m +n 5 | * 6 | * to mean tab stops every n columns, starting at column m. Choose convenient 7 | * (for the user) default behaviour. 8 | * 9 | * By Faisal Saadatmand 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #define MAXLEN 1000 /* max length of input/output line */ 17 | 18 | /* functions */ 19 | int getLine(char *, int); 20 | int expandArg(int, char **, int *, int *); 21 | void detab(char *, char *, int *, int *); 22 | 23 | /* getLine function: read a line into s, return length */ 24 | int getLine(char *s, int lim) 25 | { 26 | int c; 27 | char *len; 28 | 29 | len = s; 30 | while (--lim > 0 && (c = getchar()) != EOF && c != '\n') 31 | *s++ = c; 32 | if (c == '\n') 33 | *s++ = c; 34 | *s = '\0'; 35 | return strlen(len); 36 | } 37 | 38 | int expandArg(int count, char **list, int *n, int *m) 39 | { 40 | int posError; 41 | char *prog = *list; 42 | 43 | posError = 0; 44 | while (--count > 0) { 45 | if (strcmp(*++list, "-m") == 0) { 46 | if ((*m = atoi(*++list)) < 1) { 47 | posError = 1; 48 | break; 49 | } 50 | --count; 51 | } else if (strcmp(*list, "+n") == 0) { 52 | if ((*n = atoi(*++list)) < 1) { 53 | posError = 1; 54 | break; 55 | } 56 | --count; 57 | } else { 58 | printf("Usage: %s [-m pos] [+n pos]\n", prog); 59 | return 0; 60 | } 61 | } 62 | if (posError) { 63 | printf("%s: invalid position: %s\n", prog, *list); 64 | return 0; 65 | } 66 | return 1; 67 | } 68 | 69 | /* detab function: replaces tabs with the proper number of blanks */ 70 | void detab(char *in, char *out, int *n, int *m) 71 | { 72 | int i; /* index for read line */ 73 | int j; /* index for modified (written) line */ 74 | int nblanks; /* number of required blanks */ 75 | 76 | for (i = j = 0; in[i] != '\0'; ++i) 77 | if ((j + 1) >= *m && in[i] == '\t') { 78 | nblanks = *n - (j % *n); 79 | while (nblanks-- > 0) 80 | out[j++] = ' '; 81 | } else 82 | out[j++] = in[i]; 83 | out[j] = '\0'; 84 | } 85 | 86 | int main(int argc, char *argv[]) 87 | { 88 | char in[MAXLEN]; /* currently read line */ 89 | char out[MAXLEN]; /* modified line */ 90 | int n = 4; /* default tabstops (every n columns) */ 91 | int m = 1; /* default starting position for tabstops */ 92 | 93 | if (expandArg(argc, argv, &n, &m)) 94 | while (getLine(in, MAXLEN) > 0) { 95 | detab(in, out, &n, &m); 96 | printf("%s", out); 97 | } 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /chapter05/5-12/entab.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-12. Extend entab and detab to accpt the shorthand 3 | * 4 | * entab -m +n 5 | * 6 | * to mean tab stops every n columns, starting at column m. Choose convenient 7 | * (for the user) default behaviour. 8 | * 9 | * By Faisal Saadatmand 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #define MAXLEN 1000 /* max length of input/output line */ 17 | #define MAXTABS 100 /* max number of tab stop positions */ 18 | 19 | /* functions */ 20 | int getLine(char *, int); 21 | void entab(char *, char *, int *, int *); 22 | int expandArg(int, char **, int *, int *); 23 | 24 | /* getLine function: read a line into s, return length */ 25 | int getLine(char *s, int lim) 26 | { 27 | int c; 28 | char *len; 29 | 30 | len = s; 31 | while (--lim > 0 && (c = getchar()) != EOF && c != '\n') 32 | *s++ = c; 33 | if (c == '\n') 34 | *s++ = c; 35 | *s = '\0'; 36 | return strlen(len); 37 | } 38 | 39 | int expandArg(int count, char **list, int *n, int *m) 40 | { 41 | int posError; 42 | char *prog = *list; 43 | 44 | if (count == 1) /* no options provided */ 45 | return 0; /* signal to use default n and m */ 46 | 47 | posError = 0; 48 | while (--count > 0) { 49 | if (strcmp(*++list, "-m") == 0) { 50 | if ((*m = atoi(*++list)) < 1) { 51 | posError = 1; 52 | break; 53 | } 54 | --count; 55 | } else if (strcmp(*list, "+n") == 0) { 56 | if ((*n = atoi(*++list)) < 1) { 57 | posError = 1; 58 | break; 59 | } 60 | --count; 61 | } else { 62 | printf("Usage: %s [-m pos] [+n pos]\n", prog); 63 | return -1; 64 | } 65 | } 66 | if (posError) { 67 | printf("%s: invalid position: %s\n", prog, *list); 68 | return -1; 69 | } 70 | return 1; 71 | } 72 | 73 | /* entab function: replaces blanks with the minimum number of tabs and blanks */ 74 | void entab(char *in, char *out, int *n, int *m) 75 | { 76 | int i; /* index for read line */ 77 | int j; /* index for modified (written) line */ 78 | int nblanks; /* number of required blanks */ 79 | int ntabs; /* number of required tabs */ 80 | 81 | for (i = j = 0; in[i] != '\0'; ++i) { 82 | if (in[i] == ' ') { 83 | for (nblanks = ntabs = 0; in[i] == ' '; ++i) { /* count blanks */ 84 | if (i + 1 >= *m && (i + 1) % *n == 0) { /* replace every N blanks with a tab */ 85 | ++ntabs; 86 | nblanks = 0; /* reset */ 87 | } else 88 | ++nblanks; 89 | } 90 | --i; /* adjust position after the loop */ 91 | while (ntabs-- > 0) /* insert tabs */ 92 | out[j++] = '\t'; 93 | while (nblanks-- > 0) /* insert remaining blanks */ 94 | out[j++] = ' '; 95 | } else 96 | out[j++] = in[i]; /* copy all other characters */ 97 | } 98 | out[j] = '\0'; 99 | } 100 | 101 | int main(int argc, char *argv[]) 102 | { 103 | char in[MAXLEN]; /* currently read line */ 104 | char out[MAXLEN]; /* modified line */ 105 | int n = 4; /* default tabstops (every n columns) */ 106 | int m = 1; /* default starting position for tabstops */ 107 | 108 | if (expandArg(argc, argv, &n, &m) < 0) 109 | return 1; 110 | while (getLine(in, MAXLEN) > 0) { 111 | entab(in, out, &n, &m); 112 | printf("%s", out); 113 | } 114 | return 0; 115 | } 116 | -------------------------------------------------------------------------------- /chapter05/5-13.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-13. Write the program tail. Which prints the last n lines of its 3 | * input. By default, n is set to 10, let us say, but it can be changed by an 4 | * optional argument so that 5 | * 6 | * tail -n 7 | * 8 | * prints the last n lines. The program should behave rationally no matter how 9 | * unreasonable the input or value of n. Write the program so it makes the best 10 | * use of available storage; lines should be stored as in the sorting program 11 | * of section 5.6, not in a two-dimensional array of fixed size. 12 | * 13 | * By Faisal Saadatmand 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #define MAXLEN 1000 /* max length of any input line */ 21 | #define MAXLINES 5000 /* max #lines to be stored */ 22 | #define ALLOCSIZE 10000 /* size of available space */ 23 | 24 | static char allocbuf[ALLOCSIZE]; /* storage for alloc */ 25 | static char *allocp = allocbuf; /* next free position */ 26 | 27 | 28 | /* functions */ 29 | int getLine(char *, int); 30 | char *alloc(int); 31 | int readlines(char *[], int); 32 | void writelines(char *[], int); 33 | int expandArg(int, char **, int *); 34 | 35 | /* getLine function: read a line into s, return length */ 36 | int getLine(char *s, int lim) 37 | { 38 | int c; 39 | char *len; 40 | 41 | len = s; 42 | while (--lim > 0 && (c = getchar()) != EOF && c != '\n') 43 | *s++ = c; 44 | if (c == '\n') 45 | *s++ = c; 46 | *s = '\0'; 47 | return strlen(len); 48 | } 49 | 50 | /* alloc: return pointer to n characters */ 51 | char *alloc(int n) 52 | { 53 | if (allocbuf + ALLOCSIZE - allocp >= n) { /* it fits */ 54 | allocp += n; 55 | return allocp - n; /*old p */ 56 | } 57 | return NULL; /* not enough room */ 58 | } 59 | 60 | /* readlines: read input lines */ 61 | int readlines(char *linerptr[], int maxlines) 62 | { 63 | int len, nlines; 64 | char *p, line[MAXLEN]; 65 | 66 | nlines = 0; 67 | while ((len = getLine(line, MAXLEN)) > 0) { 68 | if (nlines >= maxlines || (p = alloc(len)) == NULL) 69 | return -1; 70 | line[len - 1] = '\0'; /* delete newline */ 71 | strcpy(p, line); 72 | linerptr[nlines++] = p; 73 | } 74 | return nlines; 75 | } 76 | 77 | /* writelines: write output lines */ 78 | void writelines(char *lineptr[], int nlines) 79 | { 80 | while (nlines-- > 0) 81 | printf("%s\n", *lineptr++); 82 | } 83 | 84 | int expandArg(int count, char **list, int *n) 85 | { 86 | char *prog = *list, *end; 87 | 88 | while (--count > 0) 89 | if (!strcmp(*++list, "-n")) { 90 | end = NULL; 91 | if (!(*n = strtol(*++list, &end, 10)) || *end) { 92 | printf("%s: invalid number of lines: %s\n", prog, *list); 93 | return 0; 94 | } 95 | *n = abs(*n); /* treat negative numbers as positives */ 96 | --count; 97 | } else { 98 | printf("Usage: %s [-n lines]\n", prog); 99 | return 0; 100 | } 101 | return 1; 102 | } 103 | 104 | int main(int argc, char *argv[]) 105 | { 106 | char *lineptr[MAXLINES]; /* pointers to text lines */ 107 | int nlines; /* number of read lines */ 108 | int n = 10; /* default last n lines to print */ 109 | 110 | if (!expandArg(argc, argv, &n)) 111 | return -1; 112 | if ((nlines = readlines(lineptr, MAXLINES)) < 0) { 113 | printf("Error: input is too big\n"); 114 | return -1; 115 | } 116 | if (n > nlines) /* max limit check */ 117 | n = nlines; /* print entire input, not more */ 118 | writelines(lineptr + nlines - n, n); 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /chapter05/5-14.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-14. Modify the sort program to handle a -r flag, which indicates 3 | * sorting in reverse (decreasing) order. Be sure that -r works with -n. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define MAXLINES 5000 /* max #lines to be sorted */ 13 | #define MAXLEN 1000 /* max length of any input line */ 14 | #define ALLOCSIZE 100000 /* storage for alloc */ 15 | #define NUMERIC 01 /* numeric sort */ 16 | #define REVERSE 02 /* reverse sort */ 17 | 18 | typedef int (*funcP)(void *, void*); /* type alias to simplify syntax 19 | see section 6.7 */ 20 | 21 | /* functions */ 22 | int getLine(char *, int); 23 | int readlines(char *[], int); 24 | void witelines(char *[], int); 25 | char *alloc(int); 26 | void qSort(void *[], int, int, funcP); 27 | int numcmp(char *, char *); 28 | funcP comperator(int); 29 | 30 | /* globals */ 31 | static char allocbuf[ALLOCSIZE]; /* storage for alloc */ 32 | static char *allocp = allocbuf; /* next free position */ 33 | static char option; /* bit flag (1 byte in size) */ 34 | 35 | /* getLine: get line into s, return length of s -- pointer version */ 36 | int getLine(char *s, int lim) 37 | { 38 | int c, len; 39 | 40 | len = 0; 41 | while (--lim > 0 && (c = getchar()) != EOF && c != '\n') { 42 | *s++ = c; 43 | ++len; 44 | } 45 | if (c == '\n') { 46 | *s++ = c; 47 | ++len; 48 | } 49 | *s = '\0'; 50 | return len; 51 | } 52 | 53 | /* readlines: read input lines */ 54 | int readlines(char *lineptr[], int maxlines) 55 | { 56 | int len, nlines; 57 | char *p, line[MAXLEN]; 58 | 59 | nlines = 0; 60 | while ((len = getLine(line, MAXLEN)) > 0) { 61 | if (nlines >= maxlines || (p = alloc(len)) == NULL) 62 | return -1; 63 | line[len - 1] = '\0'; /* delete newline character */ 64 | strcpy(p, line); 65 | lineptr[nlines++] = p; 66 | } 67 | return nlines; 68 | } 69 | 70 | /* writelines: write output lines */ 71 | void writelines(char *lineptr[], int nlines, int reverse) 72 | { 73 | if (reverse) 74 | while (--nlines > 0) /* print lines in reverser order */ 75 | printf("%s\n", lineptr[nlines]); 76 | else 77 | while (nlines-- > 0) 78 | printf("%s\n", *lineptr++); 79 | } 80 | 81 | /* alloc: return pointer to n characters */ 82 | char *alloc(int n) 83 | { 84 | if (allocbuf + ALLOCSIZE - allocp < n) 85 | return 0; /* not enough room */ 86 | allocp += n; 87 | return allocp - n; /* old p */ 88 | } 89 | 90 | /* swap: swap the values of v[i] and v[j]. */ 91 | void swap(void *v[], int i, int j) 92 | { 93 | void *temp; 94 | 95 | temp = v[i]; 96 | v[i] = v[j]; 97 | v[j] = temp; 98 | } 99 | 100 | /* qsort: sort v[left]...V[right] into increasing order */ 101 | void qSort(void *v[], int left, int right, funcP comp) 102 | { 103 | int i, last; 104 | 105 | void swap(void *v[], int, int); 106 | 107 | if (left >= right) /* do nothing if array contains */ 108 | return; /* fewer than two elements */ 109 | swap(v, left, (left + right) / 2); 110 | last = left; 111 | for (i = left + 1; i <= right; i++) 112 | if ((*comp)(v[i], v[left]) < 0) 113 | swap(v, ++last, i); 114 | swap(v, left, last); 115 | qSort(v, left, last - 1, comp); 116 | qSort(v, last + 1, right, comp); 117 | } 118 | 119 | /* numcmp: compare s1 and s2 numerically */ 120 | int numcmp(char *s1, char *s2) 121 | { 122 | double v1, v2; 123 | 124 | v1 = atof(s1); 125 | v2 = atof(s2); 126 | if (v1 < v2) 127 | return -1; 128 | if (v1 > v2) 129 | return 1; 130 | return 0; 131 | } 132 | 133 | /* strCmp: return < 0 if s < t, 0 if s == t, > 0 if s > t */ 134 | int strCmp(char *s, char *t) 135 | { 136 | for ( ; *s == *t; s++, t++) 137 | if (*s == '\0') 138 | return 0; 139 | return *s - *t; 140 | } 141 | 142 | /* comparator: return a function pointer to the compare function corresponding 143 | * to the input option(s). If no option was provided, i.e. the bit flag 144 | * 'option' was 0, return a function pointer to strCmp. */ 145 | funcP comparator(int option) 146 | { 147 | /* 148 | * Because functions could use the comparator function to determine which 149 | * operation to perform, the order of the checks is important. Moreover, 150 | * when calling the comparator, the calling function must exclude itself 151 | * and any preceding options in the checks order from the option bit flag. 152 | */ 153 | return (funcP) (option & NUMERIC ? numcmp : strCmp); 154 | } 155 | 156 | /* sort input lines */ 157 | int main(int argc, char *argv[]) 158 | { 159 | char *lineptr[MAXLINES]; /* pointers to text lines */ 160 | int nlines; /* number of input lines read */ 161 | 162 | /* Note: no input error checking */ 163 | option = 0; 164 | while (--argc > 0) { 165 | ++argv; 166 | if (!strCmp(argv[0], "-n")) 167 | option |= NUMERIC; 168 | if (!strCmp(argv[0], "-r")) 169 | option |= REVERSE; 170 | } 171 | if ((nlines = readlines(lineptr, MAXLINES)) < 0) { 172 | printf("input too big to sort\n"); 173 | return 1; 174 | } 175 | qSort((void**) lineptr, 0, nlines - 1, comparator(option)); 176 | writelines(lineptr, nlines, option & REVERSE); 177 | return 0; 178 | } 179 | -------------------------------------------------------------------------------- /chapter05/5-15.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-15. Add the option -f to fold upper and lower case together, so 3 | * that case distinctions are not made during sorting; for example, a and A 4 | * compare equal. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define MAXLINES 5000 /* max #lines to be sorted */ 15 | #define MAXLEN 1000 /* max length of any input line */ 16 | #define ALLOCSIZE 100000 /* storage for alloc */ 17 | #define NUMERIC 01 /* numeric sort */ 18 | #define REVERSE 02 /* reverse sort */ 19 | #define FOLD 04 /* case-insensitive sort */ 20 | 21 | typedef int (*funcP)(void *, void*); /* type alias to simplify syntax 22 | see section 6.7 */ 23 | /* functions */ 24 | int getLine(char *, int); 25 | int readlines(char *[], int); 26 | void witelines(char *[], int, int); 27 | char *alloc(int); 28 | void qSort(void *[], int, int, funcP); 29 | int strCmp(char *, char *); 30 | int numcmp(char *, char *); 31 | int foldCmp(char *, char *); 32 | funcP comparator(int); 33 | 34 | /* globals */ 35 | static char allocbuf[ALLOCSIZE]; /* storage for alloc */ 36 | static char *allocp = allocbuf; /* next free position */ 37 | static char option; /* bit flag (1 byte in size) */ 38 | 39 | /* getLine: get line into s, return length of s -- pointer version */ 40 | int getLine(char *s, int lim) 41 | { 42 | int c, len; 43 | 44 | len = 0; 45 | while (--lim > 0 && (c = getchar()) != EOF && c != '\n') { 46 | *s++ = c; 47 | ++len; 48 | } 49 | if (c == '\n') { 50 | *s++ = c; 51 | ++len; 52 | } 53 | *s = '\0'; 54 | return len; 55 | } 56 | 57 | /* readlines: read input lines */ 58 | int readlines(char *lineptr[], int maxlines) 59 | { 60 | int len, nlines; 61 | char *p, line[MAXLEN]; 62 | 63 | nlines = 0; 64 | while ((len = getLine(line, MAXLEN)) > 0) { 65 | if (nlines >= maxlines || (p = alloc(len)) == NULL) 66 | return -1; 67 | line[len - 1] = '\0'; /* delete newline character */ 68 | strcpy(p, line); 69 | lineptr[nlines++] = p; 70 | } 71 | return nlines; 72 | } 73 | 74 | /* writelines: write output lines */ 75 | void writelines(char *lineptr[], int nlines, int reverse) 76 | { 77 | if (reverse) 78 | while (--nlines > 0) /* print lines in reverser order */ 79 | printf("%s\n", lineptr[nlines]); 80 | else 81 | while (nlines-- > 0) 82 | printf("%s\n", *lineptr++); 83 | } 84 | 85 | /* alloc: return pointer to n characters */ 86 | char *alloc(int n) 87 | { 88 | if (allocbuf + ALLOCSIZE - allocp < n) 89 | return 0; /* not enough room */ 90 | allocp += n; 91 | return allocp - n; /* old p */ 92 | } 93 | 94 | /* swap: swap the values of v[i] and v[j]. */ 95 | void swap(void *v[], int i, int j) 96 | { 97 | void *temp; 98 | 99 | temp = v[i]; 100 | v[i] = v[j]; 101 | v[j] = temp; 102 | } 103 | 104 | /* qsort: sort v[left]...V[right] into increasing order */ 105 | void qSort(void *v[], int left, int right, funcP comp) 106 | { 107 | int i, last; 108 | 109 | void swap(void *v[], int, int); 110 | 111 | if (left >= right) /* do nothing if array contains */ 112 | return; /* fewer than two elements */ 113 | swap(v, left, (left + right) / 2); 114 | last = left; 115 | for (i = left + 1; i <= right; i++) 116 | if ((*comp)(v[i], v[left]) < 0) 117 | swap(v, ++last, i); 118 | swap(v, left, last); 119 | qSort(v, left, last - 1, comp); 120 | qSort(v, last + 1, right, comp); 121 | } 122 | 123 | /* numcmp: compare s1 and s2 numerically */ 124 | int numcmp(char *s1, char *s2) 125 | { 126 | double v1, v2; 127 | 128 | v1 = atof(s1); 129 | v2 = atof(s2); 130 | if (v1 < v2) 131 | return -1; 132 | if (v1 > v2) 133 | return 1; 134 | return 0; 135 | } 136 | 137 | /* strCmp: return < 0 if s < t, 0 if s == t, > 0 if s > t */ 138 | int strCmp(char *s, char *t) 139 | { 140 | for ( ; *s == *t; s++, t++) 141 | if (*s == '\0') 142 | return 0; 143 | return *s - *t; 144 | } 145 | 146 | /* foldCmp: perform case-insensitive comparison between s and t */ 147 | int foldCmp(char *s, char *t) 148 | { 149 | char str1[MAXLEN], str2[MAXLEN]; 150 | int i; 151 | 152 | for (i = 0; *s; ++s) 153 | str1[i++] = tolower(*s); 154 | str1[i] = '\0'; 155 | for (i = 0; *t; ++t) 156 | str2[i++] = tolower(*t); 157 | str2[i] = '\0'; 158 | return strCmp(str1, str2); 159 | } 160 | 161 | /* comparator: return a function pointer to the compare function corresponding 162 | * to the input option(s). If no option was provided, i.e. the bit flag 163 | * 'option' was 0, return a function pointer to strCmp. */ 164 | funcP comparator(int option) 165 | { 166 | /* 167 | * Because functions use the comparator function to determine which 168 | * operation to perform, the order of the checks is important. Moreover, 169 | * when calling the comparator, the calling function must exclude itself 170 | * and any preceding options in the checks order from the option bit flag. 171 | */ 172 | return (funcP) (option & NUMERIC? numcmp : option & FOLD ? foldCmp : strCmp); 173 | } 174 | 175 | /* sort input lines */ 176 | int main(int argc, char *argv[]) 177 | { 178 | char *lineptr[MAXLINES]; /* pointers to text lines */ 179 | int nlines; /* number of input lines read */ 180 | 181 | /* Note: no input error checking */ 182 | option = 0; 183 | while (--argc > 0) { 184 | ++argv; 185 | if (strCmp(argv[0], "-n") == 0) 186 | option |= NUMERIC; 187 | if (strCmp(argv[0], "-r") == 0) 188 | option |= REVERSE; 189 | if (strCmp(argv[0], "-f") == 0) 190 | option |= FOLD; 191 | } 192 | if ((nlines = readlines(lineptr, MAXLINES)) < 0) { 193 | printf("input too big to sort\n"); 194 | return 1; 195 | } 196 | qSort((void**) lineptr, 0, nlines - 1, comparator(option)); 197 | writelines(lineptr, nlines, option & REVERSE); 198 | return 0; 199 | } 200 | -------------------------------------------------------------------------------- /chapter05/5-18.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-18. Make dcl recover from input error. 3 | * 4 | * By Faisal Saadatmand 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define BUFSIZE 100 12 | #define MAXLEN 1000 13 | #define MAXTOKEN 100 14 | 15 | enum { NAME, PARENS, BRACKETS }; 16 | enum { GOOD, FAIL }; 17 | 18 | /* functions */ 19 | void dcl(void); 20 | void dirdcl(void); 21 | int gettoken(); 22 | void errmsg(const char *); 23 | 24 | /* globals */ 25 | int buf[BUFSIZE]; /* buffer from ungetch */ 26 | int bufp = 0; /* next free position in buf */ 27 | int tokentype; /* type of last token */ 28 | char token[MAXTOKEN]; /* last token string */ 29 | char name[MAXTOKEN]; /* identifier name */ 30 | char datatype[MAXTOKEN]; /* data type = char, int, etc. */ 31 | char out[MAXLEN]; /* composed output string */ 32 | char state; /* flag to propagate the current state of parsing */ 33 | 34 | /* dcl: parse a declarator */ 35 | void dcl(void) 36 | { 37 | int ns; /* number of asterisks */ 38 | 39 | for (ns = 0; gettoken() == '*'; ++ns) /* count *'s */ 40 | ; 41 | dirdcl(); 42 | while (ns-- > 0) 43 | strcat(out, " pointer to"); 44 | } 45 | 46 | /* dirdcl: parse a direct declarator */ 47 | void dirdcl(void) 48 | { 49 | int type; 50 | 51 | if (tokentype == '(') { /* ( dcl ) */ 52 | dcl(); 53 | if (tokentype != ')') 54 | errmsg("error: missing )\n"); 55 | } else if (tokentype == NAME) /* variable name */ 56 | strcpy(name, token); 57 | else 58 | errmsg("error: expected name of (dcl)\n"); 59 | while ((type = gettoken()) == PARENS || type == BRACKETS) 60 | if (type == PARENS) 61 | strcat(out, " function returning"); 62 | else { 63 | strcat(out, " array"); 64 | strcat(out, token); 65 | strcat(out, " of"); 66 | } 67 | } 68 | 69 | /* errmsg: print error message, set state flag to FAIL */ 70 | void errmsg(const char *msg) 71 | { 72 | printf("%s", msg); 73 | state = FAIL; /* propagate state of parsing to allow for error recovery */ 74 | } 75 | 76 | /* gettoken: return next token */ 77 | int gettoken(void) 78 | { 79 | int c, getch(void); 80 | void ungetch(int); 81 | char *p = token; 82 | 83 | if (state == FAIL) { 84 | state = GOOD; 85 | return tokentype; /* push back the previous token */ 86 | } 87 | while ((c = getch()) == ' ' || c == '\t') 88 | ; 89 | if (c == '(') { 90 | if ((c = getch()) == ')') { 91 | strcpy(token, "()"); 92 | return tokentype = PARENS; 93 | } 94 | ungetch(c); 95 | return tokentype = '('; 96 | } 97 | if (c == '[') { 98 | for (*p++ = c; (*p++ = getch()) != ']'; ) 99 | ; 100 | *p = '\0'; 101 | return tokentype = BRACKETS; 102 | } 103 | if (isalpha(c)) { 104 | for (*p++ = c; isalnum(c = getch()); ) 105 | *p++ = c; 106 | *p = '\0'; 107 | ungetch(c); 108 | return tokentype = NAME; 109 | } 110 | return tokentype = c; 111 | } 112 | 113 | /* getch: get a (possibly pushed back) character */ 114 | int getch(void) 115 | { 116 | return (bufp > 0) ? buf[--bufp] : getchar(); 117 | } 118 | 119 | /* ungerch: push character back on input */ 120 | void ungetch(int c) 121 | { 122 | if (bufp >= BUFSIZE) 123 | printf("ungetch: too many characters\n"); 124 | else 125 | buf[bufp++] = c; 126 | } 127 | 128 | /* convert declaration to words */ 129 | int main(void) 130 | { 131 | while (gettoken() != EOF) { /* first token on line */ 132 | if (tokentype == '\n') /* skip empty lines */ 133 | continue; 134 | strcpy(datatype, token); /* is the datatype */ 135 | out[0] = '\0'; 136 | dcl(); /* parse rest of line */ 137 | if (tokentype != '\n') 138 | printf("%s", "syntax error\n"); 139 | else if (state == GOOD) 140 | printf("%s: %s %s\n", name, out, datatype); 141 | } 142 | return 0; 143 | } 144 | -------------------------------------------------------------------------------- /chapter05/5-19.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-19. Modify undcl so that it does not add redundant parentheses to 3 | * declaration. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define BUFSIZE 100 13 | #define MAXLEN 1000 14 | #define MAXTOKEN 100 15 | 16 | enum { NAME, PARENS, BRACKETS }; 17 | enum { GOOD, FAIL }; 18 | 19 | /* functions */ 20 | void undcl(void); 21 | int gettoken(); 22 | 23 | /* globals */ 24 | int buf[BUFSIZE]; /* buffer from ungetch */ 25 | int bufp = 0; /* next free position in buf */ 26 | int tokentype; /* type of last token */ 27 | char token[MAXTOKEN]; /* last token string */ 28 | char name[MAXTOKEN]; /* identifier name */ 29 | char datatype[MAXTOKEN]; /* data type = char, int, etc. */ 30 | char out[MAXLEN]; /* composed output string */ 31 | char state; /* flag to propagate the current state of parsing */ 32 | 33 | /* getch: get a (possibly pushed back) character */ 34 | int getch(void) 35 | { 36 | return (bufp > 0) ? buf[--bufp] : getchar(); 37 | } 38 | 39 | /* ungerch: push character back on input */ 40 | void ungetch(int c) 41 | { 42 | if (bufp >= BUFSIZE) 43 | printf("ungetch: too many characters\n"); 44 | else 45 | buf[bufp++] = c; 46 | } 47 | 48 | /* needsParens: check if the next token is a parenthesis or a bracket */ 49 | int needsParens() 50 | { 51 | void ungettokken(int); 52 | int nextType; 53 | 54 | nextType = gettoken(); /* peek ahead */ 55 | state = FAIL; /* signal token push back */ 56 | return nextType == PARENS || nextType == BRACKETS; 57 | } 58 | 59 | /* undcl: convert word descriptions to declarations */ 60 | void undcl(void) 61 | { 62 | int type; 63 | char temp[MAXLEN]; 64 | 65 | while ((type = gettoken()) != '\n') 66 | if (type == PARENS || type == BRACKETS) 67 | strcat(out, token); 68 | else if (type == '*') { 69 | sprintf(temp, needsParens() ? "(*%s)" : "*%s", out); 70 | strcpy(out, temp); 71 | } else if (type == NAME) { 72 | sprintf(temp, "%s %s", token, out); 73 | strcpy(out, temp); 74 | } else 75 | printf("invalid input at %s\n", token); 76 | } 77 | 78 | /* gettoken: return next token */ 79 | int gettoken(void) 80 | { 81 | int c, getch(void); 82 | void ungetch(int); 83 | char *p = token; 84 | 85 | if (state == FAIL) { 86 | state = GOOD; 87 | return tokentype; /* push back the previous token */ 88 | } 89 | while ((c = getch()) == ' ' || c == '\t') 90 | ; 91 | if (c == '(') { 92 | if ((c = getch()) == ')') { 93 | strcpy(token, "()"); 94 | return tokentype = PARENS; 95 | } 96 | ungetch(c); 97 | return tokentype = '('; 98 | } 99 | if (c == '[') { 100 | for (*p++ = c; (*p++ = getch()) != ']'; ) 101 | ; 102 | *p = '\0'; 103 | return tokentype = BRACKETS; 104 | } 105 | if (isalpha(c)) { 106 | for (*p++ = c; isalnum(c = getch()); ) 107 | *p++ = c; 108 | *p = '\0'; 109 | ungetch(c); 110 | return tokentype = NAME; 111 | } 112 | return tokentype = c; 113 | } 114 | 115 | int main(void) 116 | { 117 | while (gettoken() != EOF) { /* first token on line */ 118 | if (tokentype == '\n') /* skip empty input lines */ 119 | continue; 120 | strcpy(out, token); /* is the datatype */ 121 | undcl(); 122 | printf("%s\n", out); 123 | } 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /chapter05/5-2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-2. Write getfloat, the floating-point anolog of getint. What type 3 | * does getfloat return as its function value? 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | /* 9 | * Answer: It should return an int, which then could be used to check if the 10 | * operation was successful or not or if the EOF was reached. See main 11 | * function. 12 | */ 13 | 14 | #include 15 | #include 16 | 17 | #define BUFSIZE 100 18 | 19 | /* functions */ 20 | int getch(void); 21 | void unget(int); 22 | int getfloat(double *); 23 | 24 | /* global */ 25 | int buf[BUFSIZE]; /* buffer from ungetch (can handle EOF push back) */ 26 | int bufp = 0; /* next free position in buf */ 27 | 28 | int getch(void) /* get a (possibly pushed back) character */ 29 | { 30 | return (bufp > 0) ? buf[--bufp] : getchar(); 31 | } 32 | 33 | void ungetch(int c) /* push character back on input */ 34 | { 35 | if (bufp >= BUFSIZE) 36 | printf("ungetch: too many characters\n"); 37 | else 38 | buf[bufp++] = c; 39 | } 40 | 41 | /* getfloat: get next float from input into *pn */ 42 | int getfloat(double *pn) 43 | { 44 | int c, sign; 45 | double i; 46 | 47 | while (isspace((c = getch()))) /* skip whitespace */ 48 | ; 49 | if (!isdigit(c) && c != '.' && c != '+' && c != '-') { 50 | ungetch(c); /* it is not a number */ 51 | return 0; 52 | } 53 | sign = (c == '-') ? -1 : 1; 54 | if (c == '+' || c == '-') 55 | if (!isdigit((c = getch()))) /* is the sign is follwed by a digit? */ 56 | return 0; /* it is not a number */ 57 | for (*pn = 0; isdigit(c); c = getch()) 58 | *pn = 10 * *pn + (c - '0'); 59 | if (c == '.') 60 | for (i = 10.0F; isdigit(c = getch()); i = i * 10.0F) 61 | *pn += (c - '0') / i; 62 | *pn *= sign; 63 | ungetch(c); 64 | return c; 65 | } 66 | 67 | int main(void) 68 | { 69 | int input; 70 | double num; 71 | 72 | while ((input = getfloat(&num)) && input != EOF) 73 | printf("%g\n", num); 74 | 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /chapter05/5-3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-3. Write a pointer version of the function strcat that we showed 3 | * in Chapter 2: strcat(s,t) copies the string t to the end of s. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | /* functions */ 11 | void strCat(char *, char *); 12 | 13 | /* concatenate t to end of s; s must be big enough */ 14 | void strCat(char *s, char *t) 15 | { 16 | while (*s) /* find end of s */ 17 | ++s; 18 | while ((*s++ = *t++)) /* copy t */ 19 | ; 20 | } 21 | 22 | int main(void) 23 | { 24 | char str1[64] = "conca"; 25 | char str2[] = "tenate"; 26 | 27 | strCat(str1, str2); 28 | printf("%s\n", str1); 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /chapter05/5-4.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-4. Write the function strend(s, t), which returns 1 if string t 3 | * occurs at the end of the string s, and zero otherwise. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | /* functions */ 12 | int strend(char *, char *); 13 | 14 | /* strend: returns 1 if t occurs at the end of s, and zero otherwise */ 15 | int strend(char *s, char *t) 16 | { 17 | s += strlen(s) - strlen(t); /* advance the pointer */ 18 | while (*s && *t) 19 | if (*s++ != *t++) 20 | return 0;; 21 | return 1; 22 | } 23 | 24 | int main(void) 25 | { 26 | char string1[] = { "this is a string" }; 27 | char string2[] = { "string" }; 28 | 29 | printf("%i\n", strend(string1, string2)); 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /chapter05/5-5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-5. Write versions of the library functions strncpy, strncat, and 3 | * strncmp, which operate on at most the first n characters of their argument 4 | * strings. For example, strncpy(s, t, n) copies at most n characters of t to 5 | * s. Full descriptions are in Appendix B. 6 | * 7 | * By Faisal Saadatmand 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #define MAXCHAR 32 14 | 15 | /* functions */ 16 | char *strnCpy(char *, char *, const size_t); 17 | char *strnCat(char *, char *, const size_t); 18 | int strnCmp(const char *, const char *, const size_t); 19 | 20 | /* strnCpy: copy at most n characters of string t to s. Returns s. Pad with 21 | * '\0's if t has fewer than n characters */ 22 | char *strnCpy(char *s, char *t, const size_t n) 23 | { 24 | size_t i; 25 | 26 | for (i = 0; i < n; ++i) 27 | if (!(*s++ = *t++)) /* copy the character */ 28 | *s = '\0'; /* pad with '\0's, if t's length is < n */ 29 | *s = '\0'; /* add the terminating character */ 30 | 31 | return s - n; 32 | } 33 | 34 | /* strnCat: concatenate at most n characters of string t to string s, terminate 35 | * s with '\0'; return s */ 36 | char *strnCat(char *s, char *t, const size_t n) 37 | { 38 | size_t i, s_len, t_len, lim; 39 | 40 | s += (s_len = strlen(s)); /* advance pointer */ 41 | lim = (n > (t_len = strlen(t))) ? t_len : n; /* scale down n */ 42 | for (i = 0; i < lim && (*s++ = *t++); ++i) 43 | ; 44 | *s = '\0'; 45 | return s - s_len - i; 46 | } 47 | 48 | /* strnCmp: compare at most n characters of string s to string t; return < 0 49 | * if s < t, 0 if s == t, or > 0 if s > t. */ 50 | int strnCmp(const char *s, const char *t, const size_t n) 51 | { 52 | size_t i; 53 | 54 | for (i = 1; i < n && *s == *t; ++s, ++t, ++i) 55 | if (*s == '\0') 56 | return 0; 57 | return *s - *t; 58 | } 59 | 60 | int main(void) 61 | { 62 | char dest[MAXCHAR]; 63 | 64 | printf("%s\n", strnCpy(dest, "copy me", 4)); 65 | printf("%s\n", strnCat(dest, "concatenate", 4)); 66 | printf("%i\n", strnCmp ("samee", "same", 4)); 67 | printf("%i\n", strnCmp ("samee", "same", 5)); 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /chapter05/5-6/Makefile: -------------------------------------------------------------------------------- 1 | CC = clang 2 | CFLAGS = -g -Wall -Wextra -Werror -pedantic-errors 3 | DEPS = functions.h 4 | TARGET = main 5 | OBJ = $(TARGET).o functions.o 6 | 7 | %.o: %.cpp $(DEPS) 8 | $(CC) -c -o $@ $< $(CFLAGS) 9 | 10 | $(TARGET): $(OBJ) 11 | $(CC) -o $@ $^ $(CFLAGS) 12 | 13 | clean: 14 | rm -f *.o $(TARGET) $(TEST) 15 | -------------------------------------------------------------------------------- /chapter05/5-6/functions.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "functions.h" 6 | 7 | /* atoi: convert s to integer (chapter 3) */ 8 | int atoi(const char *s) 9 | { 10 | int n, sign; 11 | 12 | while (isspace(*s)) /* skip whitespace */ 13 | ++s; 14 | sign = (*s == '-') ? -1 : 1; 15 | if (*s == '+' || *s == '-') /* skip sign */ 16 | ++s; 17 | for (n = 0; isdigit(*s); ++s) 18 | n = 10 * n + (*s - '0'); 19 | return sign * n; 20 | } 21 | 22 | /* atof: convert string s to double (chapter 4) */ 23 | double atof(const char *s) 24 | { 25 | int sign, expSign; 26 | double val, power, exponent; 27 | 28 | while (isspace(*s)) /* skip whitespace */ 29 | ++s; 30 | sign = (*s == '-') ? -1 : 1; 31 | if (*s == '+' || *s == '-') 32 | ++s; 33 | for (val = 0.0; isdigit(*s); ++s) 34 | val = 10.0 * val + (*s - '0'); 35 | if (*s == '.') 36 | ++s; 37 | for (power = 1.0; isdigit(*s); ++s) { 38 | val = 10.0 * val + (*s - '0'); 39 | power *= 10.0; 40 | } 41 | if (tolower(*s) == 'e') /* handle scientific notation */ 42 | ++s; 43 | expSign = (*s == '-') ? -1 : 1; /* record exponent's sign */ 44 | if (*s == '+' || *s == '-') 45 | ++s; 46 | for (exponent = 0.0; isdigit(*s); s++) /* extract the exponent */ 47 | exponent = 10.0 * exponent + (*s - '0'); 48 | while (exponent-- != 0) /* adjust power according to exponent */ 49 | power = (expSign > 0) ? power / 10: power * 10; 50 | return sign * val / power; 51 | } 52 | 53 | /* itoa: convert n to characters in s (chapter 3) */ 54 | void itoa(int n, char *s) 55 | { 56 | int sign; 57 | 58 | sign = n; /* record sign */ 59 | 60 | do { /* generate digits in reverse order */ 61 | *s++ = abs(n % 10 + '0'); /* get the next digit */ 62 | } while ((n /= 10) > 0); /* delete it */ 63 | if (sign < 0) 64 | *s++ = '-'; 65 | *s = '\0'; 66 | reverse(s - strlen(s)); 67 | } 68 | 69 | /* getLine function: read a line into s, return length (chapter 4) */ 70 | int getLine(char *s, int lim) 71 | { 72 | int c; 73 | char *len; 74 | 75 | len = s; 76 | while (--lim > 0 && (c = getchar()) != EOF && c != '\n') 77 | *s++ = c; 78 | if (c == '\n') 79 | *s++ = c; 80 | *s = '\0'; 81 | return strlen(len); 82 | } 83 | 84 | /* getop: get next operator or numeric operand (chapter 4) - getch/ungetch version */ 85 | int getop(char *s) 86 | { 87 | int c; 88 | char *var; 89 | 90 | while (isblank(s[0] = c = getchar())) 91 | ; 92 | s[1] = '\0'; 93 | if (c == '-') /* check sign */ 94 | if (!isdigit(*++s = c = getchar())) { 95 | ungetc(c, stdin); 96 | c = *--s; /* not a sign */ 97 | } 98 | if (isalpha(c)) { /* string command */ 99 | var = s; 100 | while (isalpha(*++s = c = getchar())) 101 | ; 102 | *s = '\0'; 103 | ungetc(c, stdin); 104 | return (strlen(var) == 1) ? var[0] : NAME; 105 | } 106 | if (!isdigit(c) && c != '.') 107 | return c; /* not a number */ 108 | if (isdigit(c)) 109 | while (isdigit(*++s = c = getchar())) 110 | ; 111 | if( c == '.') /* collect fraction part */ 112 | while (isdigit(*++s = c = getchar())) 113 | ; 114 | *s = '\0'; 115 | ungetc(c, stdin); 116 | return NUMBER; 117 | } 118 | 119 | /* reverse: reverses the string s in place (chapter 3) */ 120 | void reverse(char *s) 121 | { 122 | char *p, *q, tmp; 123 | 124 | q = s + strlen(s) - 1; 125 | for (p = s; p < q; ++p, --q) { 126 | tmp = *p; 127 | *p = *q; 128 | *q = tmp; 129 | } 130 | } 131 | 132 | /* strindex: return index of t in s, -1 if none (chapter 4) */ 133 | int strindex(const char *s, const char *t) 134 | { 135 | int pos; 136 | const char *j, *k; 137 | 138 | for (pos = 0; *s; ++s, ++pos) { 139 | for (j = s, k = t; *k && (*j == *k); ++j, ++k) 140 | ; 141 | if (k > t && *k == '\0') 142 | return pos; 143 | } 144 | return -1; 145 | } 146 | -------------------------------------------------------------------------------- /chapter05/5-6/functions.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_H 2 | #define FUNCTIONS_H 3 | 4 | #include 5 | 6 | #define NUMBER '0' /* signal that a number was found */ 7 | #define NAME '1' /* signal that a string command was found */ 8 | 9 | int atoi(const char *); 10 | double atof(const char *); 11 | void itoa(int , char *); 12 | int getLine(char *, int); 13 | int getop(char *); 14 | void reverse(char *); 15 | int strindex(const char *, const char *); 16 | 17 | #endif /* FUNCTIONS_H */ 18 | -------------------------------------------------------------------------------- /chapter05/5-6/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-6. Rewrite appropriate programs from earlier chapters and 3 | * exercises with pointers instead of array indexing. Good possibilities 4 | * include getLine (chapter 1 and 4), atoi, itoa, and their variants (Chapters 5 | * 2, 3 and 4), reverse (Chapter 3), and strindex and gettop (Chapter 4) 6 | * 7 | * By Faisal Saadatmand 8 | */ 9 | 10 | /* 11 | * Just use any function listed above in main.c to test its code. Below are 12 | * some examples. 13 | * 14 | * Instructions: 15 | * To Build: 16 | * make main 17 | * To run: 18 | * ./main 19 | * To clean object files: 20 | * make clean 21 | * 22 | */ 23 | 24 | #include 25 | #include "functions.h" 26 | 27 | 28 | int main(void) 29 | { 30 | char str[] = "It's not personal"; 31 | const char *pattern = "son"; 32 | char str2[] = "5892"; 33 | int pos, number; 34 | 35 | /* strindex */ 36 | if ((pos = strindex(str, pattern)) < 0) 37 | printf("Not found\n"); 38 | else 39 | printf("%i\n", pos); 40 | /* reverse */ 41 | reverse(str); 42 | printf("%s\n", str); 43 | /* atoi */ 44 | number = atoi(str2); 45 | printf("%i\n", number); 46 | printf("%i\n", atoi("4092")); 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /chapter05/5-7-alloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-7. Rewrite readlines to store lines in an array supplied by main, 3 | * rather than calling alloc to maintain storage. How much faster is the 4 | * program? 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #define MAXLINES 5000 /* max #lines to be stored */ 12 | #define MAXLEN 1000 /* max length of any input line */ 13 | #define ALLOCSIZE 100000 /* storage for alloc */ 14 | 15 | /* functions */ 16 | int readlines(char *[], int); 17 | void writelines(char *[], int); 18 | int getLine(char *, int); 19 | char *alloc(int); 20 | void qsort(char *[], int, int); 21 | void swap(char *[], int, int); 22 | 23 | /* globals */ 24 | char *lineptr[MAXLINES]; /* pointers to text lines */ 25 | static char allocbuf[ALLOCSIZE]; /* storage for alloc */ 26 | static char *allocp = allocbuf; /* next free position */ 27 | 28 | /* readlines: read input lines */ 29 | int readlines(char *lineptr[], int maxlines) 30 | { 31 | int len, nlines; 32 | char *p, line[MAXLEN]; 33 | 34 | nlines = 0; 35 | while ((len = getLine(line, MAXLEN)) > 0) 36 | if (nlines >= maxlines || (p = alloc(len)) == NULL) 37 | return -1; 38 | else { 39 | line[len - 1] = '\0'; /* delete newline character */ 40 | strcpy(p, line); 41 | lineptr[nlines++] = p; 42 | } 43 | return nlines; 44 | } 45 | 46 | /* getLine: get line into s, return length of s -- pointer version */ 47 | int getLine(char *s, int lim) 48 | { 49 | int c, len; 50 | 51 | len = 0; 52 | while (--lim > 0 && (c = getchar()) != EOF && c != '\n') { 53 | *s++ = c; 54 | ++len; 55 | } 56 | if ( c == '\n') { 57 | *s++ = c; 58 | ++len; 59 | } 60 | *s = '\0'; 61 | return len; 62 | } 63 | 64 | char *alloc(int n) /* return pointer to n characters */ 65 | { 66 | if (allocbuf + ALLOCSIZE - allocp >=n) { /* it fits */ 67 | allocp += n; 68 | return allocp - n; /* old p */ 69 | } else /* not enough room */ 70 | return 0; 71 | } 72 | 73 | /* writelines: write output lines */ 74 | void writelines(char *lineptr[], int nlines) 75 | { 76 | while (nlines-- > 0) 77 | printf("%s\n", *lineptr++); 78 | } 79 | 80 | /* qsort: sort v[left]...v[right] into increasing order */ 81 | void qsort(char *v[], int left, int right) 82 | { 83 | int i, last; 84 | void swap(char *v[], int i, int j); 85 | 86 | if (left >= right) /* do nothing if array contains */ 87 | return; /* fewer than two elements */ 88 | 89 | swap(v, left, (left + right) / 2); 90 | last = left; 91 | 92 | for (i = left + 1; i <= right; i++) 93 | if (strcmp(v[i], v[left]) < 0) 94 | swap(v, ++last, i); 95 | swap(v, left, last); 96 | qsort(v, left, last - 1); 97 | qsort(v, last + 1, right); 98 | } 99 | 100 | /* swap: interchange v[i] and v[j] */ 101 | void swap(char *v[], int i, int j) 102 | { 103 | char *temp; 104 | 105 | temp = v[i]; 106 | v[i] = v[j]; 107 | v[j] = temp; 108 | } 109 | 110 | /* sort lines */ 111 | int main(void) 112 | { 113 | int nlines; /* number of input lines read */ 114 | 115 | if ((nlines = readlines(lineptr, MAXLINES)) >= 0) { 116 | qsort(lineptr, 0, nlines - 1); 117 | writelines(lineptr, nlines); 118 | return 0; 119 | } else { 120 | printf("error: input too big to sort\n"); 121 | return 1; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /chapter05/5-7.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-7. Rewrite readlines to store lines in an array supplied by main, 3 | * rather than calling alloc to maintain storage. How much faster is the 4 | * program? 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | /* 10 | * Answer: With the current implementation both clock_t and unix time command 11 | * report similar runtimes. This readlines, however, should be faster because 12 | * it doesn't have the overhead of calling the alloc function on each iteration 13 | * of its loop. 14 | */ 15 | 16 | #include 17 | #include 18 | 19 | #define MAXLINES 5000 /* max #lines to be stored */ 20 | #define MAXLEN 1000 /* max length of any input line */ 21 | #define MAXSIZE 10000 /* size of available space */ 22 | 23 | /* functions */ 24 | int readlines(char *[], int, char []); 25 | void writelines(char *[], int); 26 | int getLine(char *, int); 27 | void qsort(char *[], int, int); 28 | void swap(char *[], int, int); 29 | 30 | /* readlines: read input lines */ 31 | int readlines(char *lineptr[], int maxlines, char text[]) 32 | { 33 | int len, nlines; 34 | char *p, line[MAXLEN]; 35 | 36 | p = text; 37 | nlines = 0; 38 | while ((len = getLine(line, MAXLEN)) > 0) { 39 | if (nlines >= maxlines || p >= text + MAXSIZE) 40 | return -1; 41 | line[len - 1] = '\0'; /* delete newline character */ 42 | strcpy(p, line); 43 | lineptr[nlines++] = p; 44 | p += len; 45 | } 46 | return nlines; 47 | } 48 | 49 | /* getLine: get line into s, return length of s -- pointer version */ 50 | int getLine(char *s, int lim) 51 | { 52 | int c, len; 53 | 54 | len = 0; 55 | while (--lim > 0 && (c = getchar()) != EOF && c != '\n') { 56 | *s++ = c; 57 | ++len; 58 | } 59 | if ( c == '\n') { 60 | *s++ = c; 61 | ++len; 62 | } 63 | *s = '\0'; 64 | return len; 65 | } 66 | 67 | /* writelines: write output lines */ 68 | void writelines(char *lineptr[], int nlines) 69 | { 70 | while (nlines-- > 0) 71 | printf("%s\n", *lineptr++); 72 | } 73 | 74 | /* qsort: sort v[left]...v[right] into increasing order */ 75 | void qsort(char *v[], int left, int right) 76 | { 77 | int i, last; 78 | void swap(char *v[], int i, int j); 79 | if (left >= right) /* do nothing if array contains */ 80 | return; /* fewer than two elements */ 81 | swap(v, left, (left + right) / 2); 82 | last = left; 83 | for (i = left + 1; i <= right; i++) 84 | if (strcmp(v[i], v[left]) < 0) 85 | swap(v, ++last, i); 86 | swap(v, left, last); 87 | qsort(v, left, last - 1); 88 | qsort(v, last + 1, right); 89 | } 90 | 91 | /* swap: interchange v[i] and v[j] */ 92 | void swap(char *v[], int i, int j) 93 | { 94 | char *temp; 95 | 96 | temp = v[i]; 97 | v[i] = v[j]; 98 | v[j] = temp; 99 | } 100 | 101 | /* sort lines */ 102 | int main(void) 103 | { 104 | int nlines; 105 | char *lineptr[MAXLINES]; /* pointers to text lines */ 106 | char text[MAXSIZE]; /* array to store lines */ 107 | 108 | if ((nlines = readlines(lineptr, MAXLINES, text)) < 0) { 109 | printf("error: input too big to sort\n"); 110 | return -1; 111 | } 112 | qsort(lineptr, 0, nlines - 1); 113 | writelines(lineptr, nlines); 114 | return 0; 115 | } 116 | -------------------------------------------------------------------------------- /chapter05/5-8.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-8. There is no error checking in day_of_year or month_day. Remedy 3 | * this defect. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | /* functions */ 11 | int day_of_year(int, int, int); 12 | void month_day(int, int, int *, int *); 13 | 14 | static char daytab[2][13] = { 15 | {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 16 | {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} 17 | }; 18 | 19 | /* day_of_year: set day of year from month & day; return -1 on invalid input */ 20 | int day_of_year(int year, int month, int day) 21 | { 22 | int i, leap; 23 | 24 | if (year < 0 || month < 1 || month > 12 || day < 1 || day > 31) 25 | return -1; 26 | leap = (year % 4 == 0 && year % 100) || year % 400 == 0; 27 | for (i = 1; i < month; i++) 28 | day += daytab[leap][i]; 29 | return day; 30 | } 31 | 32 | /* month_day: set month, day from day of year; zero value is an invalid input */ 33 | void month_day(int year, int yearday, int *pmonth, int *pday) 34 | { 35 | int i, leap; 36 | 37 | if (year < 0 || yearday < 1 || yearday > 366) { 38 | *pmonth = *pday = 0; 39 | return; 40 | } 41 | leap = (year % 4 == 0 && year % 100) || year % 400 == 0; 42 | for (i = 1; yearday > daytab[leap][i]; i++) 43 | yearday -= daytab[leap][i]; 44 | *pmonth = i; 45 | *pday = yearday; 46 | } 47 | 48 | int main(void) 49 | { 50 | int yearday, month, day; 51 | 52 | if ((yearday = day_of_year(1986, 3, 35)) < 0) 53 | printf("Invalid input\n"); 54 | else 55 | printf("day of the year: %i\n", yearday); 56 | month_day(2020, 367, &month, &day); 57 | if (!month || !day) 58 | printf("Invalid input\n"); 59 | else 60 | printf("month %i, day %i\n", month, day); 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /chapter05/5-9.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 5-9. Rewrite the routine day_of_year and month_day with pointers 3 | * instead of indexing. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | 10 | /* functions */ 11 | int day_of_year(int, int, int); 12 | void month_day(int, int, int *, int *); 13 | char *month_name(int); 14 | 15 | static char daytab[][13] = { 16 | {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 17 | {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} 18 | }; 19 | 20 | char (*pdaytab)[13] = daytab; /* pointer to first element (1st array) */ 21 | /* we could also use datab name directly */ 22 | 23 | /* day_of_year: set day of year from month & day; return -1 on invalid input */ 24 | int day_of_year(int year, int month, int day) 25 | { 26 | int i, leap; 27 | 28 | if ((year < 1) || month < 1 || month > 12 || day < 1 || day > 31) 29 | return -1; 30 | leap = (year % 4 == 0 && year % 100) || year % 400 == 0; 31 | for (i = 1; i < month; i++) 32 | day += *(*(pdaytab + leap) + i); 33 | return day; 34 | } 35 | 36 | /* month_day: set month, day from day of year; zero value is an invalid input */ 37 | void month_day(int year, int yearday, int *pmonth, int *pday) 38 | { 39 | int i, leap; 40 | 41 | if (year < 0 || yearday < 1 || yearday > 366) { 42 | *pmonth = *pday = 0; 43 | return; 44 | } 45 | leap = (year % 4 == 0 && year % 100) || year % 400 == 0; 46 | for (i = 1; yearday > daytab[leap][i]; i++) 47 | yearday -= *(*(pdaytab + leap) + i); 48 | *pmonth = i; 49 | *pday = yearday; 50 | } 51 | 52 | /* month_name: return name of n-th month */ 53 | char *month_name(int n) 54 | { 55 | static char *name[] = { 56 | "illegal month", 57 | "January", "February", "March", 58 | "April", "May", "June", 59 | "July", "August", "September", 60 | "October", "November", "December" 61 | }; 62 | 63 | return (n < 1 || n > 12) ? name[0] : name[n]; 64 | } 65 | 66 | int main(void) 67 | { 68 | int yearday, month, day; 69 | 70 | if ((yearday = day_of_year(2018, 9, 3)) < 0) 71 | printf("Invalid input\n"); 72 | else 73 | printf("day of the year: %i\n", yearday); 74 | month_day(2018, 246, &month, &day); 75 | if (!month || !day) 76 | printf("Invalid input\n"); 77 | else 78 | printf("%s %i\n", month_name(month), day); 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /chapter06/6-1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 6-1. Our version of getword does not properly handle underscores, 3 | * string constants, comments, or preprocessor control lines. Write a better 4 | * version. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #define MAXWORD 100 14 | #define BUFSIZE 100 15 | #define NKEYS (int) (sizeof keytab / sizeof keytab[0]) 16 | 17 | /* types */ 18 | struct key { 19 | char *word; 20 | int count; 21 | }; 22 | 23 | /* functions */ 24 | int binsearch(char *, struct key[], int); 25 | int getword(char *, int); 26 | 27 | /* globals */ 28 | int buf[BUFSIZE]; /* buffer from ungetch */ 29 | int bufp = 0; /* next free position in buf */ 30 | 31 | struct key keytab[] = { 32 | { "auto", 0 }, 33 | 34 | { "case", 0 }, 35 | { "char", 0 }, 36 | { "const", 0 }, 37 | { "continue", 0 }, 38 | { "default", 0 }, 39 | { "do", 0 }, 40 | { "double", 0 }, 41 | { "else", 0 }, 42 | { "enum", 0 }, 43 | { "extern", 0 }, 44 | { "float", 0 }, 45 | { "goto", 0 }, 46 | { "if", 0 }, 47 | { "int", 0 }, 48 | { "long", 0 }, 49 | { "register", 0 }, 50 | { "return", 0 }, 51 | { "short", 0 }, 52 | { "signed", 0 }, 53 | { "sizeof", 0 }, 54 | { "static", 0 }, 55 | { "struct", 0 }, 56 | { "switch", 0 }, 57 | { "typeof", 0 }, 58 | { "union", 0 }, 59 | { "unsigned", 0 }, 60 | { "void", 0 }, 61 | { "volatile", 0 }, 62 | { "while", 0 }, 63 | }; 64 | 65 | /* binsearch: find word in tab[0]...tab[n-1] */ 66 | int binsearch(char *word, struct key tab[], int n) 67 | { 68 | int cond; 69 | int low, high, mid; 70 | 71 | low = 0; 72 | high = n - 1; 73 | while (low <= high) { 74 | mid = (low + high) / 2; 75 | if ((cond = strcmp(word, tab[mid].word)) < 0) 76 | high = mid - 1; 77 | else if (cond > 0) 78 | low = mid + 1; 79 | else 80 | return mid; 81 | } 82 | return -1; 83 | } 84 | 85 | /* getword: get next word or character from input */ 86 | int getword(char *word, int lim) 87 | { 88 | int c, getch(void); 89 | void ungetch(int); 90 | char *w = word; 91 | 92 | while (isspace(c = getch())) 93 | ; 94 | if (c != EOF) 95 | *w++ = c; 96 | if (isalpha(c) || c == '_' || c == '#') { 97 | for ( ; --lim > 0; ++w) 98 | if (!isalnum(*w = getch()) && *w != '_') { 99 | ungetch(*w); 100 | break; 101 | } 102 | } else if (c == '\'') /* skip character constants */ 103 | while ((c = getch()) != '\'') 104 | ; 105 | else if (c == '\"') { /* skip string constants */ 106 | while ((c = getch()) != '\"') 107 | if (c == '\\') 108 | getch(); 109 | } else if (c == '/' && (c = getch()) == '*') /* skip comments */ 110 | while ((c = getch()) != EOF) 111 | if (c == '*' && (c = getch()) == '/') 112 | break; 113 | *w ='\0'; 114 | return c; 115 | } 116 | 117 | /* get a (possibly pushed back) character */ 118 | int getch(void) 119 | { 120 | return (bufp > 0) ? buf[--bufp] : getchar(); 121 | } 122 | 123 | /* push character back on input */ 124 | void ungetch(int c) 125 | { 126 | if (bufp >= BUFSIZE) 127 | printf("ungetch: too many characters\n"); 128 | else 129 | buf[bufp++] = c; 130 | } 131 | 132 | /* count C keywords */ 133 | int main(void) 134 | { 135 | int n; 136 | char word[MAXWORD]; 137 | 138 | while (getword(word, MAXWORD) != EOF) 139 | if (isalpha(word[0])) 140 | if ((n = binsearch(word, keytab, NKEYS)) >= 0) 141 | ++keytab[n].count; 142 | for (n = 0; n < NKEYS; ++n) 143 | if (keytab[n].count > 0) 144 | printf("%4d %s\n", keytab[n].count, keytab[n].word); 145 | return 0; 146 | } 147 | -------------------------------------------------------------------------------- /chapter06/6-3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 6-3. Write a cross-referencer that prints a list of all words in a 3 | * document, and, for each word, a list of the line numbers on which it occurs. 4 | * Remove noise words like "the," "and," and so on. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define MAXWORD 100 15 | #define BUFSIZE 100 16 | #define NKEYS (sizeof noisetab / sizeof noisetab[0]) 17 | 18 | /* types */ 19 | struct list { 20 | int number; 21 | struct list *next; 22 | }; 23 | 24 | struct tnode { 25 | char *word; 26 | struct list *lines; 27 | struct tnode *left; 28 | struct tnode *right; 29 | }; 30 | 31 | struct key { 32 | char *word; 33 | int count; 34 | }; 35 | 36 | /* functions */ 37 | int getword(char *, int); 38 | struct tnode *talloc(void); /* allocate memory to new tree node */ 39 | char *strDup(char *); /* copy string into safe place */ 40 | struct tnode *addtree(struct tnode *, char *, int); 41 | void treeprint(struct tnode *); 42 | void printList(struct list *); 43 | struct key *binsearch(char *, struct key *, int); 44 | void freetree(struct tnode *); 45 | 46 | /* globals */ 47 | int buf[BUFSIZE]; /* buffer from ungetch */ 48 | int bufp = 0; /* next free position in buf */ 49 | 50 | struct key noisetab[] = { 51 | { "a", 0 }, 52 | { "and", 0 }, 53 | { "is", 0 }, 54 | { "of", 0}, 55 | { "on", 0}, 56 | { "the", 0 }, 57 | { "to", 0 }, 58 | { "was", 0}, 59 | { "were", 0}, 60 | { "with", 0}, 61 | }; 62 | 63 | /* getword: get next word or character from input */ 64 | int getword(char *word, int lim) 65 | { 66 | int c, getch(void); 67 | void ungetch(int); 68 | char *w = word; 69 | 70 | while (isblank(c = getch())) 71 | ; 72 | if (c != EOF) 73 | *w++ = c; 74 | if (!isalpha(c)) { 75 | *w = '\0'; 76 | return c; 77 | } 78 | for ( ; --lim > 0; ++w) 79 | if (!isalnum(*w = getch())) { 80 | ungetch(*w); 81 | break; 82 | } 83 | *w = '\0'; 84 | return word[0]; 85 | } 86 | 87 | /* get a (possibly pushed back) character */ 88 | int getch(void) 89 | { 90 | return (bufp > 0) ? buf[--bufp] : getchar(); 91 | } 92 | 93 | /* push character back on input */ 94 | void ungetch(int c) 95 | { 96 | if (bufp >= BUFSIZE) 97 | printf("ungetch: too many characters\n"); 98 | else 99 | buf[bufp++] = c; 100 | } 101 | 102 | /* talloc: make a tnode */ 103 | struct tnode *talloc(void) 104 | { 105 | return malloc(sizeof(struct tnode)); 106 | } 107 | 108 | /*strDup: make a duplicate of s */ 109 | char *strDup(char *s) 110 | { 111 | char *p; 112 | 113 | p = malloc(strlen(s) + 1); /* +1 for '\0' */ 114 | if (p != NULL) 115 | strcpy(p, s); 116 | return p; 117 | } 118 | 119 | /* addList: add a node with ln, at or before p */ 120 | struct list *addlist(struct list *p, int ln) 121 | { 122 | if (!p) { 123 | p = malloc(sizeof(struct list)); 124 | p->number = ln; 125 | p->next = NULL; 126 | } else if (p->number != ln) /* skip multi-occurrence on same line */ 127 | p->next = addlist(p->next, ln); 128 | return p; 129 | } 130 | 131 | /* addtree: add a node with w, at or below p */ 132 | struct tnode *addtree(struct tnode *p, char *w, int ln) 133 | { 134 | int cond; 135 | struct list *first = NULL; 136 | 137 | if (!p) { /* a new word has arrived */ 138 | p = talloc(); /* make a new node */ 139 | p->word = strDup(w); /* copy data to it */ 140 | p->lines = addlist(first, ln); 141 | p->left = p->right = NULL; 142 | } else if (!(cond = strcmp(w, p->word))) 143 | p->lines = addlist(p->lines, ln); 144 | else if (cond < 0) /* less than into left subtree */ 145 | p->left = addtree(p->left, w, ln); 146 | else 147 | p->right = addtree(p->right, w, ln); 148 | return p; 149 | } 150 | 151 | /* treeprint: in-order print of tree p */ 152 | void treeprint(struct tnode *p) 153 | { 154 | if (!p) 155 | return; 156 | treeprint(p->left); 157 | printf(" %s ", p->word); 158 | printList(p->lines); 159 | printf("\n"); 160 | treeprint(p->right); 161 | } 162 | 163 | /* printList: preorder print of list p */ 164 | void printList(struct list *p) 165 | { 166 | if (!p) 167 | return; 168 | printf("%d ", p->number); 169 | printList(p->next); 170 | } 171 | 172 | /* binsearch: find word in tab[0]...tab[n - 1] */ 173 | struct key *binsearch(char *word, struct key *tab, int n) 174 | { 175 | int cond; 176 | struct key *low = &tab[0]; 177 | struct key *high = &tab[n]; 178 | struct key *mid; 179 | 180 | while (low < high) { 181 | mid = low + (high - low) / 2; 182 | if ((cond = strcmp(word, mid->word)) < 0) 183 | high = mid; 184 | else if (cond > 0) 185 | low = mid + 1; 186 | else 187 | return mid; 188 | } 189 | return NULL; 190 | } 191 | 192 | /* freellist: frees allocated heap memory of linked list */ 193 | void freelist(struct list *node) 194 | { 195 | if (!node) 196 | return; 197 | freelist(node->next); 198 | free(node); 199 | } 200 | 201 | /* freetree: frees allocated heap memory of tree */ 202 | void freetree(struct tnode *node) 203 | { 204 | void freelist(struct list *); 205 | 206 | if (!node) 207 | return; 208 | freetree(node->left); 209 | freetree(node->right); 210 | free(node->word); 211 | freelist(node->lines); /* delete linked list in nodes */ 212 | free(node); 213 | } 214 | 215 | int main(void) 216 | { 217 | struct tnode *root; /* root node */ 218 | char word[MAXWORD]; /* currently read word */ 219 | int lineno = 1; /* currently searched line */ 220 | 221 | root = NULL; 222 | while (getword(word, MAXWORD) != EOF) 223 | if (isalpha(word[0]) && !binsearch(word, noisetab, NKEYS)) 224 | root = addtree(root, word, lineno); 225 | else if (word[0] == '\n') 226 | ++lineno; 227 | treeprint(root); 228 | /* clean up */ 229 | freetree(root); 230 | root = NULL; 231 | return 0; 232 | } 233 | -------------------------------------------------------------------------------- /chapter06/6-4.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Write a program that prints the distinct words in its input sorted into 3 | * decreasing order of frequency of occurrence. Precede each word by its count. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | /* NOTE: I included a definition for sorttree and copytree for academic 9 | * purposes. They are not used in the code */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define MAXWORD 100 17 | #define BUFSIZE 100 18 | 19 | /* types */ 20 | struct tnode { 21 | char *word; 22 | int count; 23 | struct tnode *left; 24 | struct tnode *right; 25 | }; 26 | 27 | /* functions */ 28 | int getword(char *, int); 29 | struct tnode *talloc(void); /* allocate memory to new tree node */ 30 | char *strDup(char *); /* copy string into safe place */ 31 | struct tnode *addtree(struct tnode *, char *); 32 | void printtree(struct tnode *); 33 | void freetree(struct tnode *); 34 | struct tnode *sorttree(struct tnode *, struct tnode *); /* optional */ 35 | 36 | /* globals */ 37 | int buf[BUFSIZE]; /* buffer from ungetch */ 38 | int bufp = 0; /* next free position in buf */ 39 | 40 | /* addtree: add a node with w, at or below p */ 41 | struct tnode *addtree(struct tnode *p, char *w) 42 | { 43 | int cond; 44 | 45 | if (!p) { /* a new word has arrived */ 46 | p = talloc(); /* make a new node */ 47 | p->word = strDup(w); /* copy data to it */ 48 | p->count = 1; 49 | p->left = p->right = NULL; 50 | } else if (!(cond = strcmp(w, p->word))) 51 | ++p->count; /* repeated word */ 52 | else if (cond < 0) /* less than into left subtree */ 53 | p->left = addtree(p->left, w); 54 | else 55 | p->right = addtree(p->right, w); 56 | return p; 57 | } 58 | 59 | /* print: in-order print of tree p - decreasing order version */ 60 | void printtree(struct tnode *p) 61 | { 62 | if (p) { 63 | printtree(p->right); 64 | printf("%4d %s\n", p->count, p->word); 65 | printtree(p->left); 66 | } 67 | } 68 | 69 | /* copyTree: copy nodes in root into p according to frequency of occurrence. */ 70 | struct tnode *copyTree(struct tnode *p, struct tnode *root) 71 | { 72 | if (!p) { 73 | p = talloc(); 74 | p->word = strDup(root->word); 75 | p->count = root->count; 76 | p->left = p->right = NULL; 77 | } else if (root->count <= p->count) 78 | p->left = copyTree(p->left, root); 79 | else 80 | p->right = copyTree(p->right, root); 81 | return p; 82 | } 83 | 84 | /* sorttree: performs inorder traversal on root and creates a BST p according 85 | * to frequency of occurrence */ 86 | struct tnode *sorttree(struct tnode *p, struct tnode *root) 87 | { 88 | struct tnode *copyTree(struct tnode *, struct tnode *); 89 | 90 | if (root) { 91 | p = sorttree(p, root->left); 92 | p = copyTree(p, root); 93 | p = sorttree(p, root->right); 94 | } 95 | return p; 96 | } 97 | 98 | /* talloc: make a tnode */ 99 | struct tnode *talloc(void) 100 | { 101 | return malloc(sizeof(struct tnode)); 102 | } 103 | 104 | /*strDup: make a duplicate of s */ 105 | char *strDup(char *s) 106 | { 107 | char *p; 108 | 109 | p = malloc(strlen(s) + 1); /* +1 for '\0' */ 110 | if (p) 111 | strcpy(p, s); 112 | return p; 113 | } 114 | 115 | /* freetree: free allocated heap memory of node tree */ 116 | void freetree(struct tnode *node) 117 | { 118 | if (!node) 119 | return; 120 | freetree(node->left); 121 | freetree(node->right); 122 | free(node->word); 123 | free(node); 124 | } 125 | 126 | /* getword: get next word or character from input */ 127 | int getword(char *word, int lim) 128 | { 129 | int c, getch(void); 130 | void ungetch(int); 131 | char *w = word; 132 | 133 | while (isblank(c = getch())) 134 | ; 135 | if (c != EOF) 136 | *w++ = c; 137 | if (!isalpha(c)) { 138 | *w = '\0'; 139 | return c; 140 | } 141 | for ( ; --lim > 0; w++) 142 | if (!isalnum(*w = getch())) { 143 | ungetch(*w); 144 | break; 145 | } 146 | *w = '\0'; 147 | return word[0]; 148 | } 149 | 150 | /* get a (possibly pushed back) character */ 151 | int getch(void) 152 | { 153 | return (bufp > 0) ? buf[--bufp] : getchar(); 154 | } 155 | 156 | /* push character back on input */ 157 | void ungetch(int c) 158 | { 159 | if (bufp >= BUFSIZE) 160 | printf("ungetch: too many characters\n"); 161 | else 162 | buf[bufp++] = c; 163 | } 164 | 165 | int main(void) 166 | { 167 | struct tnode *root; /* root node */ 168 | char word[MAXWORD]; /* currently read word */ 169 | 170 | root = NULL; 171 | while (getword(word, MAXWORD) != EOF) 172 | if (isalpha(word[0])) 173 | root = (addtree(root, word)); /* build tree */ 174 | printtree(root); 175 | freetree(root); 176 | root = NULL; 177 | return 0; 178 | } 179 | -------------------------------------------------------------------------------- /chapter06/6-5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 6-5. Write a function undef that will remove a name and a 3 | * definition from the table maintained by lookup and install. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define HASHSIZE 101 13 | 14 | struct nlist { /* table entry: */ 15 | struct nlist *next; /* next entry in chain */ 16 | char *name; /* defined name */ 17 | char *defn; /* replacement text */ 18 | }; 19 | 20 | /* functions */ 21 | unsigned hash(char *); 22 | //char *strDup(char *); 23 | struct nlist *lookup(char *); 24 | struct nlist *install(char *, char *); 25 | void undef(char *); 26 | void printtab(struct nlist *[], int); 27 | void freetable(struct nlist *[], int); 28 | 29 | /* globals */ 30 | static struct nlist *hashtab[HASHSIZE]; /* pointer table */ 31 | 32 | /*strDup: make a duplicate of s. Error checking is handled by the caller */ 33 | char *strDup(char *s) 34 | { 35 | char *p; 36 | 37 | p = malloc(strlen(s) + 1); /* +1 for '\0' */ 38 | if (p) 39 | strcpy(p, s); 40 | return p; 41 | } 42 | 43 | /* hash: form hash value for string s */ 44 | unsigned hash(char *s) 45 | { 46 | unsigned hashval; 47 | 48 | for (hashval = 0; *s; s++) 49 | hashval = *s + (31 * hashval); 50 | return hashval % HASHSIZE; 51 | } 52 | 53 | /* lookup: look for s in hashtab */ 54 | struct nlist *lookup(char *s) 55 | { 56 | struct nlist *np; 57 | 58 | for (np = hashtab[hash(s)]; np; np = np->next) 59 | if (!strcmp(s, np->name)) 60 | return np; /* found */ 61 | return NULL; 62 | } 63 | 64 | /* install: put (name, def) in hashtab */ 65 | struct nlist *install(char *name, char *defn) 66 | { 67 | struct nlist *np; 68 | unsigned hashval; 69 | 70 | if (!(np = lookup(name))) { /* not found */ 71 | np = malloc(sizeof(*np)); 72 | if (!np || !(np->name = strDup(name))) 73 | return NULL; /* no (heap) memory */ 74 | hashval = hash(name); 75 | np->next = hashtab[hashval]; 76 | hashtab[hashval] = np; 77 | } else /* already there */ 78 | free((void *) np->defn); /* free previous definition */ 79 | 80 | np->defn = strDup(defn); /* copy definition */ 81 | 82 | if (!np->defn) 83 | return NULL; 84 | return np; 85 | } 86 | 87 | /* undef: remove (name, def) in hash table. Takes name as argument */ 88 | void undef(char *s) 89 | { 90 | struct nlist *np; 91 | 92 | if ((np = lookup(s))) { 93 | free((void *) np->name); /* free name memory */ 94 | free((void *) np->defn); /* free definition memory */ 95 | hashtab[hash(s)] = np->next; /* clear index or relink next node */ 96 | free((void *) np); /* free node memory */ 97 | } 98 | } 99 | 100 | /* printtab: prints content of hashtable, linear scan */ 101 | void printtab(struct nlist *node[], int size) 102 | { 103 | int i; 104 | for (i = 0; i < size; ++i) 105 | if (node[i]) 106 | printf("%i name: %s defn: %s\n", i, node[i]->name, node[i]->defn); 107 | } 108 | 109 | /* freetable: free table's (and its content's) allocated memory from heap */ 110 | void freetable(struct nlist *node[], int size) 111 | { 112 | int i; 113 | 114 | for (i = 0; i < size; ++i) 115 | if (node[i]) { 116 | free(node[i]->name); 117 | free(node[i]->defn); 118 | free(node[i]); 119 | } 120 | } 121 | 122 | int main(void) 123 | { 124 | struct nlist *p; 125 | 126 | /* insert nodes (skipped error checking) */ 127 | p = install("YES", "1"); 128 | p = install("NO", "0"); 129 | printf("Hash Table Values:\n"); 130 | printtab(hashtab, HASHSIZE); 131 | /* delete a node */ 132 | printf("\nDelete \"YES\"\n\n"); 133 | undef("YES"); 134 | printf("Hash Table Values (After Deletion):\n"); 135 | printtab(hashtab, HASHSIZE); 136 | freetable(hashtab, HASHSIZE); /* clean up */ 137 | return 0; 138 | } 139 | -------------------------------------------------------------------------------- /chapter06/6-6.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 6-6. Implement a simple version of the #define processor (i.e., no 3 | * arguments) suitable for use with C programs, based on the routines of this 4 | * section. You may also find getch and ungetch helpful. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define HASHSIZE 101 15 | #define MAXWORD 100 16 | #define MAXLEN 5000 17 | #define BUFSIZE 100 18 | 19 | struct nlist { /* table entry: */ 20 | struct nlist *next; /* next entry in chain */ 21 | char *name; /* defined name */ 22 | char *defn; /* replacement text */ 23 | }; 24 | 25 | /* globals */ 26 | int buf[BUFSIZE]; /* buffer from ungetch */ 27 | int bufp = 0; /* next free position in buf */ 28 | static struct nlist *hashtab[HASHSIZE]; /* pointer table */ 29 | 30 | /* functions */ 31 | int getword(char *, int); 32 | int getdef(char *, int); 33 | char *strDup(char *); 34 | unsigned hash(char *); 35 | struct nlist *lookup(char *); 36 | struct nlist *install(char *, char *); 37 | void printtab(struct nlist *[], int); 38 | void freetable(struct nlist *[], int); 39 | 40 | /* getword: get next word or character from input */ 41 | int getword(char *word, int lim) 42 | { 43 | int c, getch(void); 44 | void ungetch(int); 45 | char *w = word; 46 | 47 | while (isspace(c = getch())) 48 | ; 49 | if (c != EOF) 50 | *w++ = c; 51 | if (isalpha(c) || c == '_' || c == '#') { 52 | for ( ; --lim > 0; ++w) 53 | if (!isalnum(*w = getch()) && *w != '_') { 54 | ungetch(*w); 55 | break; 56 | } 57 | } else if (c == '\'') /* skip character constants */ 58 | while ((c = getch()) != '\'') 59 | ; 60 | else if (c == '\"') { /* skip string constants */ 61 | while ((c = getch()) != '\"') 62 | if (c == '\\') 63 | getch(); 64 | } else if (c == '/' && (c = getch()) == '*') /* skip comments */ 65 | while ((c = getch()) != EOF) 66 | if (c == '*' && (c = getch()) == '/') 67 | break; 68 | *w ='\0'; 69 | return c; 70 | } 71 | 72 | /* get a (possibly pushed back) character */ 73 | int getch(void) 74 | { 75 | return (bufp > 0) ? buf[--bufp] : getchar(); 76 | } 77 | 78 | /* push character back on input */ 79 | void ungetch(int c) 80 | { 81 | if (bufp >= BUFSIZE) 82 | printf("ungetch: too many characters\n"); 83 | else 84 | buf[bufp++] = c; 85 | } 86 | 87 | /* getdef: copy the rest of the line int s, return length of s. This is a 88 | * modified version of getLine, which skips leading blanks andd does not insert 89 | * a newline character at the end. */ 90 | int getdef(char *s, int lim) 91 | { 92 | int c, len; 93 | 94 | while (isspace(c = getch())) 95 | ; 96 | *s++ = c; 97 | len = 0; 98 | while (--lim > 0 && (c = getchar()) != EOF && c != '\n') { 99 | *s++ = c; 100 | ++len; 101 | } 102 | *s = '\0'; 103 | return len; 104 | } 105 | 106 | /*strDup: make a duplicate of s. Error checking is handled by the caller */ 107 | char *strDup(char *s) 108 | { 109 | char *p; 110 | 111 | p = malloc(strlen(s) + 1); /* +1 for '\0' */ 112 | if (p) 113 | strcpy(p, s); 114 | return p; 115 | } 116 | 117 | /* hash: form hash value for string s */ 118 | unsigned hash(char *s) 119 | { 120 | unsigned hashval; 121 | 122 | for (hashval = 0; *s; ++s) 123 | hashval = *s + (31 * hashval); 124 | return hashval % HASHSIZE; 125 | } 126 | 127 | /* lookup: look for s in hashtab */ 128 | struct nlist *lookup(char *s) 129 | { 130 | struct nlist *np; 131 | 132 | for (np = hashtab[hash(s)]; np; np = np->next) 133 | if (!strcmp(s, np->name)) 134 | return np; /* found */ 135 | return NULL; 136 | } 137 | 138 | /* install: put (name, def) in hashtab */ 139 | struct nlist *install(char *name, char *defn) 140 | { 141 | struct nlist *np; 142 | unsigned hashval; 143 | 144 | if (!(np = lookup(name))) { /* not found */ 145 | np = malloc(sizeof(*np)); 146 | if (!np || !(np->name = strDup(name))) 147 | return NULL; /* no (heap) memory */ 148 | hashval = hash(name); 149 | np->next = hashtab[hashval]; 150 | hashtab[hashval] = np; 151 | } else /* already there */ 152 | free((void *) np->defn); /* free previous definition */ 153 | if (!(np->defn = strDup(defn))) /* copy definition */ 154 | return NULL; 155 | return np; 156 | } 157 | /* printtab: prints content of hashtable, linear scan */ 158 | void printtab(struct nlist *node[], int size) 159 | { 160 | int i; 161 | 162 | for (i = 0; i < size; ++i) 163 | if (node[i]) 164 | printf("%i name: %s defn: %s\n", i, node[i]->name, node[i]->defn); 165 | } 166 | 167 | /* freetable: free table's (and its content's) allocated memory from heap */ 168 | void freetable(struct nlist *node[], int size) 169 | { 170 | int i; 171 | 172 | for (i = 0; i < size; ++i) 173 | if (node[i]) { 174 | free(node[i]->name); 175 | free(node[i]->defn); 176 | free(node[i]); 177 | } 178 | } 179 | 180 | int main (void) 181 | { 182 | char word[MAXWORD]; 183 | char defn[MAXLEN]; 184 | const char *keyword = "#define"; 185 | 186 | while (getword(word, MAXWORD) != EOF) 187 | if (word[0] == '#' && !strcmp(word, keyword)) { /* found #define */ 188 | getword(word, MAXLEN); /* get the name */ 189 | getdef(defn, MAXLEN); /* parse definition */ 190 | install(word, defn); 191 | } 192 | printf("Hash Table Values:\n"); 193 | printtab(hashtab, HASHSIZE); 194 | freetable(hashtab, HASHSIZE); /* clean up */ 195 | return 0; 196 | } 197 | -------------------------------------------------------------------------------- /chapter07/7-1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 7-1. Write a program that converts upper case to lower or lower 3 | * case to upper, depending on the name it is invoked with, as found in 4 | * argv[0]. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | /* parsename: if program name starts with '.', parse it, i.e. trim the full 14 | * pathname and return name only, otherwise return name as is. */ 15 | char *parsename(char *name) 16 | { 17 | return name[0] == '.' ? strrchr(name, '/') + 1 : name; 18 | } 19 | 20 | int main(int argc, char *argv[]) 21 | { 22 | int c; 23 | int (*func)(int); /* function pointer to tolower or toupper */ 24 | char *prog; /* program name */ 25 | 26 | --argc; 27 | prog = parsename(argv[0]); 28 | if (!strcmp("tolower", prog)) 29 | func = tolower; 30 | else if (!strcmp("toupper", prog)) 31 | func = toupper; 32 | else { 33 | printf("Error: rename program to tolower or toupper\n"); 34 | return -1; 35 | } 36 | while ((c = getchar()) != EOF) 37 | putchar(func(c)); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /chapter07/7-2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 7-2. Write a program that will print arbitrary input in a sensible 3 | * way. As a minimum, it should print non-graphic characters in octal or 4 | * hexadecimal according to local custom, and break long text lines. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #define MAXLEN 80 13 | #define NCHAR 6 /* number of actually printed characters */ 14 | 15 | /* inclen: increment line's length by the number of actually printed 16 | * characters. Break the line if its too long. */ 17 | int inclen(int len, int n) 18 | { 19 | if ((len += n) < MAXLEN) 20 | return len; 21 | printf("\n"); 22 | return n; 23 | } 24 | 25 | int main(void) 26 | { 27 | int c; 28 | int len; /* length of currently read line */ 29 | 30 | len = 0; 31 | while ((c = getchar()) != EOF) 32 | if (!isgraph(c)) { 33 | printf(" 0x%02o ", c); /* replace by NCHAR hex characters */ 34 | len = inclen(len, NCHAR); 35 | if (c == '\n') { 36 | printf("\n"); /* break the line */ 37 | len = 0; 38 | } 39 | } else { /* graphical character */ 40 | printf("%c", c); 41 | len = inclen(len, 1); 42 | } 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /chapter07/7-3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 7-3. Revise minprintf to handle more of the other facilities of 3 | * printf. 4 | * Note: all flags (except for left adjust) and variable width/length are not implemented. 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define MAXNUMB 20 15 | #define FLT_PRCISION 6 16 | #define FIELD_WIDTH 1 17 | 18 | /* macros */ 19 | #define PRINT_N(la,x,w,v, nc) (la) ? (nc += printf("%-*" # x, w, v)) : \ 20 | (nc += printf("%*" #x, w, v)) 21 | #define PRINT_D(la,x,w,p,v,nc) (la) ? (nc += printf("%-*.*" # x, w, p, v)) : \ 22 | (nc += printf("%*.*" # x, w, p, v)) 23 | 24 | /* functionst */ 25 | int minprintf(char *, ...); 26 | 27 | /* minprintf: minimal printf with variable argument list */ 28 | int minprintf(char *fmt, ...) 29 | { 30 | va_list ap; /* points to each unnamed arg in turn */ 31 | char *p, *sval, number[MAXNUMB]; 32 | int ival, i, width, precision, nchar = 0; 33 | double dval; 34 | unsigned uval; 35 | void *pval; 36 | int leftAdjust, *pival; 37 | 38 | 39 | va_start(ap, fmt); /* make ap point to 1st unnamed arg */ 40 | 41 | for (p = fmt; *p; p++) { 42 | 43 | if (*p != '%') { 44 | putchar(*p); 45 | ++nchar; 46 | continue; 47 | } 48 | 49 | leftAdjust = 0; 50 | width = 1; 51 | precision = -1; 52 | 53 | if (*++p == '-') { 54 | leftAdjust = 1; /* left adjust */ 55 | ++p; 56 | } 57 | 58 | if (isdigit(*p)) { /* get width */ 59 | for (i = 0; isdigit(*p) && i < MAXNUMB; i++) 60 | number[i] = *p++; 61 | number[i] = '\0'; 62 | width = atoi(number); 63 | } 64 | if (*p == '.') { 65 | ++p; 66 | for (i = 0; isdigit(*p) && i < MAXNUMB; i++) /* get precision pts */ 67 | number[i] = *p++; 68 | number[i] = '\0'; 69 | precision = atoi(number); 70 | } 71 | 72 | switch (*p) { 73 | case 'd': 74 | ival = va_arg(ap, int); 75 | PRINT_N(leftAdjust, d, width, ival, nchar); 76 | break; 77 | case 'i': 78 | ival = va_arg(ap, int); 79 | PRINT_N(leftAdjust, i, width, ival, nchar); 80 | break; 81 | case 'o': 82 | uval = va_arg(ap, int); 83 | PRINT_N(leftAdjust, o, width, uval, nchar); 84 | break; 85 | case 'x': 86 | uval = va_arg(ap, int); 87 | PRINT_N(leftAdjust, x, width, uval, nchar); 88 | break; 89 | case 'X': 90 | uval = va_arg(ap, int); 91 | PRINT_N(leftAdjust, X, width, uval, nchar); 92 | break; 93 | case 'u': 94 | uval = va_arg(ap, int); 95 | PRINT_N(leftAdjust, u, width, uval, nchar); 96 | break; 97 | case 'c': 98 | ival = va_arg(ap, int); 99 | PRINT_N(leftAdjust, c, width, ival, nchar); 100 | break; 101 | case 's': 102 | sval = va_arg(ap, char *); 103 | if (precision < 0) 104 | precision = strlen(sval); 105 | PRINT_D(leftAdjust, s, width, precision, sval, nchar); 106 | break; 107 | case 'f': 108 | dval = va_arg(ap, double); 109 | if (precision < 0) 110 | precision = FLT_PRCISION; 111 | PRINT_D(leftAdjust, f, width, precision, dval, nchar); 112 | break; 113 | case 'e': 114 | dval = va_arg(ap, double); 115 | if (precision < 0) 116 | precision = FLT_PRCISION; 117 | PRINT_D(leftAdjust, e, width, precision, dval, nchar); 118 | break; 119 | case 'E': 120 | dval = va_arg(ap, double); 121 | if (precision < 0) 122 | precision = FLT_PRCISION; 123 | PRINT_D(leftAdjust, E, width, precision, dval, nchar); 124 | break; 125 | case 'g': 126 | dval = va_arg(ap, double); 127 | PRINT_N(leftAdjust, g, width, dval, nchar); 128 | break; 129 | case 'G': 130 | dval = va_arg(ap, double); 131 | PRINT_N(leftAdjust, G, width, dval, nchar); 132 | break; 133 | case 'p': 134 | pval = va_arg(ap, void *); 135 | PRINT_N(leftAdjust, p, width, pval, nchar); 136 | break; 137 | case 'n': 138 | pival = va_arg(ap, int *); 139 | *pival = nchar; 140 | break; 141 | default: 142 | putchar(*p); 143 | ++nchar; 144 | break; 145 | } 146 | } 147 | va_end(ap); /* clean up when done */ 148 | return nchar; 149 | } 150 | 151 | int main(void) 152 | { 153 | minprintf("minprintf() output:\n"); 154 | minprintf("%s\n", "hello, world"); 155 | minprintf("%10s\n", "hello, world"); 156 | minprintf("%.10s\n", "hello, world"); 157 | minprintf("%-10s\n", "hello, world"); 158 | minprintf("%.15s\n", "hello, world"); 159 | minprintf("%-15s\n", "hello, world"); 160 | minprintf("%15.10s\n", "hello, world"); 161 | minprintf("%-15.10s\n", "hello, world"); 162 | 163 | printf("\n"); 164 | 165 | printf("printf() output:\n"); 166 | printf("%s\n", "hello, world"); 167 | printf("%10s\n", "hello, world"); 168 | printf("%.10s\n", "hello, world"); 169 | printf("%-10s\n", "hello, world"); 170 | printf("%.15s\n", "hello, world"); 171 | printf("%-15s\n", "hello, world"); 172 | printf("%15.10s\n", "hello, world"); 173 | printf("%-15.10s\n", "hello, world"); 174 | return 0; 175 | } 176 | -------------------------------------------------------------------------------- /chapter07/7-4.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 7-4. Write a private version of scanf analogous to minprintf from 3 | * the previous section. 4 | * By Faisal Saadatmand 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | int minscanf(char *, ...); 12 | 13 | int minscanf(char *format, ...) 14 | { 15 | va_list ap; 16 | char *p, *sval, *cval; 17 | int *ival; 18 | float *fval; 19 | unsigned *uval; 20 | int nchar = 0; 21 | 22 | va_start(ap, format); /* make ap point to 1st unnamed arg */ 23 | for (p = format; *p; p++) { 24 | if (*p != '%') 25 | continue; 26 | 27 | switch (*++p) { 28 | case 'd': 29 | ival = va_arg(ap, int *); 30 | nchar += scanf("%d", ival); 31 | break; 32 | case 'i': 33 | ival = va_arg(ap, int *); 34 | nchar += scanf("%i", ival); 35 | break; 36 | case 'o': 37 | uval = va_arg(ap, unsigned *); 38 | nchar += scanf("%u", uval); 39 | break; 40 | case 'c': 41 | cval = va_arg(ap, char *); 42 | nchar += scanf("%c", cval); 43 | break; 44 | case 's': 45 | sval = va_arg(ap, char *); 46 | nchar += scanf("%s", sval); 47 | break; 48 | case 'e': 49 | fval = va_arg(ap, float *); 50 | nchar += scanf("%e", fval); 51 | break; 52 | case 'f': 53 | fval = va_arg(ap, float *); 54 | nchar += scanf("%f", fval); 55 | break; 56 | case 'g': 57 | fval = va_arg(ap, float *); 58 | nchar += scanf("%g", fval); 59 | break; 60 | default: 61 | break; 62 | } 63 | va_end(ap); 64 | } 65 | return nchar; 66 | } 67 | 68 | int main(void) { 69 | int x, y, z; 70 | int nchar; 71 | 72 | printf("Enter 3 integers: "); 73 | nchar = minscanf("%i %i %i", &x, &y, &z); 74 | printf("%i %i %i\n", x, y, z); 75 | printf("Number of elements: %i\n", nchar); 76 | 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /chapter07/7-5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 7-5. Rewrite the postfix calculator of Chapter 4 to use scanf 3 | * and/or sscanf to do the input and number conversion. 4 | * 5 | * Note: use ungetc instead of K&R's ungetch to pushback a character onto 6 | * the stdin for scanf to pick up. 7 | * 8 | * Faisal Saadatmand 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #define MAXOP 100 /* max size of operand or operator */ 15 | #define NUMBER '0' /* signal that a number was found */ 16 | #define MAXVAL 100 /* maximum depth of val stack */ 17 | #define BUFSIZE 100 18 | 19 | /* functions */ 20 | void push(double); 21 | double pop(void); 22 | int getop(double *); 23 | 24 | /* globals */ 25 | int sp = 0; /* next free stack position */ 26 | double val[MAXVAL]; /* value stack */ 27 | 28 | /* push: push f onto value stack */ 29 | void push(double f) 30 | { 31 | if (sp < MAXVAL) 32 | val[sp++] = f; 33 | else 34 | printf("error: stack full, can't push %g\n", f); 35 | } 36 | 37 | /* pop: pop and return top value from stack */ 38 | double pop(void) 39 | { 40 | if (sp > 0) 41 | return val[--sp]; 42 | else { 43 | printf("error: stack empty\n"); 44 | return 0.0; 45 | } 46 | } 47 | 48 | /* getop: get next operator or numeric operand - scanf version */ 49 | int getop(double *number) 50 | { 51 | char c; 52 | int match; 53 | 54 | if (scanf("%c", &c) < 0) 55 | return EOF; 56 | if (c == '\n') 57 | return c; 58 | ungetc(c, stdin); 59 | match = scanf("%lf ", number); /* scan number, consumed trailing whitespace */ 60 | if (!match) { /* not a number */ 61 | if (c == '+' || c == '-') /* check for consumed leading op on failed match */ 62 | ungetc(c, stdin); 63 | scanf("%c", &c); /* scan op */ 64 | return c; 65 | } 66 | return NUMBER; 67 | } 68 | 69 | /* reverse polish calculator - scanf/sscanf version */ 70 | int main(void) 71 | { 72 | int type; 73 | double op2, op; 74 | 75 | while ((type = getop(&op)) != EOF) { 76 | switch (type) { 77 | case (NUMBER): 78 | push(op); 79 | break; 80 | case '+': 81 | push(pop() + pop()); 82 | break; 83 | case '*': 84 | push(pop() * pop()); 85 | break; 86 | case '-': 87 | op2 = pop(); 88 | push(pop() - op2); 89 | break; 90 | case '/': 91 | op2 = pop(); 92 | if (op2 != 0.0) 93 | push(pop() / op2); 94 | else 95 | printf("error: zero division\n"); 96 | break; 97 | case '\n': 98 | printf("\t%.8g\n", pop()); 99 | break; 100 | default: 101 | printf("error: unknown command %c\n", type); 102 | break; 103 | } 104 | } 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /chapter07/7-5a.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 7-5. Rewrite the postfix calculator of Chapter 4 to use scanf 3 | * and/or sscanf to do the input and number conversion. 4 | * 5 | * Note: scanf and sscanf version 6 | * 7 | * Faisal Saadatmand 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #define MAXOP 100 /* max size of operand or operator */ 15 | #define NUMBER '0' /* signal that a number was found */ 16 | #define MAXVAL 100 /* maximum depth of val stack */ 17 | #define BUFSIZE 100 18 | 19 | /* functions */ 20 | void push(double); 21 | double pop(void); 22 | int getop(double *); 23 | void ungets(char *); 24 | 25 | /* globals */ 26 | int sp = 0; /* next free stack position */ 27 | double val[MAXVAL]; /* value stack */ 28 | 29 | /* push: push f onto value stack */ 30 | void push(double f) 31 | { 32 | if (sp < MAXVAL) 33 | val[sp++] = f; 34 | else 35 | printf("error: stack full, can't push %g\n", f); 36 | } 37 | 38 | /* pop: pop and return top value from stack */ 39 | double pop(void) 40 | { 41 | if (sp > 0) 42 | return val[--sp]; 43 | else { 44 | printf("error: stack empty\n"); 45 | return 0.0; 46 | } 47 | } 48 | 49 | /* getop: get next operator or numeric operand - scanf and sscanf version */ 50 | int getop(double *number) 51 | { 52 | char c; 53 | int n; /* number of consumed characters */ 54 | int match; 55 | char s[MAXOP]; 56 | 57 | if (scanf("%c", &c) < 0) 58 | return EOF; 59 | 60 | if (c == '\n') 61 | return c; 62 | 63 | ungetc(c, stdin); 64 | scanf("%s", s); /* parse string from input stream */ 65 | match = sscanf(s, "%lf %n", number, &n); /* scan number, consume trailing witespace */ 66 | if (!match) { /* not a number */ 67 | sscanf(s, "%c", &c); 68 | if (strlen(s) > 1) /* check for unread characters */ 69 | ungets(s + 1); 70 | return c; 71 | } 72 | 73 | if (n != (int) strlen(s)) /* check unread char from match */ 74 | ungets(s + n); 75 | 76 | return NUMBER; 77 | } 78 | 79 | /* ungets: push back s on input */ 80 | void ungets(char *s) 81 | { 82 | int len; 83 | 84 | for (len = strlen(s); len >= 0; --len) 85 | ungetc(s[len], stdin); 86 | } 87 | 88 | /* reverse polish calculator - scanf/sscanf version */ 89 | int main(void) 90 | { 91 | int type; 92 | double op2, op; 93 | 94 | while ((type = getop(&op)) != EOF) { 95 | switch (type) { 96 | case (NUMBER): 97 | push(op); 98 | break; 99 | case '+': 100 | push(pop() + pop()); 101 | break; 102 | case '*': 103 | push(pop() * pop()); 104 | break; 105 | case '-': 106 | op2 = pop(); 107 | push(pop() - op2); 108 | break; 109 | case '/': 110 | op2 = pop(); 111 | if (op2 != 0.0) 112 | push(pop() / op2); 113 | else 114 | printf("error: zero division\n"); 115 | break; 116 | case '\n': 117 | printf("\t%.8g\n", pop()); 118 | break; 119 | default: 120 | printf("error: unknown command %c\n", type); 121 | break; 122 | } 123 | } 124 | return 0; 125 | } 126 | -------------------------------------------------------------------------------- /chapter07/7-6.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 7-6. Write a program to compare two file, printing the first line 3 | * where they differ. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #define MAXLEN 1000 /* maximum line length */ 9 | #define MAXNAME 100 /* maximum length of file name */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | /* types */ 16 | typedef struct { 17 | char name[MAXNAME]; /* file name */ 18 | FILE *fptr; /* file pointer */ 19 | char line[MAXLEN]; /* currently read line */ 20 | int lineno; /* number of read lines */ 21 | } File; 22 | 23 | /* globals */ 24 | char *prog; /* program name */ 25 | 26 | /* functions */ 27 | void openfile(const char *, File *); 28 | void comparefiles(File *, File *); 29 | 30 | void openfile(const char *filename, File *file) 31 | { 32 | if (!(file->fptr = fopen(filename, "r"))) { 33 | fprintf(stderr, "%s: ca't open %s\n", prog, filename); 34 | exit(EXIT_FAILURE); 35 | } 36 | strcpy(file->name, filename); 37 | *file->line = '\0'; 38 | file->lineno = 0; 39 | } 40 | 41 | void comparefiles(File *f1, File *f2) 42 | { 43 | do { 44 | if (fgets(f1->line, MAXLEN, f1->fptr)) 45 | ++f1->lineno; 46 | if (fgets(f2->line, MAXLEN, f2->fptr)) 47 | ++f2->lineno; 48 | if (!feof(f1->fptr) && !feof(f2->fptr)) /* are there lines? */ 49 | if (strcmp(f1->line, f2->line)) /* compare lines */ 50 | break; /* lines differ */ 51 | } while (!feof(f1->fptr) && !feof(f2->fptr)); /* until either return EOF */ 52 | if (!feof(f1->fptr)) 53 | fprintf(stdout, "\n%s:%i: %s", f1->name, f1->lineno, f1->line); 54 | if (!feof(f2->fptr)) 55 | fprintf(stdout, "\n%s:%i: %s", f2->name, f2->lineno, f2->line); 56 | } 57 | 58 | int main(int argc, char *argv[]) 59 | { 60 | File file_1, file_2; 61 | 62 | prog = *argv++; /* program name for errors */ 63 | if (--argc != 2) { /* check cli argument */ 64 | fprintf(stderr, "Usage: %s \n", prog); 65 | exit(EXIT_FAILURE); 66 | } 67 | openfile(*argv++, &file_1); 68 | openfile(*argv, &file_2); 69 | comparefiles(&file_1, &file_2); 70 | fclose(file_1.fptr); 71 | fclose(file_2.fptr); 72 | exit(EXIT_SUCCESS); 73 | } 74 | -------------------------------------------------------------------------------- /chapter07/7-7.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 7-7. Modify the pattern finding program of Chapter 5 to take its 3 | * input from a set named files or, if no files are named as arguments, from 4 | * the standard input. Should the file name be printed when a matching line is 5 | * found? 6 | * 7 | * By Faisal Saadatmand 8 | */ 9 | 10 | /* Answer: only when files are named as arguments */ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #define MAXLEN 1000 17 | 18 | /* globals */ 19 | char *prog; /* program name */ 20 | 21 | /* functions */ 22 | int getLine(char *, size_t, FILE *); 23 | FILE* loadFile(char *); 24 | int findPattern(FILE *, const char *, const char *, const int, const int); 25 | 26 | /* getLine: read line, return length - file pointer version */ 27 | int getLine(char *line, size_t max, FILE *fp) 28 | { 29 | if (fgets(line, max, fp) == NULL) 30 | return 0; 31 | return strlen(line); 32 | } 33 | 34 | FILE* loadFile(char *fileName) 35 | { 36 | FILE* fp; 37 | if (!(fp = fopen(fileName, "r"))) { 38 | fprintf(stderr, "%s: can't open %s\n", prog, fileName); 39 | exit(EXIT_FAILURE); 40 | } 41 | return fp; 42 | } 43 | 44 | int findPattern(FILE *fp, const char *fileName, const char *pattern, 45 | const int except, const int number) 46 | { 47 | char line[MAXLEN]; 48 | long lineno; 49 | int found; 50 | 51 | lineno = found = 0; 52 | while (getLine(line, MAXLEN, fp) > 0) { 53 | ++lineno; 54 | if ((strstr(line, pattern) != NULL) != except) { 55 | if (fileName) 56 | fprintf(stdout, "%s:", fileName); 57 | if (number) 58 | fprintf (stdout, "%ld:", lineno); 59 | fprintf(stdout, "%s", line); 60 | ++found; 61 | } 62 | } 63 | return found; 64 | } 65 | 66 | /* find: print lines that match pattern from 1s arg */ 67 | int main(int argc, char *argv[]) 68 | { 69 | int c, except, number, found; 70 | char *pattern; 71 | FILE *file; 72 | 73 | prog = argv[0]; 74 | except = number = found = 0; 75 | while (--argc > 0 && (*++argv)[0] == '-') /* check for flags */ 76 | while ((c = *++argv[0])) 77 | switch (c) { 78 | case 'x': 79 | except = 1; 80 | break; 81 | case 'n': 82 | number = 1; 83 | break; 84 | default: 85 | fprintf(stderr, "%s: illegal option %c\n", prog, c); 86 | argc = 0; 87 | found = -1; 88 | break; 89 | } 90 | pattern = *argv++; /* save a pointer to the pattern */ 91 | if (argc < 1) 92 | fprintf(stderr, "Usage: %s [-xn] PATTERN [FILE...]\n", prog); 93 | else if (argc == 1) /* input from stdin */ 94 | found += findPattern(stdin, NULL, pattern, except, number); 95 | else /* input from file or set of files */ 96 | while (argc-- > 1) { 97 | file = loadFile(*argv++); 98 | found += findPattern(file, *argv, pattern, except, number); 99 | fclose(file); 100 | } 101 | return found; 102 | } 103 | -------------------------------------------------------------------------------- /chapter07/7-8.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 7-8. Write a program to print a set of files, starting each new one 3 | * on a new page, with a title and a running page count for each file. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #define MAXHEADER 6 /* maximum header size */ 12 | #define MAXFOOTER 3 /* maximum footer size */ 13 | #define MAXLINE 80 /* maximum characters per line */ 14 | #define MAXPAGE 60 /* maximum lines per page */ 15 | 16 | /* globals */ 17 | const char *progName; 18 | 19 | /* functions */ 20 | FILE* loadFile(char *); 21 | int printHeader(char *, int); 22 | void printFile(FILE *, char *); 23 | 24 | FILE* loadFile(char *fileName) 25 | { 26 | FILE *fp; 27 | if (!(fp = fopen(fileName, "r"))) { 28 | fprintf(stderr, "%s: can't open %s\n", progName, fileName); 29 | exit(EXIT_FAILURE); 30 | } 31 | return fp; 32 | } 33 | 34 | int printHeader(char *fileName, int pageNo) 35 | { 36 | int ln = 5; /* length of the lines bellow */ 37 | 38 | printf("\n************************\n"); 39 | printf("File name: %s\n", fileName); 40 | printf("Page: %i\n", pageNo); 41 | printf("************************\n"); 42 | while (ln++ < MAXHEADER) 43 | fprintf(stdout, "\n"); 44 | return ln; 45 | } 46 | 47 | void printFile(FILE *file, char *fileName) 48 | { 49 | char line[MAXLINE]; 50 | int lineNo,pageNo; 51 | 52 | lineNo = pageNo = 1; 53 | while (fgets(line, MAXLINE, file)) { 54 | if (lineNo == 1) { 55 | fprintf(stdout, "\f"); 56 | lineNo = printHeader(fileName, pageNo++); 57 | } 58 | fputs(line, stdout); 59 | if (++lineNo > MAXPAGE - MAXFOOTER) 60 | lineNo = 1; 61 | } 62 | fprintf(stdout, "\f"); 63 | } 64 | 65 | int main(int argc, char *argv[]) 66 | { 67 | FILE *fp; 68 | 69 | progName = *argv; 70 | if (argc == 1) /* standard input */ 71 | printFile(stdin, "standard input"); 72 | else 73 | while (--argc > 0) { 74 | fp = loadFile(*++argv); 75 | printFile(fp, *argv); 76 | fclose(fp); 77 | } 78 | exit(EXIT_SUCCESS); 79 | } 80 | -------------------------------------------------------------------------------- /chapter07/7-9.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 7-9. Functions like isupper can be implemented to save space or to 3 | * save time. Explore both possibilities. 4 | * 5 | * Answer: I take it that by saving space the authors mean increasing code 6 | * compactness and by saving time reducing run time. Implementing isupper() as 7 | * a macro avoids the overhead associated with calling a function on to the 8 | * stack memory; thus, we save time. I suppose it could be argued that this 9 | * saves stack memory as well, but I do not think that is what the authors 10 | * meant. To save space, we could implement a macro such as IFUPPER that wraps 11 | * an "if ..." statement around the isupper() function. The result is a 12 | * one-line code that performs multiple instructions. Moreover, if we avoid 13 | * using other functions in the wrapping and use nested macros instead, we 14 | * could save both time and space. The draw back, of course, is that the code, 15 | * though more readable, it will be full of unconventional idioms. 16 | * 17 | * By Faisal Saadatmand 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | #define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z') ? 1 : 0 /* saves time */ 24 | #define TOLOWER(c) ((c)) + 'a' - 'A' /* saves time */ 25 | #define IFUPPER_TOLOWER(c) if (isupper((c))) (c) = tolower((c)) /* saves space */ 26 | #define IF_UPPER_TOLOWER(c) if (ISUPPER((c))) (c) = TOLOWER((c)) /* saves time and space */ 27 | #define IF_UPPER(c,a) (ISUPPER((c))) ? ((a)) : ((c)) /* saves time and space */ 28 | 29 | int main(void) 30 | { 31 | int c; 32 | 33 | printf("Enter an upper case character: " ); 34 | c = getchar(); 35 | 36 | if (ISUPPER(c)) 37 | c = tolower(c); 38 | printf("%c\n", c); 39 | 40 | IFUPPER_TOLOWER(c); 41 | printf("%c\n", c); 42 | 43 | IF_UPPER_TOLOWER(c); 44 | printf("%c\n", c); 45 | 46 | printf("%c", IF_UPPER(c, TOLOWER(c))); 47 | printf("\n"); 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /chapter08/8-1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 8-1. Rewrite the program cat from Chapter 7 using read, write, open 3 | * and close instead of their standard library equivalents. Perform experiments 4 | * to determine the relative speeds of the two version. 5 | * 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | /* Note: this version of cat is faster */ 10 | 11 | #include /* for read and write */ 12 | #include /* for open and close */ 13 | #include /* also needed for BUFSIZ */ 14 | #include 15 | #include 16 | 17 | #define IN 0 /* standard input file */ 18 | #define OUT 1 /* standard output file */ 19 | #define ERROR 2 /* standard error file */ 20 | 21 | /* functions */ 22 | void filecopy(int, int); 23 | void error(char *, ...); 24 | 25 | void filecopy(int in, int out) 26 | { 27 | char buf[BUFSIZ]; 28 | int n; 29 | 30 | while ((n = read(in, buf, BUFSIZ)) > 0) 31 | write(out, buf, n); 32 | } 33 | 34 | /* error: print an error message and die */ 35 | void error(char *fmt, ...) 36 | { 37 | va_list args; 38 | 39 | va_start(args, fmt); 40 | fprintf(stderr, "error: "); 41 | vfprintf(stderr, fmt, args); 42 | fprintf(stderr, "\n"); 43 | va_end(args); 44 | exit(EXIT_FAILURE); 45 | } 46 | 47 | int main(int argc, char *argv[]) 48 | { 49 | int fd; /* file descriptor */ 50 | char *prog = argv[0]; /* program name */ 51 | 52 | if (argc == 1) 53 | filecopy(IN, OUT); 54 | else 55 | while (--argc > 0) 56 | if ((fd = open(argv[1], O_RDONLY, 0)) == -1) 57 | error("%s: can't open %s", prog, argv[1]); 58 | else { 59 | filecopy(fd, OUT); 60 | close(fd); 61 | } 62 | exit(EXIT_SUCCESS); 63 | } 64 | -------------------------------------------------------------------------------- /chapter08/8-2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 8-2. Rewrite fopen and _fillbuf with fields instead of explicit bit 3 | * operations. Compare code size and execution speed. 4 | * 5 | * By Faisal Saadatmand 6 | */ 7 | 8 | #define EOF (-1) 9 | #define BUFSIZ 1024 10 | #define OPEN_MAX 20 /* max #files open at once */ 11 | 12 | typedef struct { 13 | unsigned int _READ : 1; /* file open for reading */ 14 | unsigned int _WRITE : 1; /* file open for writing */ 15 | unsigned int _UNBUF : 1; /* file is unbuffered */ 16 | unsigned int _EOF : 1; /* EOF has occurred on this file */ 17 | unsigned int _ERR : 1; /* error occurred on this file */ 18 | } field; 19 | 20 | typedef struct _iobuf { 21 | int cnt; /* characters left */ 22 | char *ptr; /* next character position */ 23 | char *base; /* location of buffer */ 24 | field flag; /* mode of file access */ 25 | int fd; /* file descriptor */ 26 | } FILE; 27 | 28 | extern FILE _iob[OPEN_MAX]; 29 | 30 | #define stdin (&_iob[0]) 31 | #define stdout (&_iob[1]) 32 | #define stderr (&_iob[2]) 33 | 34 | /* functions */ 35 | FILE *_fopen(char *, char *); 36 | int _fillbuf(FILE *); 37 | int _flushbuf(int, FILE *); 38 | 39 | #define feof(p) (((p)->flag & _EOF) != 0) 40 | #define ferror(p) (((p)->flag & _ERR) != 0) 41 | #define fileno(p) ((p)->fd) 42 | 43 | #define getc(p) (--(p)->cnt >= 0 \ 44 | ? (unsigned char) *(p)->ptr++ : _fillbuf(p)) 45 | #define putc(x,p) (--(p)->cnt >= 0 \ 46 | ? *(p)->ptr++ = (x) : _flushbuf((x),p)) 47 | 48 | #define getchar() getc(stdin) 49 | #define putchar(x) putc((x), stdout) 50 | 51 | #include 52 | #include 53 | #include /* for malloc */ 54 | 55 | #define PERMS 0666 /* RW for owner, group, others */ 56 | 57 | /* fopen: open file, return ptr */ 58 | FILE *_fopen(char *name, char *mode) 59 | { 60 | int fd; 61 | FILE *fp; 62 | 63 | if (*mode != 'r' && *mode != 'w' && *mode != 'a') 64 | return NULL; 65 | for (fp = _iob; fp < _iob + OPEN_MAX; fp++) 66 | if ((fp->flag._READ == 0 && fp->flag._WRITE == 0)) 67 | break; /* found free slot */ 68 | if ( fp >= _iob + OPEN_MAX) /* no free slots */ 69 | return NULL; 70 | 71 | if (*mode == 'w') 72 | fd = creat(name, PERMS); 73 | else if (*mode == 'a') { 74 | if ((fd = open(name, O_WRONLY, 0)) == -1) 75 | fd = creat(name, PERMS); 76 | lseek(fd, 0L, 2); 77 | } else 78 | fd = open(name, O_RDONLY, 0); 79 | if (fd == -1) 80 | return NULL; /* couldn't access name */ 81 | fp->fd = fd; 82 | fp->cnt = 0; 83 | fp->base = NULL; 84 | (*mode == 'r') ? (fp->flag._READ = 1) : (fp->flag._WRITE = 1); 85 | return fp; 86 | } 87 | 88 | /* _fillbuf: allocate and fill input buffer */ 89 | int _fillbuf(FILE *fp) 90 | { 91 | int bufsize; 92 | 93 | if (fp->flag._READ == 0) 94 | return EOF; 95 | bufsize = (fp->flag._UNBUF == 1) ? 1 : BUFSIZ; 96 | if (fp->base == NULL) /* no buffer yet */ 97 | if ((fp->base = (char *) malloc(bufsize)) == NULL) 98 | return EOF; /* can't get buffer */ 99 | fp->ptr = fp->base; 100 | fp->cnt = read(fp->fd, fp->ptr, bufsize); 101 | if (--fp->cnt < 0) { 102 | if (fp->cnt == -1) 103 | fp->flag._EOF = 1; 104 | else 105 | fp->flag._ERR = 1; 106 | fp->cnt = 0; 107 | return EOF; 108 | } 109 | return (unsigned char) *fp->ptr++; 110 | } 111 | 112 | /* _flushbuf: allocate and flush output buffer */ 113 | int _flushbuf(int x, FILE *fp) 114 | { 115 | int bufsize; 116 | 117 | if (fp->flag._WRITE == 0) 118 | return EOF; 119 | bufsize = (fp->flag._UNBUF == 1) ? BUFSIZ : 1; 120 | if (fp->base == NULL) { /* no buffer yet */ 121 | if ((fp->base = (char *) malloc(bufsize)) == NULL) 122 | return EOF; /* can't get buffer */ 123 | *fp->base = '\0'; /* initialize string */ 124 | } 125 | fp->ptr = fp->base; 126 | if (bufsize == 1) /* unbuffered output */ 127 | *fp->ptr = x; 128 | if (*fp->ptr != '\0' || bufsize == 1) { 129 | fp->cnt = write(fp->fd, fp->ptr, bufsize); 130 | if (--fp->cnt != bufsize) { 131 | fp->flag._ERR = 1; 132 | fp->cnt = 0; 133 | return EOF; 134 | } 135 | } 136 | if (bufsize > 1) { /* buffered output */ 137 | *fp->ptr = x; 138 | fp->cnt = BUFSIZ - 1; /* set character left counter */ 139 | } 140 | return (unsigned char) *fp->ptr++; 141 | } 142 | 143 | FILE _iob[OPEN_MAX] = { /* stdin, stdout, stderr */ 144 | { 0, (char *) 0, (char *) 0, {1, 0, 0 , 0, 0}, 0 }, 145 | { 0, (char *) 0, (char *) 0, {0, 1, 0, 0, 0}, 1 }, 146 | { 0, (char *) 0, (char *) 0, { 0, 1, 1, 0, 0} , 2 } 147 | }; 148 | 149 | int main(int argc, char *argv[]) 150 | { 151 | FILE *fp; 152 | int c; 153 | 154 | if (argc == 1) { 155 | while ((c = getchar()) != EOF) 156 | putchar(c); 157 | } else 158 | while (--argc > 0) 159 | if ((fp = _fopen(*++argv, "r")) == NULL) 160 | exit(EXIT_FAILURE); 161 | else 162 | while ((c = getc(fp)) != EOF) 163 | putchar(c); 164 | exit(0); 165 | } 166 | -------------------------------------------------------------------------------- /chapter08/8-3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 8-3. Design and write _flushbuf, fflush and fclose. 3 | * By Faisal Saadatmand 4 | */ 5 | 6 | #define EOF (-1) 7 | #define BUFSIZ 1024 8 | #define OPEN_MAX 20 /* max #files open at once */ 9 | 10 | typedef struct _iobuf { 11 | int cnt; /* characters left */ 12 | char *ptr; /* next character position */ 13 | char *base; /* location of buffer */ 14 | int flag; /* mode of file access */ 15 | int fd; /* file descriptor */ 16 | } FILE; 17 | 18 | extern FILE _iob[OPEN_MAX]; 19 | 20 | #define stdin (&_iob[0]) 21 | #define stdout (&_iob[1]) 22 | #define stderr (&_iob[2]) 23 | 24 | enum _flags { 25 | _READ = 01, /* file open for reading */ 26 | _WRITE = 02, /* file open for writing */ 27 | _UNBUF = 04, /* file is unbuffered */ 28 | _EOF = 010, /* EOF has occurred on this file */ 29 | _ERR = 020, /* error occurred on this file */ 30 | }; 31 | 32 | /* functions */ 33 | int _fillbuf(FILE *); 34 | int _flushbuf(int, FILE *); 35 | int fflush(FILE *); 36 | int fclose (FILE *); 37 | 38 | #define feof(p) (((p)->flag & _EOF) != 0) 39 | #define ferror(p) (((p)->flag & _ERR) != 0) 40 | #define fileno(p) ((p)->fd) 41 | 42 | #define getc(p) (--(p)->cnt >= 0 \ 43 | ? (unsigned char) *(p)->ptr++ : _fillbuf(p)) 44 | #define putc(x,p) (--(p)->cnt >= 0 \ 45 | ? *(p)->ptr++ = (x) : _flushbuf((x),p)) 46 | 47 | #define getchar() getc(stdin) 48 | #define putchar(x) putc((x), stdout) 49 | 50 | #include 51 | #include 52 | #include /* for malloc */ 53 | 54 | #define PERMS 0666 /* RW for owner, group, others */ 55 | 56 | /* _fopen: open file, return ptr */ 57 | FILE *_fopen(char *name, char *mode) 58 | { 59 | int fd; 60 | FILE *fp; 61 | 62 | if (*mode != 'r' && *mode != 'w' && *mode != 'a') 63 | return NULL; 64 | for (fp = _iob; fp < _iob + OPEN_MAX; fp++) 65 | if ((fp->flag & (_READ | _WRITE)) == 0) 66 | break; /* found free slot */ 67 | if ( fp >= _iob + OPEN_MAX) /* no free slots */ 68 | return NULL; 69 | 70 | if (*mode == 'w') 71 | fd = creat(name, PERMS); 72 | else if (*mode == 'a') { 73 | if ((fd = open(name, O_WRONLY, 0)) == -1) 74 | fd = creat(name, PERMS); 75 | lseek(fd, 0L, 2); 76 | } else 77 | fd = open(name, O_RDONLY, 0); 78 | if (fd == -1) 79 | return NULL; /* couldn't access name */ 80 | fp->fd = fd; 81 | fp->cnt = 0; 82 | fp->base = NULL; 83 | fp->flag = (*mode == 'r') ? _READ : _WRITE; 84 | return fp; 85 | } 86 | 87 | /* _fillbuf: allocate and fill input buffer */ 88 | int _fillbuf(FILE *fp) 89 | { 90 | int bufsize; 91 | 92 | if ((fp->flag & (_READ | _EOF | _ERR)) != _READ) 93 | return EOF; 94 | bufsize = (fp->flag & _UNBUF) ? 1 : BUFSIZ; 95 | if (fp->base == NULL) /* no buffer yet */ 96 | if ((fp->base = (char *) malloc(bufsize)) == NULL) 97 | return EOF; /* can't get buffer */ 98 | fp->ptr = fp->base; 99 | fp->cnt = read(fp->fd, fp->ptr, bufsize); 100 | if (--fp->cnt < 0) { 101 | if (fp->cnt == -1) 102 | fp->flag |= _EOF; 103 | else 104 | fp->flag |= _ERR; 105 | fp->cnt = 0; 106 | return EOF; 107 | } 108 | return (unsigned char) *fp->ptr++; 109 | } 110 | 111 | /* _flushbuf: allocate and flush output buffer */ 112 | int _flushbuf(int x, FILE *fp) 113 | { 114 | int bufsize; 115 | 116 | if ((fp->flag & (_WRITE | _EOF | _ERR)) != _WRITE) 117 | return EOF; 118 | bufsize = (fp->flag & _UNBUF) ? 1 : BUFSIZ; 119 | if (fp->base == NULL) { /* no buffer yet */ 120 | if ((fp->base = (char *) malloc(bufsize)) == NULL) 121 | return EOF; /* can't get buffer */ 122 | *fp->base = '\0'; /* initialize string */ 123 | } 124 | fp->ptr = fp->base; 125 | if (bufsize == 1) /* unbuffered output */ 126 | *fp->ptr = x; 127 | if (*fp->ptr != '\0' || bufsize == 1) { 128 | fp->cnt = write(fp->fd, fp->ptr, bufsize); 129 | if (fp->cnt != bufsize) { 130 | fp->flag |= _ERR; 131 | fp->cnt = 0; 132 | return EOF; 133 | } 134 | } 135 | if (bufsize > 1) { /* buffered output */ 136 | *fp->ptr = x; 137 | fp->cnt = BUFSIZ - 1; /* reset counter */ 138 | } 139 | return (unsigned char) *fp->ptr++ ; 140 | } 141 | 142 | /* fflush: on output stream, write unwritten buffered data. On input stream, 143 | * the effect is undefined. NULL flushes all output streams. */ 144 | int fflush(FILE *fp) 145 | { 146 | int bufsize; 147 | FILE *cond; /* loop condition */ 148 | 149 | if (fp == NULL) { /* flush all output stream */ 150 | fp = _iob; 151 | cond = _iob + OPEN_MAX; 152 | } else /* flush fp's buffer */ 153 | cond = fp + 1; 154 | 155 | for ( ; fp < cond; fp++) { 156 | if ((fp->flag & (_WRITE | _EOF | _ERR)) != _WRITE) 157 | return EOF; 158 | bufsize = BUFSIZ - fp->cnt; 159 | fp->ptr = fp->base; 160 | fp->cnt = write(fp->fd, fp->ptr, bufsize); 161 | if (fp->cnt != bufsize) { 162 | fp->flag |= _ERR; 163 | fp->cnt = 0; 164 | return EOF; 165 | } 166 | *fp->ptr = '\0'; 167 | fp->cnt = 0; 168 | } 169 | return 0; 170 | } 171 | 172 | /* fclose: flushes unwritten date from stream, discard unread buffered input, 173 | * frees allocated memory, and closes stream. */ 174 | int fclose (FILE *fp) 175 | { 176 | if ((fp->flag & (_WRITE | _EOF | _ERR)) == _WRITE) 177 | if (fflush(fp) < 0) 178 | return EOF; 179 | free(fp->base); 180 | if (close(fp->fd) < 0) { 181 | fp->flag |= _ERR; 182 | return EOF; 183 | } 184 | return 0; 185 | } 186 | 187 | FILE _iob[OPEN_MAX] = { /* stdin, stdout, stderr */ 188 | { 0, (char *) 0, (char *) 0, _READ, 0 }, 189 | { 0, (char *) 0, (char *) 0, _WRITE, 1 }, 190 | { 0, (char *) 0, (char *) 0, _WRITE | _UNBUF, 2 } 191 | }; 192 | 193 | int main(int argc, char *argv[]) 194 | { 195 | FILE *fp; 196 | int c; 197 | 198 | if (argc == 1) 199 | while ((c = getchar()) != EOF) 200 | putchar(c); 201 | else 202 | while (--argc > 0) 203 | if ((fp = _fopen(*++argv, "r")) == NULL) 204 | exit(EXIT_FAILURE); 205 | else { 206 | while ((c = getc(fp)) != EOF) 207 | putchar(c); 208 | fclose(fp); 209 | } 210 | if (ferror(stdout)) 211 | exit(EXIT_FAILURE); 212 | fclose(stdout); 213 | exit(0); 214 | } 215 | -------------------------------------------------------------------------------- /chapter08/8-5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 8-5. Modify the fsize program to print the other information contained in the inode entry. 3 | * By Faisal Saadatmand 4 | */ 5 | 6 | #define _XOPEN_SOURCE /* for modern Unix sys/stat.h */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include /* flags for read and write */ 12 | #include /* typedefs */ 13 | #include /* structure returned by stat */ 14 | #include "dirent.h" 15 | #include /* for malloc */ 16 | #include 17 | 18 | #include /* structure returned by stat */ 19 | 20 | #define MAX_PATH 1024 21 | #define MAX_TIME 60 22 | #ifndef DIRSIZE 23 | #define DIRSIZE 14 24 | #endif 25 | 26 | struct direct /* directory */ 27 | { 28 | ino_t d_ino; /* inode number */ 29 | char d_name[DIRSIZE]; /* long name does not have '\0' */ 30 | }; 31 | 32 | /* functions */ 33 | void fsize(char *); 34 | void dirwalk(char *, void (*fcn)(char *)); 35 | int fstat(int fd, struct stat *); 36 | void timefmt(char *, int, time_t *); 37 | 38 | /* timefmt: format unix time into local time */ 39 | void timefmt(char *buffer, int bufsize, time_t *utime) 40 | { 41 | struct tm brokentime; 42 | struct tm *tp = &brokentime; 43 | 44 | tp = localtime(utime); /* Unix time to broken local time time */ 45 | strftime(buffer, bufsize, "%b %d %H:%M ", tp); 46 | } 47 | 48 | /* fsize: print size of file name */ 49 | void fsize(char *name) 50 | { 51 | struct stat stbuf; 52 | char *time; /* formated time */ 53 | 54 | if (stat(name, &stbuf) == -1) { 55 | fprintf(stderr, "fsize: can't access %s\n", name); 56 | return; 57 | } 58 | if ((stbuf.st_mode & S_IFMT) == S_IFDIR) 59 | dirwalk(name, fsize); 60 | 61 | if ((time = (char *) malloc(MAX_TIME)) == NULL) { 62 | fprintf(stderr, "fsize: can't allocate memory to buffer %s\n", name); 63 | exit(EXIT_FAILURE); 64 | } 65 | 66 | timefmt(time, MAX_TIME, &stbuf.st_mtime); 67 | printf("%o %lu %u %u %8ld %s %s\n", stbuf.st_mode, stbuf.st_nlink, 68 | stbuf.st_uid, stbuf.st_gid, stbuf.st_size, time, name); 69 | free(time); 70 | } 71 | 72 | /* dirwalk: apply fcn to all files in dir */ 73 | void dirwalk(char *dir, void (*fcn)(char *name)) 74 | { 75 | char name[MAX_PATH]; 76 | Dirent *dp; 77 | DIR *dfd; 78 | 79 | if ((dfd = opendir(dir)) == NULL) { 80 | fprintf(stderr, "dirwalk: can't open %s\n", dir); 81 | return; 82 | } 83 | while ((dp = readdir(dfd)) != NULL) { 84 | if (strcmp(dp->name, ".") == 0 85 | || strcmp(dp->name, "..") == 0) 86 | continue; /* skip self and parent */ 87 | if (strlen(dir) + strlen(dp->name) + 2 > sizeof(name)) 88 | fprintf(stderr, "dirwalk: name %s/%s too long\n", dir, dp->name); 89 | else { 90 | sprintf(name, "%s/%s", dir, dp->name); 91 | (*fcn)(name); 92 | } 93 | } 94 | closedir(dfd); 95 | } 96 | 97 | /* opendir: open a directory for readdir calls */ 98 | DIR *opendir(char *dirname) 99 | { 100 | int fd; 101 | struct stat stbuf; 102 | DIR *dp; 103 | 104 | if ((fd = open(dirname, O_RDONLY, 0)) == -1 105 | || fstat(fd, &stbuf) == -1 106 | || (stbuf.st_mode & S_IFMT) != S_IFDIR 107 | || (dp = (DIR *) malloc(sizeof(DIR))) == NULL) 108 | return NULL; 109 | dp->fd = fd; 110 | return dp; 111 | } 112 | 113 | /* closedir: close directory opened by opendir */ 114 | void closedir(DIR *dp) 115 | { 116 | if (dp) { 117 | close(dp->fd); 118 | free(dp); 119 | } 120 | } 121 | 122 | /* readdir: read directory entries in sequence */ 123 | Dirent *readdir(DIR *dp) 124 | { 125 | struct direct dirbuf; /* local directory structure */ 126 | static Dirent d; /* return: portable structure */ 127 | 128 | while (read(dp->fd, (char *) &dirbuf, sizeof(dirbuf)) == sizeof(dirbuf)) { 129 | if (dirbuf.d_ino == 0) /* slot not in use */ 130 | continue; 131 | d.ino = dirbuf.d_ino; 132 | strncpy(d.name, dirbuf.d_name, DIRSIZE); 133 | d.name[DIRSIZE - 1] = '\0'; /* ensure termination */ 134 | return &d; 135 | } 136 | return NULL; 137 | } 138 | 139 | /* print file size */ 140 | int main(int argc, char **argv) /* **argv == *argv[] */ 141 | { 142 | if (argc == 1) /* default: current directory */ 143 | fsize("."); 144 | else 145 | while (--argc > 0) 146 | fsize(*++argv); 147 | return 0; 148 | } 149 | -------------------------------------------------------------------------------- /chapter08/8-6.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 8-6. The standard library function calloc(n, size) returns a 3 | * pointer to n objects of size size, with the storage initialized to zero. 4 | * Write calloc, by calling malloc or by modifying it. 5 | * 6 | * Note: to clear memory, you could use memset function in my_calloc instead, 7 | * but without measures faster. 8 | * 9 | * By Faisal Saadatmand 10 | */ 11 | 12 | #include /* for NULL */ 13 | 14 | #define NALLOC 1024 /* minimum #units to request */ 15 | 16 | typedef long Align; /* for alignment to long boundary */ 17 | 18 | union header { /* block header: */ 19 | struct { 20 | union header *ptr; /* next block if on free list */ 21 | unsigned size; /* size of this block */ 22 | } s; 23 | Align x; /* force alignment of blocks */ 24 | }; 25 | 26 | typedef union header Header; 27 | 28 | static Header base; /* empty list to get started */ 29 | static Header *freep = NULL; /* start of free list */ 30 | 31 | void *knr_malloc(unsigned); 32 | static Header *morecore(unsigned); 33 | void knr_free(void *); 34 | 35 | /* knr_malloc: general-purpose storage allocator */ 36 | void *knr_malloc(unsigned nbytes) 37 | { 38 | Header *p; /* pointer to current block */ 39 | Header *prevp; /* pointer to previous block */ 40 | 41 | unsigned nunits; 42 | 43 | /* round up to allocate in units of sizeof(Header) */ 44 | nunits = (nbytes + sizeof(Header) - 1) / sizeof(Header) + 1; 45 | if ((prevp = freep) == NULL) { /* no free list yet */ 46 | base.s.ptr = &base; 47 | freep = prevp = &base; /* point all to base */ 48 | base.s.size = 0; 49 | } 50 | 51 | for (p = prevp->s.ptr; ; p = p->s.ptr) { /* search free linked-list */ 52 | if (p->s.size >= nunits) { /* big enough */ 53 | if (p->s.size == nunits) /* exactly */ 54 | prevp->s.ptr = p->s.ptr; 55 | else { 56 | p->s.size -= nunits; 57 | p += p->s.size; /* allocate at tail-end */ 58 | p->s.size = nunits; 59 | } 60 | freep = prevp; 61 | return (void *) (p + 1); 62 | } 63 | if (p == freep) /* wrapped around free list */ 64 | if ((p = morecore(nunits)) == NULL) /* request more memory */ 65 | return NULL; /* none left */ 66 | prevp = p; /* save current pointer's address */ 67 | } 68 | } 69 | 70 | /* my_calloc: general-purpose storage allocator. Initialize memory to zeros */ 71 | void *my_calloc(unsigned n, unsigned size) 72 | { 73 | unsigned char *p; /* char is exactly 1 byte */ 74 | Header *hp; 75 | unsigned bsize; /* actual block size in bytes */ 76 | unsigned i; 77 | 78 | if ((p = (unsigned char *) knr_malloc(n * size)) != NULL) { 79 | hp = (Header *) p - 1; 80 | bsize = (hp->s.size - 1) * sizeof(Header); 81 | for (i = 0; i < bsize - 1; i++) 82 | p[i] &= 0x0u; /* clear each byte */ 83 | } 84 | return (void *) p; 85 | } 86 | 87 | /* morecore: ask system for more memory */ 88 | static Header *morecore(unsigned nu) 89 | { 90 | char *cp; /* pointer to chunk of memory */ 91 | char *sbrk(int); 92 | Header *up; 93 | 94 | if (nu < NALLOC) 95 | nu = NALLOC; 96 | cp = sbrk(nu * sizeof(Header)); 97 | if (cp == (char *) -1) /* no space at all */ 98 | return NULL; 99 | up = (Header *) cp; 100 | up->s.size = nu; 101 | knr_free((void *)(up + 1)); 102 | return freep; 103 | } 104 | 105 | /* knr_free: put block ap in free list */ 106 | void knr_free(void *ap) 107 | { 108 | Header *bp, *p; 109 | 110 | bp = (Header *) ap - 1; /* point to block header */ 111 | for (p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) 112 | if (p >= p->s.ptr && (bp > p || bp < p->s.ptr)) 113 | break; /* free block at start or end of arena */ 114 | 115 | if (bp + bp->s.size == p->s.ptr) { /* join to upper nbr */ 116 | bp->s.size += p->s.ptr->s.size; 117 | bp->s.ptr = p->s.ptr->s.ptr; 118 | } else 119 | bp->s.ptr = p->s.ptr; 120 | if (p + p->s.size == bp) { /* join to lower nbr */ 121 | p->s.size += bp->s.size; 122 | p->s.ptr = bp->s.ptr; 123 | } else 124 | p->s.ptr = bp; 125 | freep = p; 126 | } 127 | 128 | #include 129 | 130 | #define SIZE 20 131 | #define LENGTH 21 132 | 133 | int main(void) 134 | { 135 | int *array, i; 136 | char *s; 137 | 138 | if ((array = (int *) my_calloc(SIZE, sizeof(int))) == NULL) 139 | fprintf(stderr, "my_calloc: Can't allocate memory"); 140 | else { 141 | for (i = 0; i < SIZE; i++) 142 | printf("%i ", array[i]); 143 | printf("\n"); 144 | knr_free(array); 145 | } 146 | 147 | if ((s = (char *) my_calloc(LENGTH, sizeof(char))) == NULL) 148 | fprintf(stderr, "my_calloc: Can't allocate memory"); 149 | else { 150 | for (i = 0; i < LENGTH - 1; i++) 151 | printf("%i ", s[i]); 152 | printf("\n"); 153 | knr_free(s); 154 | } 155 | return 0; 156 | } 157 | -------------------------------------------------------------------------------- /chapter08/8-7.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise 8-7. malloc accepts a size request without checking its 3 | * plausibility; free believes that the block it is asked to free contains a 4 | * valid size field. Improve these routines so they take more pains with error 5 | * checking. 6 | * By Faisal Saadatmand 7 | */ 8 | 9 | #include /* for NULL */ 10 | #include /* for INT_MAX */ 11 | 12 | #define NALLOC 1024 /* minimum #units to request */ 13 | 14 | typedef long Align; /* for alignment to long boundary */ 15 | 16 | union header { /* block header: */ 17 | struct { 18 | union header *ptr; /* next block if on free list */ 19 | unsigned size; /* size of this block */ 20 | } s; 21 | Align x; /* force alignment of blocks */ 22 | }; 23 | 24 | typedef union header Header; 25 | 26 | static Header base; /* empty list to get started */ 27 | static Header *freep = NULL; /* start of free list */ 28 | 29 | void *knr_malloc(unsigned); 30 | static Header *morecore(unsigned); 31 | void knr_free(void *); 32 | 33 | /* knr_malloc: general-purpose storage allocator */ 34 | void *knr_malloc(unsigned nbytes) 35 | { 36 | Header *p; /* pointer to current block */ 37 | Header *prevp; /* pointer to previous block */ 38 | unsigned nunits; 39 | 40 | if (nbytes == 0 || nbytes > INT_MAX) /* error check */ 41 | return NULL; 42 | 43 | /* round up to allocate in units of sizeof(Header) */ 44 | nunits = (nbytes + sizeof(Header) - 1) / sizeof(Header) + 1; 45 | if ((prevp = freep) == NULL) { /* no free list yet */ 46 | base.s.ptr = &base; 47 | freep = prevp = &base; /* point all to base */ 48 | base.s.size = 0; 49 | } 50 | 51 | for (p = prevp->s.ptr; ; p = p->s.ptr) { /* search free linked-list */ 52 | if (p->s.size >= nunits) { /* big enough */ 53 | if (p->s.size == nunits) /* exactly */ 54 | prevp->s.ptr = p->s.ptr; 55 | else { 56 | p->s.size -= nunits; 57 | p += p->s.size; /* allocate at tail-end */ 58 | p->s.size = nunits; 59 | } 60 | freep = prevp; 61 | return (void *) (p + 1); 62 | } 63 | if (p == freep) /* wrapped around free list */ 64 | if ((p = morecore(nunits)) == NULL) /* request more memory */ 65 | return NULL; /* none left */ 66 | prevp = p; /* save current pointer's address */ 67 | } 68 | } 69 | 70 | /* my_calloc: general-purpose storage allocator. Initialize memory to zeros */ 71 | void *my_calloc(unsigned n, unsigned size) 72 | { 73 | unsigned char *p; /* char is exactly 1 byte */ 74 | Header *hp; 75 | unsigned bsize; /* actual block size in bytes */ 76 | unsigned i; 77 | 78 | if ((p = (unsigned char *) knr_malloc(n * size)) != NULL) { 79 | hp = (Header *) p - 1; 80 | bsize = (hp->s.size - 1) * sizeof(Header); 81 | for (i = 0; i < bsize - 1; i++) 82 | p[i] &= 0x0u; /* clear each byte */ 83 | } 84 | return (void *) p; 85 | } 86 | 87 | /* morecore: ask system for more memory */ 88 | static Header *morecore(unsigned nu) 89 | { 90 | char *cp; /* pointer to chunk of memory */ 91 | char *sbrk(int); 92 | Header *up; 93 | 94 | if (nu < NALLOC) 95 | nu = NALLOC; 96 | cp = sbrk(nu * sizeof(Header)); 97 | if (cp == (char *) -1) /* no space at all */ 98 | return NULL; 99 | up = (Header *) cp; 100 | up->s.size = nu; 101 | knr_free((void *)(up + 1)); 102 | return freep; 103 | } 104 | 105 | /* knr_free: put block ap in free list */ 106 | void knr_free(void *ap) 107 | { 108 | Header *bp, *p; 109 | int valid; 110 | 111 | valid = 1; 112 | if (ap == NULL) /* error checking */ 113 | valid = 0; 114 | else { 115 | bp = (Header *) ap - 1; /* point to block header */ 116 | if (bp->s.size <= 1) /* must be at least 2 units: */ 117 | valid = 0; /* 1 for header, 1 for mem block */ 118 | } 119 | 120 | if (valid) { 121 | for (p = freep ; !(bp > p && bp < p->s.ptr); p = p->s.ptr) 122 | if (p >= p->s.ptr && (bp > p || bp < p->s.ptr)) 123 | break; /* free block at start or end of arena */ 124 | 125 | if (bp + bp->s.size == p->s.ptr) { /* join to upper nbr */ 126 | bp->s.size += p->s.ptr->s.size; 127 | bp->s.ptr = p->s.ptr->s.ptr; 128 | } else 129 | bp->s.ptr = p->s.ptr; 130 | if (p + p->s.size == bp) { /* join to lower nbr */ 131 | p->s.size += bp->s.size; 132 | p->s.ptr = bp->s.ptr; 133 | } else 134 | p->s.ptr = bp; 135 | freep = p; 136 | } 137 | } 138 | 139 | #include 140 | 141 | #define SIZE 21 /* chenge value to test error checking */ 142 | 143 | int main(void) 144 | { 145 | char *s; 146 | 147 | if ((s = (char *) knr_malloc(SIZE * sizeof(char))) != NULL) 148 | printf("Valid size\n"); 149 | else 150 | fprintf(stderr, "Invalid size\n"); 151 | 152 | knr_free(s); 153 | 154 | return 0; 155 | } 156 | -------------------------------------------------------------------------------- /chapter08/dirent.h: -------------------------------------------------------------------------------- 1 | #define NAME_MAX 14 /* longest filename; system-dependent */ 2 | 3 | typedef struct { /* portable directory entry; */ 4 | long ino; /* inode number */ 5 | char name[NAME_MAX]; /* name + '\0' terminator */ 6 | } Dirent; 7 | 8 | typedef struct { /* minimal DIR: no buffering, etc. */ 9 | int fd; /* file descriptor for directory */ 10 | Dirent d; /* the directory entry */ 11 | } DIR; 12 | 13 | DIR *opendir(char *dirname); 14 | Dirent *readdir(DIR *dfd); 15 | void closedir(DIR *dfd); 16 | --------------------------------------------------------------------------------