├── .clang-format ├── .vscode ├── extensions.json ├── settings.json ├── c_cpp_properties.json ├── launch.json └── tasks.json ├── chapter_1 ├── exercise_1_01 │ └── hello_world.c ├── exercise_1_02 │ └── printf_argument.c ├── exercise_1_07 │ └── print_eof.c ├── exercise_1_09 │ └── copy_io.c ├── exercise_1_06 │ └── verify_expression.c ├── exercise_1_20 │ ├── file_in.txt │ ├── file_out.txt │ └── detab.c ├── exercise_1_10 │ └── copy_io_esc_replace.c ├── exercise_1_08 │ └── count_b_t_n.c ├── exercise_1_04 │ └── celsius_fahrenheit.c ├── exercise_1_11 │ └── word_count_test.c ├── exercise_1_12 │ └── copy_io_nl.c ├── exercise_1_15 │ └── temperature_conversion.c ├── exercise_1_17 │ └── line_80.c ├── exercise_1_21 │ ├── entab.c │ ├── file_out.txt │ └── file_in.txt ├── exercise_1_03 │ └── fahrenheit_celsius.c ├── exercise_1_05 │ └── celsius_fahrenheit.c ├── exercise_1_18 │ └── trailing_blanks.c ├── exercise_1_14 │ └── frequency_histogram.c ├── exercise_1_16 │ └── longest_line.c ├── exercise_1_19 │ └── reverse.c ├── exercise_1_22 │ └── fold_line.c ├── exercise_1_23 │ └── c_remove_comments.c ├── exercise_1_13 │ └── histogram.c └── exercise_1_24 │ └── check_syntax.c ├── chapter_5 ├── exercise_5_19 │ ├── test.txt │ └── undcl.c ├── exercise_5_17 │ └── file_in.txt ├── exercise_5_06 │ ├── getline.c │ ├── atoi.c │ ├── reverse.c │ ├── strindex.c │ ├── itoa.c │ └── getop.c ├── exercise_5_05 │ ├── strncpy.c │ ├── strncat.c │ └── strncmp.c ├── exercise_5_18 │ ├── test.txt │ └── dcl.c ├── exercise_5_12 │ ├── file_tabs.txt │ ├── file_spaces.txt │ ├── detab.c │ └── entab.c ├── exercise_5_03 │ └── strcat.c ├── exercise_5_13 │ ├── file_in.txt │ └── tail.c ├── exercise_5_14 │ └── file_in.txt ├── exercise_5_16 │ └── file_in.txt ├── exercise_5_20 │ └── test.txt ├── exercise_5_04 │ └── strend.c ├── exercise_5_15 │ └── file_in.txt ├── exercise_5_02 │ └── getfloat.c ├── exercise_5_01 │ └── getint.c ├── exercise_5_11 │ ├── file_tabs.txt │ ├── detab.c │ ├── file_spaces.txt │ └── entab.c ├── exercise_5_08 │ └── date_conversion.c ├── exercise_5_09 │ └── date_conversion_pointers.c ├── exercise_5_10 │ └── expr.c └── exercise_5_07 │ └── readlines.c ├── .clang-tidy ├── .editorconfig ├── chapter_2 ├── exercise_2_10 │ └── lower.c ├── exercise_2_04 │ └── squeeze.c ├── exercise_2_05 │ └── any.c ├── exercise_2_02 │ └── loop.c ├── exercise_2_09 │ └── bitcount.c ├── exercise_2_07 │ └── invert.c ├── exercise_2_06 │ └── setbits.c ├── exercise_2_08 │ └── rightrot.c ├── exercise_2_01 │ └── types_ranges.c └── exercise_2_03 │ └── htoi.c ├── .github ├── workflows │ └── c.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── FUNDING.yml ├── .gitignore ├── chapter_4 ├── exercise_4_08 │ └── getch.c ├── exercise_4_12 │ └── itoa.c ├── exercise_4_13 │ └── reverse.c ├── exercise_4_14 │ └── swap.c ├── exercise_4_09 │ └── getch.c ├── exercise_4_07 │ └── ungets.c ├── exercise_4_01 │ └── strindex.c ├── exercise_4_02 │ └── atof.c ├── exercise_4_11 │ └── getop.c ├── exercise_4_03 │ └── calculator.c ├── exercise_4_04 │ └── stack.c └── exercise_4_05 │ └── math.c ├── chapter_6 ├── exercise_6_03 │ └── test.txt ├── exercise_6_05 │ └── undef.c ├── exercise_6_01 │ └── count_c_keywords.c └── exercise_6_04 │ └── words_frequency.c ├── chapter_7 ├── exercise_7_06 │ ├── file_2.txt │ ├── file_1.txt │ └── compare.c ├── exercise_7_07 │ ├── file_2.txt │ ├── file_1.txt │ └── find.c ├── exercise_7_02 │ ├── test.txt │ └── print.c ├── exercise_7_09 │ └── isupper.c ├── exercise_7_01 │ └── case.c ├── exercise_7_08 │ └── print.c ├── exercise_7_03 │ └── minprintf.c ├── exercise_7_05 │ └── calculator.c └── exercise_7_04 │ └── minscanf.c ├── LICENSE ├── chapter_8 ├── exercise_8_01 │ └── cat.c ├── exercise_8_02 │ ├── syscalls.h │ └── syscalls.c ├── exercise_8_03 │ └── syscalls.h ├── exercise_8_04 │ └── syscalls.h ├── exercise_8_06 │ └── calloc.c ├── exercise_8_07 │ └── malloc_free.c └── exercise_8_05 │ └── fsize.c ├── chapter_3 ├── exercise_3_06 │ └── itoa.c ├── exercise_3_04 │ └── itoa.c ├── exercise_3_05 │ └── itob.c ├── exercise_3_03 │ └── expand.c ├── exercise_3_01 │ └── binsearch.c └── exercise_3_02 │ └── escape.c ├── Makefile ├── README.md └── CONTRIBUTING.md /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["llvm-vs-code-extensions.vscode-clangd"] 3 | } 4 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_01/hello_world.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | printf("hello, world\n"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_02/printf_argument.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | printf("hello, world\\c"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_19/test.txt: -------------------------------------------------------------------------------- 1 | x * int 2 | argv * * char 3 | daytab * [] int 4 | daytab [] * int 5 | comp () * void 6 | comp * () void 7 | x () * [] * () char 8 | x [] * () * [] char 9 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_07/print_eof.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | printf("EOF: %d", EOF); 5 | return 0; 6 | } 7 | 8 | // NOTE: The value of the EOF character is -1, which is an integer. 9 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_17/file_in.txt: -------------------------------------------------------------------------------- 1 | 3900083151 Audi 2003-03-03 2 | 3900083151 audi 2003-03-03 3 | 87423 Volkswagen 2001-01-01 4 | 3900063521 Renault 2003-03-03 5 | 642396 Fiat 2002-02-02 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "[c]": { 4 | "editor.formatOnSave": true 5 | }, 6 | "C_Cpp.clang_format_style": "file", 7 | "C_Cpp.clang_format_fallbackStyle": "LLVM" 8 | } 9 | 10 | 11 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | # Minimal checks for K&R C code style 3 | # Only enforce one variable per declaration for readability 4 | Checks: > 5 | -*, 6 | readability-isolate-declaration 7 | 8 | WarningsAsErrors: '' 9 | HeaderFilterRegex: '.*' 10 | FormatStyle: 'file' 11 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_09/copy_io.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | int c; 5 | int last_c = '\0'; 6 | 7 | while ((c = getchar()) != EOF) { 8 | if (c != ' ' || last_c != ' ') { 9 | putchar(c); 10 | } 11 | 12 | last_c = c; 13 | } 14 | 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.{c,h}] 14 | indent_size = 4 15 | 16 | [*.md] 17 | insert_final_newline = false 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /chapter_2/exercise_2_10/lower.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | char lower(char c); 4 | 5 | int main(void) { 6 | char a = 'A'; 7 | 8 | putchar(lower(a)); 9 | 10 | return 0; 11 | } 12 | 13 | char lower(char c) { return (c >= 'A' && c <= 'Z') ? c += 'a' - 'A' : c; } 14 | 15 | // NOTE: The ternary operator ?: can be used direct in return. 16 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_06/getline.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void get_line(char *s); 4 | 5 | int main(void) { 6 | char string[150] = ""; 7 | 8 | get_line(string); 9 | puts(string); 10 | 11 | return 0; 12 | } 13 | 14 | void get_line(char *s) { 15 | while ((*s = getchar()) != EOF && (*s != '\n')) { 16 | ++s; 17 | } 18 | 19 | *s = '\0'; 20 | } 21 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_06/verify_expression.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | printf("value of expression: %d", getchar() != EOF); 5 | return 0; 6 | } 7 | 8 | // NOTE: The expression getchar() != EOF is equal with 1 only if input char 9 | // is != with EOF. A useful thing to know is that on Windows the EOF char is 10 | // accessible with CTRL+Z, while on Unix like systems the EOF char is 11 | // accessible with CTRL+D. 12 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_06/atoi.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int atoi(const char *s); 4 | 5 | int main(void) { 6 | int i; 7 | char *s = "12s3a"; 8 | 9 | i = atoi(s); 10 | 11 | printf("atoi: %d", i); 12 | 13 | return 0; 14 | } 15 | 16 | int atoi(const char *s) { 17 | int i = 0; 18 | 19 | while (*s != '\0' && *s > '0' && *s < '9') { 20 | i = i * 10 + *s - '0'; 21 | ++s; 22 | } 23 | 24 | return i; 25 | } 26 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_20/file_in.txt: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define TAB_LENGTH 8 4 | 5 | int main(void) 6 | { 7 | int c; 8 | unsigned int nr_of_spaces; 9 | 10 | while ((c = getchar()) != EOF) 11 | { 12 | if (c == '\t') 13 | { 14 | nr_of_spaces = TAB_LENGTH; 15 | 16 | while (nr_of_spaces) 17 | { 18 | putchar(' '); 19 | --nr_of_spaces; 20 | } 21 | } 22 | else 23 | { 24 | putchar(c); 25 | } 26 | } 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_05/strncpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void strcpy_ptr(char *s, char *t, size_t n); 4 | 5 | int main(void) { 6 | char s[100] = "This is the first string"; 7 | char *t = "Test is the second string, that is cool"; 8 | size_t nr_chars = 26; 9 | 10 | strcpy_ptr(s, t, nr_chars); 11 | 12 | puts(s); 13 | 14 | return 0; 15 | } 16 | 17 | void strcpy_ptr(char *s, char *t, size_t n) { 18 | while ((*s++ = *t++) && --n) 19 | ; 20 | ; 21 | } 22 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_06/reverse.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void reverse(char *s); 5 | 6 | int main(void) { 7 | char s[100] = "test"; 8 | 9 | reverse(s); 10 | puts(s); 11 | 12 | return 0; 13 | } 14 | 15 | void reverse(char *s) { 16 | char *t = s + strlen(s) - 1; 17 | char aux = 0; 18 | 19 | if (*s == '\0') 20 | return; 21 | 22 | while (s < t) { 23 | aux = *t; 24 | *t-- = *s; 25 | *s++ = aux; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/c.yml: -------------------------------------------------------------------------------- 1 | name: C CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | check: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | 15 | - name: Install clang-tidy 16 | run: sudo apt-get update && sudo apt-get install -y clang-tidy 17 | 18 | - name: Check formatting 19 | run: make format-check 20 | 21 | - name: Lint 22 | run: make lint 23 | 24 | - name: Build 25 | run: make 26 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_10/copy_io_esc_replace.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | char c; 5 | while ((c = getchar()) != EOF) { 6 | if (c == '\t') { 7 | putchar('\\'); 8 | putchar('t'); 9 | } else if (c == '\b') { 10 | putchar('\\'); 11 | putchar('b'); 12 | } else if (c == '\\') { 13 | putchar('\\'); 14 | putchar('\\'); 15 | } else { 16 | putchar(c); 17 | } 18 | } 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_18/test.txt: -------------------------------------------------------------------------------- 1 | char **argv 2 | int ( * daytab ) [ 13] 3 | int *daytab[13] 4 | long a_name[ MAX_SIZE ] // This is a comment 5 | void * comp( ) 6 | void (*comp_2)() 7 | char (*(* x() )[])( ) 8 | char (*(*x[ 3 ] ) ( ) ) [ 5 ] 9 | char a /* This is a comment */ 10 | int b 11 | float A_TEST_NAME 12 | float fn_name_test() 13 | 14 | int b 15 | 16 | 17 | 18 | 19 | long f ( ) 20 | void * comp() 21 | long name_0 [ 22 | int t_0 23 | void name_1 ( 24 | int t_0 25 | void name_1 bad 26 | float m 27 | custom_type c_t_name 28 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_08/count_b_t_n.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | int blanks_nr = 0; 5 | int tabs_nr = 0; 6 | int newlines_nr = 0; 7 | 8 | char c; 9 | while ((c = getchar()) != EOF) { 10 | if (c == ' ') { 11 | ++blanks_nr; 12 | } else if (c == '\t') { 13 | ++tabs_nr; 14 | } else if (c == '\n') { 15 | ++newlines_nr; 16 | } 17 | } 18 | 19 | printf("blanks_nr: %d\ntabs_nr: %d\nnewlines_nr: %d\n", blanks_nr, tabs_nr, 20 | newlines_nr); 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore all files without an extension 2 | * 3 | !*/ 4 | !*.* 5 | 6 | # Don't ignore Makefile 7 | !Makefile 8 | 9 | # Unknoun files 10 | *.ipch 11 | 12 | # Object files 13 | *.o 14 | *.ko 15 | *.obj 16 | *.elf 17 | 18 | # Precompiled Headers 19 | *.gch 20 | *.pch 21 | 22 | # Libraries 23 | *.lib 24 | *.a 25 | *.la 26 | *.lo 27 | *.bin 28 | 29 | # Shared objects (inc. Windows DLLs) 30 | *.dll 31 | *.so 32 | *.so.* 33 | *.dylib 34 | 35 | # Executables 36 | *.exe 37 | *.out 38 | *.app 39 | *.i*86 40 | *.x86_64 41 | *.hex 42 | 43 | # Debug files 44 | *.dSYM/ 45 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_12/file_tabs.txt: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define TAB_WIDTH 8 4 | 5 | int main(void) 6 | { 7 | int c; 8 | int line_pos = 0; 9 | char nr_of_spaces; 10 | 11 | while ((c = getchar()) != EOF) 12 | { 13 | if (c == '\t') 14 | { 15 | nr_of_spaces = TAB_WIDTH - line_pos % TAB_WIDTH; 16 | line_pos += nr_of_spaces; 17 | 18 | while (nr_of_spaces) 19 | { 20 | putchar(' '); 21 | --nr_of_spaces; 22 | } 23 | } 24 | else 25 | { 26 | putchar(c); 27 | 28 | if (c == '\n') 29 | { 30 | line_pos = 0; 31 | } 32 | else 33 | { 34 | ++line_pos; 35 | } 36 | } 37 | } 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /chapter_4/exercise_4_08/getch.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int getch(void); 4 | void ungetch(int c); 5 | 6 | int main(void) { 7 | char c = getch(); 8 | printf("%c\n", c); 9 | 10 | ungetch(c); 11 | 12 | printf("%c\n", c = getch()); 13 | 14 | return 0; 15 | } 16 | 17 | int buf = -1; 18 | 19 | int getch(void) { 20 | char temp; 21 | 22 | if (buf != -1) { 23 | temp = buf; 24 | buf = -1; 25 | 26 | return temp; 27 | } 28 | 29 | return getchar(); 30 | } 31 | 32 | void ungetch(int c) { 33 | if (buf != -1) { 34 | printf("ungetch: buffer full\n"); 35 | } else { 36 | buf = c; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 1. Go to '...' 15 | 2. Click on '...' 16 | 3. Scroll down to '...' 17 | 4. See Error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Additional context** 26 | Add any other context about the problem here. 27 | -------------------------------------------------------------------------------- /chapter_2/exercise_2_04/squeeze.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MAXSTR 1000 4 | 5 | void squeeze(char str1[], char str2[]); 6 | 7 | int main(void) { 8 | char str1[MAXSTR] = "abcdefg"; 9 | char str2[MAXSTR] = "abcd"; 10 | 11 | squeeze(str1, str2); 12 | 13 | printf("%s", str1); 14 | 15 | return 0; 16 | } 17 | 18 | void squeeze(char str1[], char str2[]) { 19 | int i; 20 | int j; 21 | int k; 22 | for (k = 0; str2[k] != '\0'; ++k) { 23 | for (i = j = 0; str1[i] != '\0'; ++i) { 24 | if (str1[i] != str2[k]) { 25 | str1[j++] = str1[i]; 26 | } 27 | } 28 | 29 | str1[j] = '\0'; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ohkimur] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_04/celsius_fahrenheit.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Print Celsius-Fahrenheit table 4 | // for celsius = 0, 20, ... 300 5 | // floating point version 6 | 7 | int main(void) { 8 | float celsius; 9 | float fahr; 10 | int lower; 11 | int upper; 12 | int step; 13 | 14 | lower = 0; 15 | upper = 300; 16 | step = 20; 17 | 18 | printf("Celsius\tFahr\n"); 19 | printf("---------------\n"); 20 | 21 | celsius = lower; 22 | while (celsius <= upper) { 23 | fahr = (9.0 / 5.0) * celsius + 32.0f; 24 | printf("%3.0f\t\t%6.1f\n", celsius, fahr); 25 | celsius = celsius + step; 26 | } 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_03/strcat.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void strcat_ptr(char *s, char *t); 4 | 5 | int main(void) { 6 | char s[100] = "This is the first string"; 7 | char t[] = ", this second string!"; 8 | 9 | strcat_ptr(s, t); 10 | 11 | puts(s); 12 | 13 | return 0; 14 | } 15 | 16 | // Concatenate t to end of s; s must be big enough. 17 | void strcat_ptr(char *s, char *t) { 18 | // Find the end of s 19 | while (*s) 20 | ++s; 21 | 22 | // copy t to the end of s 23 | // Extra parentheses around assignment suppress compiler warning about 24 | // using assignment as condition (this is intentional and safe here) 25 | while ((*s++ = *t++)) 26 | ; 27 | } 28 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Mac", 5 | "includePath": ["${workspaceFolder}/**"], 6 | "defines": [], 7 | "macFrameworkPath": [ 8 | "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks" 9 | ], 10 | "compilerPath": "/usr/bin/clang", 11 | "cStandard": "c11", 12 | "intelliSenseMode": "macos-clang-x64" 13 | }, 14 | { 15 | "name": "Linux", 16 | "includePath": ["${workspaceFolder}/**"], 17 | "defines": [], 18 | "compilerPath": "/usr/bin/gcc", 19 | "cStandard": "c11", 20 | "intelliSenseMode": "linux-gcc-x64" 21 | } 22 | ], 23 | "version": 4 24 | } 25 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_06/strindex.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int strindex(char *s, char *t); 4 | 5 | int main(void) { 6 | char s[] = "this is first string"; 7 | char t[] = "this"; 8 | 9 | printf("%d", strindex(s, t)); 10 | 11 | return 0; 12 | } 13 | 14 | int strindex(char *s, char *t) { 15 | char *first; 16 | char *second; 17 | int pos = 0; 18 | 19 | while (*s != '\0') { 20 | if (*s == *t) { 21 | first = s; 22 | second = t; 23 | 24 | while (*first++ == *second++) { 25 | if (*second == '\0') 26 | return pos; 27 | } 28 | } 29 | pos++; 30 | s++; 31 | } 32 | 33 | return -1; 34 | } 35 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_05/strncat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void strcat_ptr(char *s, char *t, size_t n); 5 | 6 | int main(void) { 7 | char s[100] = "This is the first string"; 8 | char *t = ", this second string!"; 9 | size_t nr_chars = 5; 10 | 11 | strcat_ptr(s, t, nr_chars); 12 | 13 | puts(s); 14 | 15 | return 0; 16 | } 17 | 18 | // Concatenate t to end of s; s must be big enough. 19 | void strcat_ptr(char *s, char *t, size_t n) { 20 | // Find the end of s 21 | size_t s_length = strlen(s); 22 | 23 | // Move the s pointer to the end of the s string. 24 | s += s_length; 25 | 26 | // copy t to the end of s 27 | while ((*s++ = *t++) != '\0' && n--) 28 | ; 29 | } 30 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_20/file_out.txt: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define TAB_LENGTH 8 4 | 5 | int main(void) 6 | { 7 | int c; 8 | unsigned int nr_of_spaces; 9 | 10 | while ((c = getchar()) != EOF) 11 | { 12 | if (c == '\t') 13 | { 14 | nr_of_spaces = TAB_LENGTH; 15 | 16 | while (nr_of_spaces) 17 | { 18 | putchar(' '); 19 | --nr_of_spaces; 20 | } 21 | } 22 | else 23 | { 24 | putchar(c); 25 | } 26 | } 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_11/word_count_test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define IN 1 4 | #define OUT 0 5 | 6 | int main(void) { 7 | char nl; 8 | char nw; 9 | char nc; 10 | 11 | int state; 12 | 13 | nl = nw = nc = 0; 14 | state = OUT; 15 | 16 | char c; 17 | while ((c = getchar()) != EOF) { 18 | ++nc; 19 | 20 | if (c == '\n') { 21 | ++nl; 22 | } 23 | 24 | if (c == ' ' || c == '\n' || c == '\t') { 25 | state = OUT; 26 | } else if (state == OUT) { 27 | state = IN; 28 | ++nw; 29 | } 30 | } 31 | 32 | printf("lines: %d\nwords: %d\ncharacters: %d\n", nl, nw, nc); 33 | 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /chapter_6/exercise_6_03/test.txt: -------------------------------------------------------------------------------- 1 | Throughout human history, people have tried to define intelligence several times. 2 | Some people think about it as the capacity for logic, reasoning, or critical 3 | thinking. Some people have stated that intelligence might be self-awareness, 4 | emotional knowledge, or creativity. Other people think about intelligence as the 5 | capacity for planning and problem-solving. 6 | 7 | Somebody could think about intelligence from a large diversity of perspectives. 8 | Trying to define it is not the most simple type of problem that one could deal 9 | with. It’s natural to start asking some questions about this problem. Why is 10 | intelligence so hard to define? What makes it so special? Is even possible to 11 | define what it is? 12 | -------------------------------------------------------------------------------- /chapter_7/exercise_7_06/file_2.txt: -------------------------------------------------------------------------------- 1 | Throughout human history, people have tried to define intelligence several times. 2 | Some people think about it as the capacity for logic, reasoning, or critical 3 | thinking. Some people have stated that intelligence might be self-awareness, 4 | emotional knowledge, or creativity. Other people think about intelligence as the 5 | capacity for planning and problem-solving. 6 | 7 | One could think about intelligence from a large diversity of perspectives. 8 | Trying to define it is not the most simple type of problem that one could deal 9 | with. It is natural to start asking some questions about this problem. Why is 10 | intelligence so hard to define? What makes it that special? Is even possible to 11 | define what it is? 12 | -------------------------------------------------------------------------------- /chapter_7/exercise_7_07/file_2.txt: -------------------------------------------------------------------------------- 1 | Throughout human history, people have tried to define intelligence several times. 2 | Some people think about it as the capacity for logic, reasoning, or critical 3 | thinking. Some people have stated that intelligence could be self-awareness, 4 | emotional knowledge, or creativity. Other people think about intelligence as the 5 | capacity for planning and problem-solving. 6 | 7 | One could think about intelligence from a large diversity of perspectives. 8 | Trying to define it is not the most simple type of problem that one could deal 9 | with. It is natural to start asking some questions about this problem. Why is 10 | intelligence so hard to define? What makes it that special? Is even possible to 11 | define what it is? 12 | -------------------------------------------------------------------------------- /chapter_7/exercise_7_06/file_1.txt: -------------------------------------------------------------------------------- 1 | Throughout human history, people have tried to define intelligence several times. 2 | Some people think about it as the capacity for logic, reasoning, or critical 3 | thinking. Some people have stated that intelligence might be self-awareness, 4 | emotional knowledge, or creativity. Other people think about intelligence as the 5 | capacity for planning and problem-solving. 6 | 7 | Somebody could think about intelligence from a large diversity of perspectives. 8 | Trying to define it is not the most simple type of problem that one could deal 9 | with. It’s natural to start asking some questions about this problem. Why is 10 | intelligence so hard to define? What makes it so special? Is even possible to 11 | define what it is? 12 | -------------------------------------------------------------------------------- /chapter_7/exercise_7_07/file_1.txt: -------------------------------------------------------------------------------- 1 | Throughout human history, people have tried to define intelligence several times. 2 | Some people think about it as the capacity for logic, reasoning, or critical 3 | thinking. Some people have stated that intelligence might be self-awareness, 4 | emotional knowledge, or creativity. Other people think about intelligence as the 5 | capacity for planning and problem-solving. 6 | 7 | Somebody could think about intelligence from a large diversity of perspectives. 8 | Trying to define it is not the most simple type of problem that one could deal 9 | with. It’s natural to start asking some questions about this problem. Why is 10 | intelligence so hard to define? What makes it so special? Is even possible to 11 | define what it is? 12 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_20/detab.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define TAB_LENGTH 8 4 | 5 | int main(void) { 6 | int c; 7 | unsigned int nr_of_spaces; 8 | 9 | while ((c = getchar()) != EOF) { 10 | if (c == '\t') { 11 | nr_of_spaces = TAB_LENGTH; 12 | 13 | while (nr_of_spaces) { 14 | putchar(' '); 15 | --nr_of_spaces; 16 | } 17 | } else { 18 | putchar(c); 19 | } 20 | } 21 | 22 | return 0; 23 | } 24 | 25 | // NOTE: In UNIX like systems you can execute commands like this: 26 | // ./detab < file_in.txt > file_out.txt that takes the input from a 27 | // file and after the program processed its content puts it to another file. 28 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_12/copy_io_nl.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Using (void) instead of () explicitly declares that main takes no parameters 4 | // This is the preferred modern C style and avoids compiler warnings 5 | int main(void) { 6 | int character; 7 | int previous_character = EOF; 8 | 9 | while ((character = getchar()) != EOF) { 10 | if (character == ' ' || character == '\t' || character == '\n') { 11 | if (previous_character != ' ' && previous_character != '\t' && 12 | previous_character != '\n') { 13 | putchar('\n'); 14 | } 15 | } else { 16 | putchar(character); 17 | } 18 | 19 | previous_character = character; 20 | } 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /chapter_7/exercise_7_02/test.txt: -------------------------------------------------------------------------------- 1 | Throughout human history, people have tried to define intelligence several times. 2 | Some people think about it as the capacity for logic, reasoning, or critical 3 | thinking. Some people have stated 🤦‍♂️ 😎 that intelligence might be self-awareness, 4 | emotional knowledge, or creativity. Other people think about intelligence as the 5 | capacity for planning and problem-solving. 🥳 6 | 7 | Somebody could think about intelligence from a large diversity of perspectives. 8 | Trying to define it is not the most simple type of problem that one could deal 9 | with. It’s natural to start asking some questions about this problem. Why is 10 | intelligence so hard to define? 🤷‍♂️ What makes it so special? Is even possible to 11 | define what it is? 12 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_06/itoa.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void reverse(char *s); 5 | void itoa(char *s, int n); 6 | 7 | int main(void) { 8 | int i = 1234; 9 | char s[100] = ""; 10 | 11 | itoa(s, i); 12 | 13 | printf("itoa: %s\n", s); 14 | 15 | return 0; 16 | } 17 | 18 | void reverse(char *s) { 19 | char *t = s + strlen(s) - 1; 20 | char aux = 0; 21 | 22 | if (*s == '\0') 23 | return; 24 | 25 | while (s < t) { 26 | aux = *t; 27 | *t-- = *s; 28 | *s++ = aux; 29 | } 30 | } 31 | 32 | void itoa(char *s, int n) { 33 | char *t = s; 34 | 35 | while (n) { 36 | *(t++) = n % 10 + '0'; 37 | n /= 10; 38 | } 39 | 40 | *t = '\0'; 41 | 42 | reverse(s); 43 | } 44 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_13/file_in.txt: -------------------------------------------------------------------------------- 1 | 1. Throughout human history, people have tried to define intelligence several times. 2 | 2. Some people think about it as the capacity for logic, reasoning, or critical 3 | 3. thinking. Some people have stated that intelligence might be self-awareness, 4 | 4. emotional knowledge, or creativity. Other people think about intelligence as the 5 | 5. capacity for planning and problem-solving. 6 | 6. 7 | 7. Somebody could think about intelligence from a large diversity of perspectives. 8 | 8. Trying to define it is not the most simple type of problem that one could deal 9 | 9. with. It’s natural to start asking some questions about this problem. Why is 10 | 10. intelligence so hard to define? What makes it so special? Is even possible to 11 | 11. define what it is? 12 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_14/file_in.txt: -------------------------------------------------------------------------------- 1 | 1. Throughout human history, people have tried to define intelligence several times. 2 | 2. Some people think about it as the capacity for logic, reasoning, or critical 3 | 3. thinking. Some people have stated that intelligence might be self-awareness, 4 | 4. emotional knowledge, or creativity. Other people think about intelligence as the 5 | 5. capacity for planning and problem-solving. 6 | 6. 7 | 7. Somebody could think about intelligence from a large diversity of perspectives. 8 | 8. Trying to define it is not the most simple type of problem that one could deal 9 | 9. with. It’s natural to start asking some questions about this problem. Why is 10 | 10. intelligence so hard to define? What makes it so special? Is even possible to 11 | 11. define what it is? 12 | -------------------------------------------------------------------------------- /chapter_7/exercise_7_09/isupper.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int is_upper_v1(int c); 7 | int is_upper_v2(int c); 8 | 9 | int main(void) { 10 | if (is_upper_v1('c')) { 11 | puts("is_upper_v1: This letter is uppercase."); 12 | } else { 13 | puts("is_upper_v1: This letter is lowercase."); 14 | } 15 | 16 | if (is_upper_v2('c')) { 17 | puts("is_upper_v2: This letter is uppercase."); 18 | } else { 19 | puts("is_upper_v2: This letter is lowercase."); 20 | } 21 | 22 | exit(EXIT_SUCCESS); 23 | } 24 | 25 | int is_upper_v1(int c) { return (c >= 'A' && c <= 'Z'); } 26 | 27 | int is_upper_v2(int c) { 28 | return (strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", c) != NULL); 29 | } 30 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_12/file_spaces.txt: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define TAB_WIDTH 8 4 | 5 | int main(void) 6 | { 7 | int c; 8 | int line_pos = 0; 9 | char nr_of_spaces; 10 | 11 | while ((c = getchar()) != EOF) 12 | { 13 | if (c == '\t') 14 | { 15 | nr_of_spaces = TAB_WIDTH - line_pos % TAB_WIDTH; 16 | line_pos += nr_of_spaces; 17 | 18 | while (nr_of_spaces) 19 | { 20 | putchar(' '); 21 | --nr_of_spaces; 22 | } 23 | } 24 | else 25 | { 26 | putchar(c); 27 | 28 | if (c == '\n') 29 | { 30 | line_pos = 0; 31 | } 32 | else 33 | { 34 | ++line_pos; 35 | } 36 | } 37 | } 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_15/temperature_conversion.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | float celsius_to_fahrenheit(int celsius); 4 | 5 | int main(void) { 6 | float celsius; 7 | float fahr; 8 | int lower; 9 | int upper; 10 | int step; 11 | 12 | lower = 0; 13 | upper = 300; 14 | step = 30; 15 | 16 | // Printing a heading abouve the table 17 | printf("Celsius\t\tFahr.\n"); 18 | printf("----------------------\n"); 19 | 20 | celsius = lower; 21 | while (celsius <= upper) { 22 | fahr = celsius_to_fahrenheit(celsius); 23 | printf("%3.0f\t\t%6.1f\n", celsius, fahr); 24 | celsius += step; 25 | } 26 | 27 | return 0; 28 | } 29 | 30 | float celsius_to_fahrenheit(int celsius) { 31 | return (9.0 / 5.0) * celsius + 32.0f; 32 | } 33 | -------------------------------------------------------------------------------- /chapter_4/exercise_4_12/itoa.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MAXLEN 100 4 | 5 | void int_to_array(int n, char str[]); 6 | 7 | int main(void) { 8 | int n = -1234; 9 | char str[MAXLEN]; 10 | 11 | int_to_array(n, str); 12 | printf("%s\n", str); 13 | 14 | n = -7676; 15 | 16 | int_to_array(n, str); 17 | printf("%s", str); 18 | 19 | return 0; 20 | } 21 | 22 | void int_to_array(int n, char str[]) { 23 | static int i = 0; 24 | 25 | if (n) { 26 | if (n < 0) { 27 | i = 0; 28 | str[i++] = '-'; 29 | n *= -1; 30 | } 31 | 32 | int d = n % 10; 33 | n /= 10; 34 | 35 | int_to_array(n, str); 36 | 37 | str[i++] = d + '0'; 38 | str[i] = '\0'; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_17/line_80.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MAXLINE 1000 4 | #define LIMIT 80 5 | 6 | int get_line(char line[], int max_line_len); 7 | 8 | int main(void) { 9 | int len; 10 | char line[MAXLINE]; 11 | 12 | while ((len = get_line(line, MAXLINE)) > 0) { 13 | if (len > LIMIT) { 14 | printf("%s", line); 15 | } 16 | } 17 | 18 | return 0; 19 | } 20 | 21 | int get_line(char line[], int max_line_len) { 22 | int c; 23 | int i; 24 | 25 | for (i = 0; i < max_line_len - 1 && (c = getchar()) != EOF && c != '\n'; 26 | ++i) { 27 | line[i] = c; 28 | } 29 | 30 | if (c == '\n') { 31 | line[i] = c; 32 | ++i; 33 | } 34 | 35 | line[i] = '\0'; 36 | 37 | return i; 38 | } 39 | -------------------------------------------------------------------------------- /chapter_2/exercise_2_05/any.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define MAXSTR 1000 6 | 7 | int any(char str1[], char str2[]); 8 | 9 | int main(void) { 10 | char str1[MAXSTR] = "xxxabcabc"; 11 | char str2[MAXSTR] = "cbaa"; 12 | 13 | printf("%d", any(str1, str2)); 14 | 15 | return 0; 16 | } 17 | 18 | int any(char str1[], char str2[]) { 19 | int i; 20 | int j; 21 | for (i = 0; str1[i] != '\0'; ++i) { 22 | for (j = 0; str2[j] != '\0'; ++j) { 23 | if (str1[i] == str2[j]) { 24 | return i; 25 | } 26 | } 27 | } 28 | 29 | return -1; 30 | } 31 | 32 | // NOTE: The standard library (string.h), cotains the function strpbrk which 33 | // returns a pointer to the location of the char from the first string. 34 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "C/C++: build and debug active file", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${fileDirname}/${fileBasenameNoExtension}", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${workspaceFolder}", 15 | "environment": [], 16 | "externalConsole": false, 17 | "linux": { 18 | "MIMode": "gdb" 19 | }, 20 | "osx": { 21 | "MIMode": "lldb" 22 | }, 23 | "windows": { 24 | "MIMode": "gdb" 25 | }, 26 | "preLaunchTask": "C/C++: gcc build active file" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_16/file_in.txt: -------------------------------------------------------------------------------- 1 | 1. Throughout human history, people have tried to define intelligence several times. 2 | 2. Some people think about it as the capacity for logic, reasoning, or critical 3 | 3. thinking. Some people have stated that intelligence might be self-awareness, 4 | 4. emotional knowledge, or creativity. Other people think about intelligence as the 5 | 5. capacity for planning and problem-solving. 6 | 6. 7 | 7. Somebody could think about intelligence from a large diversity of perspectives. 8 | 8. Trying to define it is not the most simple type of problem that one could deal 9 | 9. with. It’s natural to start asking some questions about this problem. Why is 10 | 10. intelligence so hard to define?-+ What makes it so sp^ecial? Is even possible to 11 | 11. define what it is? 12 | 10. Intelligence so hard to define?- What makes it so sp^ecial? Is even possible to 13 | 11. define what it is? 14 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_21/entab.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define TAB_LENGTH 8 4 | 5 | int main(void) { 6 | int c; 7 | unsigned int line_pos = 0; 8 | unsigned int nr_of_spaces = 0; 9 | 10 | while ((c = getchar()) != EOF) { 11 | ++line_pos; 12 | 13 | if (c == ' ') { 14 | ++nr_of_spaces; 15 | 16 | if (line_pos % TAB_LENGTH == 0 && nr_of_spaces > 1) { 17 | putchar('\t'); 18 | nr_of_spaces = 0; 19 | } 20 | } else { 21 | while (nr_of_spaces) { 22 | putchar(' '); 23 | --nr_of_spaces; 24 | } 25 | 26 | if (c == '\n') { 27 | line_pos = 0; 28 | } 29 | 30 | putchar(c); 31 | } 32 | } 33 | 34 | return 0; 35 | } 36 | 37 | // NOTE: run: ./entab < file_in.txt > file_out.txt 38 | -------------------------------------------------------------------------------- /chapter_4/exercise_4_13/reverse.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MAXLEN 100 4 | 5 | void reverse(char str[]); 6 | 7 | int main(void) { 8 | char str[MAXLEN] = "This is just a string"; 9 | 10 | printf("%s\n", str); 11 | reverse(str); 12 | printf("%s\n", str); 13 | 14 | return 0; 15 | } 16 | 17 | void reverse(char str[]) { 18 | static int i = 0; 19 | static int j = 0; 20 | 21 | if (str[i] != '\0') { 22 | char c = str[i++]; 23 | reverse(str); 24 | 25 | str[j++] = c; 26 | } 27 | 28 | // if whole reverse process is complete, reset the static variables to make 29 | // this function reusable 30 | if (str[j] == '\0') { 31 | i = 0; 32 | j = 0; 33 | } 34 | } 35 | 36 | // NOTE: As a simple observation when recursive functions are used, static 37 | // variables become handy to construct some useful functionalities. 38 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_03/fahrenheit_celsius.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Print Fahrenheit-Celsius table 4 | // for fahr = 0, 20, ... 300 5 | // floating point version 6 | 7 | int main(void) { 8 | float fahr; 9 | float celsius; 10 | int lower; 11 | int upper; 12 | int step; 13 | 14 | lower = 0; 15 | upper = 300; 16 | step = 20; 17 | 18 | // Printing a heading above the table 19 | printf("Fahr\tCelsius\n"); 20 | printf("---------------\n"); 21 | 22 | fahr = lower; 23 | while (fahr <= upper) { 24 | celsius = (5.0 / 9.0) * (fahr - 32.0); 25 | printf("%3.0f\t\t%6.1f\n", fahr, celsius); 26 | fahr = fahr + step; 27 | } 28 | 29 | return 0; 30 | } 31 | 32 | // NOTE: If all operands from an operation are integers then a integer operation 33 | // is performed. If at least one operand is a floating point number then a 34 | // floating point operation will be performed. 35 | -------------------------------------------------------------------------------- /chapter_4/exercise_4_14/swap.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define swap(t, x, y) \ 4 | { \ 5 | t temp; \ 6 | temp = x; \ 7 | x = y; \ 8 | y = temp; \ 9 | }; 10 | 11 | int main(void) { 12 | int x = 2; 13 | int y = 3; 14 | 15 | printf("x: %d, y: %d\n", x, y); 16 | 17 | swap(int, x, y); 18 | printf("x: %d, y: %d\n", x, y); 19 | 20 | return 0; 21 | } 22 | 23 | // NOTE: A use of a block is very useful because there can be created local 24 | // variables that don't create conflicts with already existed entities. 25 | -------------------------------------------------------------------------------- /chapter_2/exercise_2_02/loop.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MAXLINE 1000 4 | 5 | /* 6 | * K&R Exercise 2-2: Write a loop equivalent to: 7 | * for (i = 0; i < lim - 1 && (c = getchar()) != '\n' && c != EOF; ++i) 8 | * s[i] = c; 9 | * without using && or ||. 10 | * 11 | * Note: Using * instead of && is unsafe because it doesn't short-circuit, 12 | * causing getchar() to be called even when the buffer is full or EOF is 13 | * reached. 14 | */ 15 | 16 | int main(void) { 17 | char s[MAXLINE]; 18 | int c; 19 | int i = 0; 20 | 21 | while (1) { 22 | if (i >= MAXLINE - 1) { 23 | break; 24 | } 25 | 26 | c = getchar(); 27 | 28 | if (c == '\n') { 29 | break; 30 | } else if (c == EOF) { 31 | break; 32 | } else { 33 | s[i++] = c; 34 | } 35 | } 36 | 37 | s[i] = '\0'; 38 | 39 | printf("%s\n", s); 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_21/file_out.txt: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define TAB_LENGTH 8 4 | 5 | int main(void) 6 | { 7 | int c; 8 | int line_pos = 0; 9 | int nr_of_spaces = 0; 10 | 11 | while ((c = getchar()) != EOF) 12 | { 13 | ++line_pos; 14 | 15 | if (c == ' ') 16 | { 17 | ++nr_of_spaces; 18 | 19 | if (line_pos % TAB_LENGTH == 0 && nr_of_spaces > 1) 20 | { 21 | putchar('\t'); 22 | nr_of_spaces = 0; 23 | } 24 | } 25 | else 26 | { 27 | while (nr_of_spaces) 28 | { 29 | putchar(' '); 30 | --nr_of_spaces; 31 | } 32 | 33 | if (c == '\n') 34 | { 35 | line_pos = 0; 36 | } 37 | 38 | putchar(c); 39 | } 40 | } 41 | 42 | return 0; 43 | } 44 | 45 | // NOTE: In a similar fashion with detab, in UNIX like systems you can run: 46 | // ./entab < file_in.txt > file_out.txt which will take the input from the 47 | // file_in.txt it will process it through entab and then it will put the content 48 | // to file_out.txt 49 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_05/celsius_fahrenheit.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Print Celsius-Fahrenheit table 4 | // for celsius = 0, 20, ... 300 5 | // floating point version with for loop 6 | 7 | int main(void) { 8 | float celsius; 9 | float fahr; 10 | int lower; 11 | int upper; 12 | int step; 13 | 14 | lower = 0; 15 | upper = 300; 16 | step = 20; 17 | 18 | printf("Celsius\tFahr\n"); 19 | printf("---------------\n"); 20 | 21 | for (celsius = upper; celsius >= lower; celsius = celsius - step) { 22 | fahr = (9.0 / 5.0) * celsius + 32.0f; 23 | printf("%3.0f\t\t%6.1f\n", celsius, fahr); 24 | } 25 | 26 | return 0; 27 | } 28 | 29 | // NOTE: Sometimes the for loop can be more explicit and more readable then the 30 | // while loop because it's more compact. The initialization and the 31 | // incrementation of the counter variable is done through the for loop params. 32 | // However, the while loop can be, sometimes, more customizable. 33 | -------------------------------------------------------------------------------- /chapter_4/exercise_4_09/getch.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define BUFFSIZE 100 4 | 5 | int getch(void); 6 | void ungetch(int c); 7 | 8 | int main(void) { 9 | int c; 10 | 11 | c = getch(); 12 | putchar(c); 13 | 14 | ungetch(EOF); 15 | 16 | c = getch(); 17 | putchar(c); 18 | 19 | return 0; 20 | } 21 | 22 | int bufp = 0; 23 | int buf[BUFFSIZE]; 24 | 25 | int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar(); } 26 | 27 | void ungetch(int c) { 28 | if (bufp >= BUFFSIZE) { 29 | printf("ungetch: too many characters\n"); 30 | } else { 31 | buf[bufp++] = c; 32 | } 33 | } 34 | 35 | // NOTE: The getch() function can't manage correctly the EOF character because 36 | // in the original K&R version buf was a char array, which can't hold the EOF 37 | // because EOF is -1 and char can't hold negative number. However the gcc 38 | // compiler, on Windows, uses by default signed chars which can hold EOF, but 39 | // this is not platform independent. 40 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_06/getop.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define NUMBER 0 5 | 6 | int getop(char *s); 7 | 8 | int main(void) { 9 | char s[100] = ""; 10 | 11 | int type = getop(s); 12 | 13 | printf("%d ", type); 14 | puts(s); 15 | 16 | return 0; 17 | } 18 | 19 | int getop(char *s) { 20 | char c; 21 | 22 | // Skip blanks (spaces and tabs) 23 | while ((*s = c = getchar()) == ' ' || c == '\t') 24 | ; 25 | 26 | *(s + 1) = '\0'; 27 | 28 | // Not a number 29 | if (!isdigit(c) && c != '.') 30 | return c; 31 | 32 | // Collect the integer part 33 | if (isdigit(c) && c != '.') 34 | while (isdigit(*(++s) = c = getchar())) 35 | ; 36 | 37 | // Collect the fraction part 38 | if (c == '.') { 39 | while (isdigit(*(++s) = c = getchar())) 40 | ; 41 | } 42 | 43 | if (c != EOF) 44 | ungetc(c, stdin); 45 | 46 | *s = '\0'; 47 | 48 | return NUMBER; 49 | } 50 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_20/test.txt: -------------------------------------------------------------------------------- 1 | char **argv 2 | int ( * daytab ) [ 13] 3 | int *daytab[13] 4 | long a_name[ MAX_SIZE ] // This is a comment 5 | void * comp( ) 6 | void (*comp_2)() 7 | char (*(* x() )[])( ) 8 | char (*(*x[ 3 ] ) ( ) ) [ 5 ] 9 | char a /* This is a comment */ 10 | int b 11 | float A_TEST_NAME 12 | float fn_name_test() 13 | 14 | int b 15 | 16 | 17 | 18 | 19 | long f ( ) 20 | void * comp() 21 | long name_0 [ 22 | int t_0 23 | void name_1 ( 24 | int t_2 25 | void name_1 bad 26 | void name_2 ) 27 | float m 28 | custom_type c_t_name_0 29 | invalid_type c_t_name_1 30 | 31 | float fn_test( int a, char name ( * 32 | float fn_0( int a) 33 | float fn_1( int a, float a) 34 | void fn(int a, float b , char long_name ) 35 | void fn_test_2(int a, b, char long_name ) 36 | void fn_test_3(a int , ba ) 37 | void fn_test_4(int ;) 38 | void fn_test_5(int) 39 | void fn_test_6() 40 | void fn_test_7(void) 41 | void fn_test_8(void a, int b, void c_long_name) 42 | void fn_test_9(const int a, const int b, volatile long c) 43 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_18/trailing_blanks.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MAXLINE 1000 4 | 5 | int get_line(char line[], int max_line_len); 6 | void remove_trailing_blanks(char line[], int length); 7 | 8 | int main(void) { 9 | int len; 10 | char line[MAXLINE]; 11 | 12 | while ((len = get_line(line, MAXLINE)) > 0) { 13 | remove_trailing_blanks(line, len); 14 | printf("%s", line); 15 | } 16 | 17 | return 0; 18 | } 19 | 20 | int get_line(char line[], int max_line_len) { 21 | int c; 22 | int i; 23 | 24 | for (i = 0; i < max_line_len - 1 && (c = getchar()) != EOF && c != '\n'; 25 | ++i) { 26 | line[i] = c; 27 | } 28 | 29 | if (c == '\n') { 30 | line[i] = c; 31 | ++i; 32 | } 33 | 34 | line[i] = '\0'; 35 | 36 | return i; 37 | } 38 | 39 | void remove_trailing_blanks(char line[], int length) { 40 | int i; 41 | 42 | for (i = length - 2; line[i] == ' ' || line[i] == '\t'; --i) 43 | ; 44 | 45 | line[i + 1] = '\n'; 46 | line[i + 2] = '\0'; 47 | } 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ohkimur 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /chapter_2/exercise_2_09/bitcount.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void printbits(unsigned int x); 5 | int bitcount(unsigned int x); 6 | 7 | int main(void) { 8 | // Using hexadecimal instead of binary literals (0b...) for C89/C99 9 | // compatibility 0x1A = 0b011010 10 | unsigned int x = 0x1A; 11 | 12 | printbits(x); 13 | printf("x have %d bits of 1.\n", bitcount(x)); 14 | 15 | return 0; 16 | } 17 | 18 | void printbits(unsigned int x) { 19 | unsigned int n = sizeof(unsigned int); 20 | 21 | printf("0b"); 22 | 23 | int i; 24 | for (i = n * 8 - 1; i >= 0; --i) { 25 | (x & (unsigned int)pow(2, i)) ? putchar('1') : putchar('0'); 26 | } 27 | 28 | putchar('\n'); 29 | } 30 | 31 | int bitcount(unsigned int x) { 32 | int b = 0; 33 | 34 | while (x) { 35 | x &= (x - 1); 36 | ++b; 37 | } 38 | 39 | return b; 40 | } 41 | 42 | // NOTE: The expression x &= (x - 1) deletes the rightmost 1-bit of x because 43 | // x is decremented by 1 and masked with the initial x. If decremented x has 44 | // the rightmost bit 1, shifted to right by logic & operation, it is deleted. 45 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_14/frequency_histogram.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define ALPHA_NR 26 4 | #define NUM_NR 10 5 | 6 | int main(void) { 7 | int i; 8 | char chars_freq[ALPHA_NR + NUM_NR]; 9 | 10 | // Initialize the chars_freq array with 0 11 | for (i = 0; i < (ALPHA_NR + NUM_NR); ++i) { 12 | chars_freq[i] = 0; 13 | } 14 | 15 | // Count characters from the standard input 16 | char c; 17 | while ((c = getchar()) != EOF) { 18 | if (c >= 'a' && c <= 'z') { 19 | ++chars_freq[c - 'a']; 20 | } else if (c >= '0' && c <= '9') { 21 | ++chars_freq[c - '0' + ALPHA_NR]; 22 | } 23 | } 24 | 25 | // Print horizontal histogram 26 | for (i = 0; i < (ALPHA_NR + NUM_NR); ++i) { 27 | if (i < ALPHA_NR) { 28 | printf("%c: ", 'a' + i); 29 | } else if (i >= ALPHA_NR) { 30 | printf("%c: ", '0' + i - ALPHA_NR); 31 | } 32 | 33 | int j; 34 | for (j = 0; j < chars_freq[i]; ++j) { 35 | printf("#"); 36 | } 37 | 38 | putchar('\n'); 39 | } 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "cppbuild", 6 | "label": "C/C++: clang build active file", 7 | "command": "/usr/bin/clang", 8 | "args": [ 9 | "-Wall", 10 | "-g", 11 | "${file}", 12 | "-lm", 13 | "-o", 14 | "${fileDirname}/${fileBasenameNoExtension}" 15 | ], 16 | "options": { 17 | "cwd": "${workspaceFolder}" 18 | }, 19 | "problemMatcher": ["$gcc"], 20 | "group": "build", 21 | "detail": "compiler: /usr/bin/clang" 22 | }, 23 | { 24 | "type": "cppbuild", 25 | "label": "C/C++: gcc build active file", 26 | "command": "/usr/bin/gcc", 27 | "args": [ 28 | "-Wall", 29 | "-g", 30 | "${file}", 31 | "-lm", 32 | "-o", 33 | "${fileDirname}/${fileBasenameNoExtension}" 34 | ], 35 | "options": { 36 | "cwd": "${workspaceFolder}" 37 | }, 38 | "problemMatcher": ["$gcc"], 39 | "group": { 40 | "kind": "build", 41 | "isDefault": true 42 | }, 43 | "detail": "compiler: /usr/bin/gcc" 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /chapter_7/exercise_7_01/case.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | typedef enum { false, true } boolean; 7 | 8 | typedef int (*convert_fn_t)(int); 9 | 10 | boolean parse_arg_list(int argc, char *argv[]); 11 | void consume_input(convert_fn_t convert); 12 | 13 | convert_fn_t convert; 14 | 15 | int main(int argc, char *argv[]) { 16 | if (!parse_arg_list(argc, argv)) { 17 | puts("Error: invalid arguments."); 18 | return EXIT_FAILURE; 19 | } 20 | 21 | consume_input(convert); 22 | 23 | return EXIT_SUCCESS; 24 | } 25 | 26 | boolean parse_arg_list(int argc, char *argv[]) { 27 | (void)argc; 28 | if (strcmp(argv[0], "lower") == 0) { 29 | convert = tolower; 30 | return true; 31 | } else if (strcmp(argv[0], "upper") == 0) { 32 | convert = toupper; 33 | return true; 34 | } 35 | 36 | return false; 37 | } 38 | 39 | void consume_input(convert_fn_t convert) { 40 | int c; 41 | while ((c = getc(stdin)) != EOF) { 42 | putc(convert(c), stdout); 43 | } 44 | } 45 | 46 | // NOTE: run: ( exec -a upper ./case < case.c ) 47 | // It is possible to change argv[0] by using the exec command. 48 | -------------------------------------------------------------------------------- /chapter_2/exercise_2_07/invert.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c\n" 4 | #define BYTE_TO_BINARY(byte) \ 5 | (byte & 128 ? '1' : '0'), (byte & 64 ? '1' : '0'), \ 6 | (byte & 32 ? '1' : '0'), (byte & 16 ? '1' : '0'), \ 7 | (byte & 8 ? '1' : '0'), (byte & 4 ? '1' : '0'), \ 8 | (byte & 2 ? '1' : '0'), (byte & 1 ? '1' : '0') 9 | 10 | unsigned int invert(int x, int p, int n); 11 | 12 | int main(void) { 13 | // Using hexadecimal instead of binary literals (0b...) for C89/C99 14 | // compatibility 0xD7 = 0b11010111 15 | unsigned int x = 0xD7; 16 | 17 | printf(BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(x)); 18 | printf(BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(invert(x, 1, 4))); 19 | 20 | return 0; 21 | } 22 | 23 | unsigned int invert(int x, int p, int n) { 24 | ++p; // First position is 0 25 | 26 | // Use ~0U (unsigned) instead of ~0 (signed) to avoid undefined behavior 27 | // when left-shifting. Left-shifting a negative signed value is undefined. 28 | unsigned int mask1 = ~(~0U << n) << p; 29 | unsigned int mask2 = ~mask1 & x; 30 | 31 | return mask2 | ~x; 32 | } 33 | -------------------------------------------------------------------------------- /chapter_8/exercise_8_01/cat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define BUFFER_SIZE 1024 8 | 9 | void error(char *format, ...); 10 | void copy_file(int from, int to); 11 | 12 | int main(int argc, char *argv[]) { 13 | if (argc == 1) { 14 | copy_file(0, 1); 15 | } else { 16 | for (int file_index = 1; file_index < argc; ++file_index) { 17 | int file_descriptor; 18 | if ((file_descriptor = open(argv[file_index], O_RDONLY, 0)) == -1) { 19 | error("Error: could not open the file %s.", argv[file_index]); 20 | exit(EXIT_FAILURE); 21 | } 22 | 23 | copy_file(file_descriptor, 1); 24 | } 25 | } 26 | 27 | exit(EXIT_SUCCESS); 28 | } 29 | 30 | void copy_file(int from, int to) { 31 | char buffer[BUFFER_SIZE]; 32 | 33 | int n; 34 | while ((n = read(from, buffer, BUFFER_SIZE)) > 0) { 35 | write(to, buffer, n); 36 | } 37 | } 38 | 39 | void error(char *format, ...) { 40 | va_list arg_p; 41 | 42 | va_start(arg_p, format); 43 | fprintf(stderr, "Error: "); 44 | vfprintf(stderr, format, arg_p); 45 | fprintf(stderr, "\n"); 46 | va_end(arg_p); 47 | 48 | exit(EXIT_FAILURE); 49 | } 50 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_16/longest_line.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MAXLINE 1000 4 | 5 | int get_line(char line[], int maxline); 6 | void copy(char from[], char to[]); 7 | 8 | int main(void) { 9 | int len; 10 | char line[MAXLINE]; 11 | 12 | int maxlen; 13 | char maxline[MAXLINE]; 14 | 15 | maxlen = 0; 16 | while ((len = get_line(line, MAXLINE)) > 0) { 17 | if (maxlen < len) { 18 | maxlen = len; 19 | copy(line, maxline); 20 | } 21 | 22 | printf("line_length: %d\n", len); 23 | } 24 | 25 | if (maxlen > 0) { 26 | printf("%s", maxline); 27 | } 28 | 29 | return 0; 30 | } 31 | 32 | int get_line(char line[], int maxline) { 33 | int c; 34 | int i; 35 | 36 | for (i = 0; i < maxline - 1 && (c = getchar()) != EOF && c != '\n'; ++i) { 37 | line[i] = c; 38 | } 39 | if (c == '\n') { 40 | line[i] = c; 41 | ++i; 42 | } 43 | 44 | line[i] = '\0'; 45 | 46 | while (c != EOF && c != '\n') { 47 | ++i; 48 | c = getchar(); 49 | } 50 | 51 | if (c == '\n') 52 | ++i; 53 | 54 | return i; 55 | } 56 | 57 | void copy(char from[], char to[]) { 58 | int i = 0; 59 | 60 | while ((to[i] = from[i]) != '\0') { 61 | ++i; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_04/strend.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int strend(char *s, char *t); 5 | 6 | int main(void) { 7 | char *s = "This si a simple string"; 8 | char *t1 = "string"; 9 | char *t2 = "random string"; 10 | 11 | // Test if the string t1 occurs at the end of string s. 12 | if (strend(s, t1)) 13 | puts("The string t1 orrurs at the end of the string s."); 14 | else 15 | puts("The string t1 doesn't orrur at the end of the string s."); 16 | 17 | // Test if the string t2 occurs at the end of string s. 18 | if (strend(s, t2)) 19 | puts("The string t2 orrurs at the end of the string s."); 20 | else 21 | puts("The string t2 doesn't orrur at the end of the string s."); 22 | 23 | return 0; 24 | } 25 | 26 | // Returns 1 if the string t occurs at the end of the string s, and zero 27 | // otherwise. 28 | int strend(char *s, char *t) { 29 | // Determine the lengths of the strings. 30 | size_t s_length = strlen(s); 31 | size_t t_length = strlen(t); 32 | 33 | // Move the s & t pointer to the end of the corresponding strings. 34 | s += s_length - 1; 35 | t += t_length - 1; 36 | 37 | // Check backwards if each character from string t occurs in the 38 | // corresonding location from the string s. 39 | while (*s-- == *t--) 40 | --t_length; 41 | 42 | return !t_length; 43 | } 44 | -------------------------------------------------------------------------------- /chapter_4/exercise_4_07/ungets.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define MAXLEN 1000 5 | #define BUFFSIZE 1000 6 | 7 | int getstr(char line[], int limit); 8 | void ungetstr(char line[]); 9 | 10 | int main(void) { 11 | char line[MAXLEN]; 12 | char temp[MAXLEN]; 13 | 14 | getstr(line, MAXLEN); 15 | printf("%s", line); 16 | 17 | ungetstr(line); 18 | 19 | getstr(temp, MAXLEN); 20 | printf("%s", temp); 21 | 22 | return 0; 23 | } 24 | 25 | int getch(void); 26 | void ungetch(int c); 27 | 28 | int getstr(char line[], int limit) { 29 | int i = 0; 30 | int c; 31 | 32 | while (limit - 1 > 0 && (c = getch()) != EOF && c != '\n') { 33 | line[i++] = c; 34 | } 35 | 36 | if (c == '\n') { 37 | line[i++] = c; 38 | } 39 | 40 | line[i] = '\0'; 41 | 42 | return i; 43 | } 44 | 45 | void ungetstr(char line[]) { 46 | int i = strlen(line); 47 | 48 | while (i) { 49 | ungetch(line[--i]); 50 | } 51 | } 52 | 53 | int bufp = 0; 54 | char buf[BUFFSIZE]; 55 | 56 | int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar(); } 57 | 58 | void ungetch(int c) { 59 | if (bufp >= BUFFSIZE) { 60 | printf("ungetch: too many characters\n"); 61 | } else { 62 | buf[bufp++] = c; 63 | } 64 | } 65 | 66 | // NOTE: The ungetstr() function doesn't need access to buf and bufp. It is 67 | // enough just to use the ungetch() function. 68 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_21/file_in.txt: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define TAB_LENGTH 8 4 | 5 | int main(void) 6 | { 7 | int c; 8 | int line_pos = 0; 9 | int nr_of_spaces = 0; 10 | 11 | while ((c = getchar()) != EOF) 12 | { 13 | ++line_pos; 14 | 15 | if (c == ' ') 16 | { 17 | ++nr_of_spaces; 18 | 19 | if (line_pos % TAB_LENGTH == 0 && nr_of_spaces > 1) 20 | { 21 | putchar('\t'); 22 | nr_of_spaces = 0; 23 | } 24 | } 25 | else 26 | { 27 | while (nr_of_spaces) 28 | { 29 | putchar(' '); 30 | --nr_of_spaces; 31 | } 32 | 33 | if (c == '\n') 34 | { 35 | line_pos = 0; 36 | } 37 | 38 | putchar(c); 39 | } 40 | } 41 | 42 | return 0; 43 | } 44 | 45 | // NOTE: In a similar fashion with detab, in UNIX like systems you can run: 46 | // ./entab < file_in.txt > file_out.txt which will take the input from the 47 | // file_in.txt it will process it through entab and then it will put the content 48 | // to file_out.txt 49 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_05/strncmp.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int strcmp_ptr(char *s, char *t, size_t n); 4 | 5 | int main(void) { 6 | char s[100] = "This is the first string"; 7 | char *t = "This is the second string"; 8 | size_t nr_chars = 13; 9 | 10 | int is_equal = strcmp_ptr(s, t, nr_chars); 11 | 12 | if (is_equal == 0) { 13 | puts("String s is equal with string t."); 14 | } else if (is_equal > 0) { 15 | puts("String s contains more chars than string t."); 16 | } else if (is_equal < 0) { 17 | puts("String s contains less chars than string t."); 18 | } 19 | 20 | return 0; 21 | } 22 | 23 | // Return <0 if s0 if s>t *1 24 | int strcmp_ptr(char *s, char *t, size_t n) { 25 | while ((*s == *t) && --n) { 26 | if (*s == '\0') 27 | return 0; 28 | 29 | ++s; 30 | ++t; 31 | } 32 | 33 | // If the s string contains more characters than t, then the t char will 34 | // become '\0' before s char. If this happen then the s char will be its 35 | // ascii value and t char will be 0, so the final result will be 36 | // s_ascii_value - 0. 37 | 38 | // If the t string contains more character than s, then the s char will 39 | // become '\0' before t char. If this happen then the s char will be 0 and 40 | // t char will be whatever ascii_value is holding, so the final result will 41 | // be 0 - t_ascii_value. 42 | 43 | return *s - *t; 44 | } 45 | -------------------------------------------------------------------------------- /chapter_4/exercise_4_01/strindex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define MAXLEN 1000 5 | 6 | int get_line(char line[], int lim); 7 | int strindex(char source[], char pattern[]); 8 | 9 | int main(void) { 10 | char line[MAXLEN]; 11 | char pattern[MAXLEN] = "example"; 12 | 13 | get_line(line, MAXLEN); 14 | printf("%s\n", line); 15 | 16 | printf("Pattern found at index %d\n", strindex(line, pattern)); 17 | 18 | return 0; 19 | } 20 | 21 | int get_line(char line[], int lim) { 22 | int i = 0; 23 | int c; 24 | 25 | while (lim > 0 && (c = getchar()) != EOF && c != '\n') { 26 | line[i++] = c; 27 | --lim; 28 | } 29 | 30 | if (c == '\n') { 31 | line[i++] = c; 32 | } 33 | 34 | line[i] = '\0'; 35 | 36 | return i; 37 | } 38 | 39 | int strindex(char source[], char pattern[]) { 40 | int i; 41 | int j; 42 | int k; 43 | 44 | printf("line len: %lu\n", strlen(source)); 45 | 46 | for (i = strlen(source); i >= 0; --i) { 47 | for (j = i, k = 0; pattern[k] != '\0' && source[j] == pattern[k]; 48 | ++j, ++k) 49 | ; 50 | 51 | if (k > 0 && pattern[k] == '\0') { 52 | return i; 53 | } 54 | } 55 | 56 | return -1; 57 | } 58 | 59 | // NOTE: It is simple to find the rightmost string pattern in the initial string 60 | // if we search for the pattern by iterating the initial string from the end to 61 | // the begining. 62 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_15/file_in.txt: -------------------------------------------------------------------------------- 1 | Throughout human history, people have tried to define intelligence several times. 2 | Some people think about it as the capacity for logic, reasoning, or critical 3 | thinking. Some people have stated that intelligence might be self-awareness, 4 | emotional knowledge, or creativity. Other people think about intelligence as the 5 | capacity for planning and problem-solving. 6 | 7 | Somebody could think about intelligence from a large diversity of perspectives. 8 | Trying to define it is not the most simple type of problem that one could deal 9 | with. It’s natural to start asking some questions about this problem. Why is 10 | intelligence so hard to define? What makes it so special? Is even possible to 11 | define what it is? 12 | 13 | throughout human history, people have tried to define intelligence several times. 14 | some people think about it as the capacity for logic, reasoning, or critical 15 | thinking. some people have stated that intelligence might be self-awareness, 16 | emotional knowledge, or creativity. other people think about intelligence as the 17 | capacity for planning and problem-solving. 18 | 19 | somebody could think about intelligence from a large diversity of perspectives. 20 | trying to define it is not the most simple type of problem that one could deal 21 | with. it’s natural to start asking some questions about this problem. why is 22 | intelligence so hard to define? what makes it so special? is even possible to 23 | define what it is? 24 | -------------------------------------------------------------------------------- /chapter_3/exercise_3_06/itoa.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAXLEN 1000 7 | 8 | int ilen(int a); 9 | void int_to_array(int n, char s[], int padding); 10 | void str_reverse(char str1[], int index, int size); 11 | 12 | int main(void) { 13 | int n = 1995; 14 | char number_str[MAXLEN]; 15 | 16 | int_to_array(n, number_str, 6); 17 | printf("%s\n", number_str); 18 | 19 | return 0; 20 | } 21 | 22 | int ilen(int a) { 23 | int i = 0; 24 | 25 | do { 26 | ++i; 27 | } while (a /= 10); 28 | 29 | return i; 30 | } 31 | 32 | void int_to_array(int n, char s[], int padding) { 33 | int i = 0; 34 | int sign = n; 35 | 36 | do { 37 | s[i++] = abs(n % 10) + '0'; 38 | } while (n /= 10); 39 | 40 | if (sign < 0) { 41 | s[i++] = '-'; 42 | } 43 | 44 | int len = ilen(sign); 45 | while (len < padding) { 46 | s[i++] = ' '; 47 | --padding; 48 | } 49 | 50 | s[i] = '\0'; 51 | 52 | int s_len = strlen(s); 53 | str_reverse(s, 0, s_len - 1); 54 | } 55 | 56 | void str_reverse(char str1[], int index, int size) { 57 | char temp; 58 | 59 | temp = str1[index]; 60 | str1[index] = str1[size - index]; 61 | str1[size - index] = temp; 62 | 63 | if (index == size / 2) { 64 | return; 65 | } 66 | 67 | str_reverse(str1, index + 1, size); 68 | } 69 | -------------------------------------------------------------------------------- /chapter_3/exercise_3_04/itoa.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAXLEN 1000 7 | 8 | void int_to_array(int n, char s[]); 9 | void str_reverse(char str1[], int index, int size); 10 | 11 | int main(void) { 12 | int n = 785; 13 | char number_str[MAXLEN]; 14 | 15 | int_to_array(n, number_str); 16 | printf("%s\n", number_str); 17 | 18 | return 0; 19 | } 20 | 21 | void int_to_array(int n, char s[]) { 22 | int i; 23 | int sign; 24 | 25 | sign = n; 26 | 27 | i = 0; 28 | do { 29 | s[i++] = abs(n % 10) + '0'; 30 | } while (n /= 10); 31 | 32 | if (sign < 0) { 33 | s[i++] = '-'; 34 | } 35 | 36 | s[i] = '\0'; 37 | 38 | int s_len = strlen(s); 39 | str_reverse(s, 0, s_len - 1); 40 | } 41 | 42 | void str_reverse(char str1[], int index, int size) { 43 | char temp; 44 | 45 | temp = str1[index]; 46 | str1[index] = str1[size - index]; 47 | str1[size - index] = temp; 48 | 49 | if (index == size / 2) { 50 | return; 51 | } 52 | 53 | str_reverse(str1, index + 1, size); 54 | } 55 | 56 | // NOTE: It does not handle the smallest negative number because it does not 57 | // have a positive equivalent. This means that just using a expression like n = 58 | // -n is not enough for the smallest negative number. If we take the absolute 59 | // value of n % 10 we get the correct value and character. 60 | -------------------------------------------------------------------------------- /chapter_2/exercise_2_06/setbits.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c\n" 4 | #define BYTE_TO_BINARY(byte) \ 5 | (byte & 128 ? '1' : '0'), (byte & 64 ? '1' : '0'), \ 6 | (byte & 32 ? '1' : '0'), (byte & 16 ? '1' : '0'), \ 7 | (byte & 8 ? '1' : '0'), (byte & 4 ? '1' : '0'), \ 8 | (byte & 2 ? '1' : '0'), (byte & 1 ? '1' : '0') 9 | 10 | unsigned int setbits(int x, int p, int n, int y); 11 | 12 | int main(void) { 13 | // Using hexadecimal instead of binary literals (0b...) for C89/C99 14 | // compatibility 0xFF = 0b11111111, 0x06 = 0b0110 15 | unsigned int x = 0xFF; 16 | unsigned int y = 0x06; 17 | 18 | printf(BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(x)); 19 | printf(BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(setbits(x, 2, 4, y))); 20 | 21 | return 0; 22 | } 23 | 24 | unsigned int setbits(int x, int p, int n, int y) { 25 | ++p; // First position is 0 26 | 27 | // Use ~0U (unsigned) instead of ~0 (signed) to avoid undefined behavior 28 | // when left-shifting. Left-shifting a negative signed value is undefined. 29 | unsigned int mask1 = (~(~(~0U << n) << p) & x); 30 | unsigned int mask2 = (~(~0U << n) & y) << p; 31 | 32 | return mask1 | mask2; 33 | } 34 | 35 | // NOTE: Masking is a very good technique to work with bits. We can think about 36 | // logic AND as a multiply and for OR as an addition. 37 | -------------------------------------------------------------------------------- /chapter_2/exercise_2_08/rightrot.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void printbin(unsigned int x); 5 | unsigned int rightrot(unsigned int x, unsigned int n); 6 | 7 | int main(void) { 8 | // Using hexadecimal instead of binary literals (0b...) for C89/C99 9 | // compatibility 0xF5 = 0b11110101 10 | unsigned int x = 0xF5; 11 | 12 | printbin(x); 13 | printbin(rightrot(x, 5)); 14 | 15 | return 0; 16 | } 17 | 18 | void printbin(unsigned int x) { 19 | unsigned int n = sizeof(unsigned int); 20 | 21 | printf("0b"); 22 | 23 | int i; 24 | for (i = n * 8 - 1; i >= 0; --i) { 25 | (x & (unsigned int)pow(2, i)) ? putchar('1') : putchar('0'); 26 | } 27 | 28 | putchar('\n'); 29 | } 30 | 31 | unsigned int rightrot(unsigned int x, unsigned int n) { 32 | unsigned int msb_1 = ~(~(unsigned)0 >> 1); 33 | 34 | // Using unsigned int for i to match the unsigned n parameter 35 | // This prevents sign comparison warnings 36 | unsigned int i; 37 | for (i = 0; i < n; ++i) { 38 | if (x & 1) { 39 | x = (x >> 1) | msb_1; 40 | } else { 41 | x = (x >> 1); 42 | } 43 | } 44 | 45 | return x; 46 | } 47 | 48 | // NOTE: The rightrot function rotate the entire unsigned int var and if we 49 | // print just a byte we can't see all bits. In order to print all the bits from 50 | // an unsigned int we need to determine the size of an unsigned int, wich is 51 | // machine dependent, and then print 0 or 1 to the output using powers of 2. 52 | -------------------------------------------------------------------------------- /chapter_8/exercise_8_02/syscalls.h: -------------------------------------------------------------------------------- 1 | #define NULL 0 2 | 3 | #define EOF (-1) 4 | #define BUFFER_SIZE 1024 5 | #define MAX_NR_OF_OPEN_FILES 20 6 | 7 | #define EXIT_FAILURE 1 8 | #define EXIT_SUCCESS 0 9 | 10 | struct _io_buffer_file { 11 | int counter; 12 | char *next_char_pos_p; 13 | char *base; 14 | struct { 15 | // Using unsigned int for bit-fields prevents warnings about 16 | // implicit truncation when assigning 1 to a signed bit-field 17 | unsigned int _READ : 1; 18 | unsigned int _WRITE : 1; 19 | unsigned int _UNBUF : 1; 20 | unsigned int _EOF : 1; 21 | unsigned int _ERR : 1; 22 | } flag; 23 | int file_descriptor; 24 | }; 25 | 26 | typedef struct _io_buffer_file FILE; 27 | 28 | extern FILE _io_buffer[MAX_NR_OF_OPEN_FILES]; 29 | 30 | int _fill_buffer(FILE *); 31 | int _flush_buffer(int c, FILE *); 32 | 33 | #define stdin (&_io_buffer[0]) 34 | #define stdout (&_io_buffer[1]) 35 | #define stderr (&_io_buffer[2]) 36 | 37 | #define feof(p) ((p->flag & _EOF) != 0) 38 | #define ferror(p) ((p->flag & _ERR) != 0) 39 | #define fileno(p) ((p->file_descriptor) 40 | 41 | #define getc(p) \ 42 | ((--p->counter >= 0) ? (unsigned char)*(p)->next_char_pos_p++ \ 43 | : _fill_buffer(p)) 44 | #define putc(x, p) \ 45 | ((--p->counter >= 0) ? *p->next_char_pos_p++ = x : _flush_buffer(x, p)) 46 | 47 | #define getchar() getc(stdin) 48 | #define putchar(x) putc(x, stdout) 49 | -------------------------------------------------------------------------------- /chapter_2/exercise_2_01/types_ranges.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(void) { 5 | printf("#################### CHAR #####################\n"); 6 | printf("bits: %d\n", CHAR_BIT); 7 | printf("unsigned char max: %d\n", UCHAR_MAX); 8 | printf("signed char min: %d\n", SCHAR_MIN); 9 | printf("signed char max: %d\n", SCHAR_MAX); 10 | printf("\n"); 11 | 12 | printf("##################### INT #####################\n"); 13 | printf("unsigned int max: %u\n", UINT_MAX); 14 | printf("signed int min: %d\n", INT_MIN); 15 | printf("signed int max: %d\n", INT_MAX); 16 | printf("\n"); 17 | 18 | printf("################## SHORT INT ##################\n"); 19 | printf("unsigned short int max: %u\n", USHRT_MAX); 20 | printf("signed short int min: %d\n", SHRT_MIN); 21 | printf("signed short int max: %d\n", SHRT_MAX); 22 | printf("\n"); 23 | 24 | printf("################## LONG INT ###################\n"); 25 | printf("unsigned long int max: %lu\n", ULONG_MAX); 26 | printf("signed long int min: %ld\n", LONG_MIN); 27 | printf("signed long int max: %ld\n", LONG_MAX); 28 | printf("\n"); 29 | 30 | printf("################ LONG LONG INT #################\n"); 31 | printf("unsigned long long int max: %llu\n", ULLONG_MAX); 32 | printf("signed long long int min: %lld\n", LLONG_MIN); 33 | printf("signed long long int max: %lld\n", LLONG_MAX); 34 | printf("\n"); 35 | 36 | return 0; 37 | } 38 | 39 | // NOTE: The limits.h header contains all the necessary constants machine 40 | // dependent for types sizes. 41 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_19/reverse.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MAXLINE 1000 4 | 5 | int get_line(char line[], int max_line_len); 6 | int length(char line[]); 7 | void reverse(char line[]); 8 | 9 | int main(void) { 10 | int len; 11 | char line[MAXLINE]; 12 | 13 | while ((len = get_line(line, MAXLINE)) > 0) { 14 | reverse(line); 15 | printf("%s", line); 16 | } 17 | 18 | return 0; 19 | } 20 | 21 | int get_line(char line[], int max_line_len) { 22 | int c; 23 | int i; 24 | 25 | i = 0; 26 | while (i < max_line_len - 1 && (c = getchar()) != EOF && c != '\n') { 27 | line[i] = c; 28 | ++i; 29 | } 30 | 31 | // flush out input stream if exceeding max_line_len limit 32 | while (i >= max_line_len - 1 && (c = getchar()) != '\n') 33 | ; 34 | 35 | if (c == '\n') { 36 | line[i] = '\n'; 37 | ++i; 38 | } 39 | 40 | line[i] = '\0'; 41 | 42 | return i; 43 | } 44 | 45 | int length(char line[]) { 46 | int i; 47 | 48 | for (i = 0; line[i] != '\0'; ++i) 49 | ; 50 | 51 | return i; 52 | } 53 | 54 | void reverse(char line[]) { 55 | int i_front = 0; 56 | int i_back = length(line); 57 | char temp; 58 | 59 | if (line[i_back - 1] == '\n') { 60 | i_back -= 2; 61 | } else { 62 | i_back -= 1; 63 | } 64 | 65 | while (i_back > i_front) { 66 | temp = line[i_front]; 67 | line[i_front] = line[i_back]; 68 | line[i_back] = temp; 69 | 70 | ++i_front; 71 | --i_back; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /chapter_7/exercise_7_06/compare.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define MAX_LINE_LEN 1000 6 | 7 | int parse_arg_list(int argc, char *argv[]); 8 | 9 | int main(int argc, char *argv[]) { 10 | if (!parse_arg_list(argc, argv)) { 11 | fprintf(stderr, "Error: invalid arguments.\n"); 12 | exit(EXIT_FAILURE); 13 | } 14 | 15 | char *program_name = argv[0]; 16 | 17 | FILE *file_1; 18 | FILE *file_2; 19 | 20 | if ((file_1 = fopen(argv[1], "r")) == NULL) { 21 | fprintf(stderr, "%s: can't open %s.\n", program_name, argv[1]); 22 | exit(EXIT_FAILURE); 23 | } 24 | 25 | if ((file_2 = fopen(argv[2], "r")) == NULL) { 26 | fprintf(stderr, "%s: can't open %s.\n", program_name, argv[2]); 27 | exit(EXIT_FAILURE); 28 | } 29 | 30 | char line_1[MAX_LINE_LEN]; 31 | char line_2[MAX_LINE_LEN]; 32 | 33 | size_t line_number = 1; 34 | while (fgets(line_1, MAX_LINE_LEN, file_1) != NULL && 35 | fgets(line_2, MAX_LINE_LEN, file_2) != NULL) { 36 | if (strcmp(line_1, line_2) != 0) { 37 | printf("%s [%zu]: %s", argv[1], line_number, line_1); 38 | fclose(file_1); 39 | 40 | printf("%s [%zu]: %s", argv[2], line_number, line_2); 41 | fclose(file_2); 42 | break; 43 | } 44 | 45 | ++line_number; 46 | } 47 | 48 | exit(EXIT_SUCCESS); 49 | } 50 | 51 | int parse_arg_list(int argc, char *argv[]) { 52 | (void)argv; 53 | if (argc != 3) { 54 | return 0; 55 | } 56 | 57 | return 1; 58 | } 59 | 60 | // NOTE: run: ./compare file_1.txt file_2.txt 61 | -------------------------------------------------------------------------------- /chapter_7/exercise_7_08/print.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define MAX_LINE_LEN 1000 6 | #define LINES_PER_PAGE 10 7 | 8 | typedef enum { false, true } boolean; 9 | 10 | boolean parse_arg_list(int argc, char *argv[]); 11 | void print_file(char *file_name); 12 | 13 | char *program_name; 14 | 15 | int main(int argc, char *argv[]) { 16 | if (!parse_arg_list(argc, argv)) { 17 | exit(EXIT_FAILURE); 18 | } 19 | 20 | while (--argc > 0) { 21 | print_file(*++argv); 22 | 23 | if (argc != 1) { 24 | putc('\n', stdout); 25 | } 26 | } 27 | 28 | exit(EXIT_SUCCESS); 29 | } 30 | 31 | boolean parse_arg_list(int argc, char *argv[]) { 32 | const char *program_name = argv[0]; 33 | 34 | if (argc < 2) { 35 | fprintf(stderr, "Usage: %s [FILE]...\n", program_name); 36 | return false; 37 | } 38 | 39 | return true; 40 | } 41 | 42 | void print_file(char *file_name) { 43 | FILE *file_p; 44 | if ((file_p = fopen(file_name, "r")) == NULL) { 45 | fprintf(stderr, "%s: can't open %s.\n", program_name, file_name); 46 | exit(EXIT_FAILURE); 47 | } 48 | 49 | size_t line_number = 1; 50 | char line[MAX_LINE_LEN]; 51 | while (fgets(line, MAX_LINE_LEN, file_p) != NULL) { 52 | if ((line_number - 1) % LINES_PER_PAGE == 0) { 53 | printf("[%s]: page %zu\n", file_name, 54 | line_number / LINES_PER_PAGE + 1); 55 | } 56 | 57 | printf("%zu: %s", line_number, line); 58 | ++line_number; 59 | } 60 | } 61 | 62 | // NOTE: run: ./print ../exercise_7_7/file_1.txt ../exercise_7_7/file_2.txt 63 | // ./print.c 64 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_02/getfloat.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define MAXLEN 1000 6 | #define BUFFSIZE 100 7 | 8 | int getch(void); 9 | void ungetch(int c); 10 | int getfloat(float *pn); 11 | 12 | int main(void) { 13 | float number = 0.0; 14 | 15 | getfloat(&number); 16 | printf("number: %f\n", number); 17 | 18 | return 0; 19 | } 20 | 21 | int bufp = 0; 22 | int buf[BUFFSIZE]; 23 | 24 | int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar(); } 25 | 26 | void ungetch(int c) { 27 | if (bufp >= BUFFSIZE) { 28 | printf("ungetch: too many characters\n"); 29 | } else { 30 | buf[bufp++] = c; 31 | } 32 | } 33 | 34 | int getfloat(float *pn) { 35 | int c; 36 | int sign; 37 | 38 | while (isspace(c = getch())) 39 | ; 40 | 41 | if (!isdigit(c) && c != EOF && c != '+' && c != '-' && c != '.') { 42 | ungetch(c); 43 | return 0; 44 | } 45 | 46 | sign = (c == '-') ? -1 : 1; 47 | 48 | if (c == '+' || c == '-') { 49 | if (!isdigit(c = getch())) { 50 | ungetch(c); 51 | ungetch(sign == 1 ? '+' : '-'); 52 | return 0; 53 | } 54 | } 55 | 56 | for (*pn = 0; isdigit(c); c = getch()) { 57 | *pn = 10 * *pn + (c - '0'); 58 | } 59 | 60 | if (c == '.') { 61 | int i; 62 | for (i = 1; (c = getch()) && isdigit(c); ++i) { 63 | *pn += (c - '0') / (pow(10, i)); 64 | } 65 | } 66 | 67 | *pn = *pn * sign; 68 | 69 | if (c != EOF) { 70 | ungetch(c); 71 | } 72 | 73 | return c; 74 | } 75 | 76 | // NOTE: The getfloat() function should return an integer like getint(). 77 | -------------------------------------------------------------------------------- /chapter_8/exercise_8_03/syscalls.h: -------------------------------------------------------------------------------- 1 | #define NULL 0 2 | 3 | #define EOF (-1) 4 | #define BUFFER_SIZE 1024 5 | #define MAX_NR_OF_OPEN_FILES 20 6 | 7 | #define EXIT_FAILURE 1 8 | #define EXIT_SUCCESS 0 9 | 10 | struct _io_buffer_file { 11 | int counter; 12 | char *next_char_pos_p; 13 | char *base; 14 | struct { 15 | // Using unsigned int for bit-fields prevents warnings about 16 | // implicit truncation when assigning 1 to a signed bit-field 17 | unsigned int _READ : 1; 18 | unsigned int _WRITE : 1; 19 | unsigned int _UNBUF : 1; 20 | unsigned int _EOF : 1; 21 | unsigned int _ERR : 1; 22 | } flag; 23 | int file_descriptor; 24 | }; 25 | 26 | typedef struct _io_buffer_file FILE; 27 | 28 | extern FILE _io_buffer[MAX_NR_OF_OPEN_FILES]; 29 | 30 | int _fill_buffer(FILE *); 31 | int _flush_buffer(int c, FILE *file_p); 32 | int file_flush(FILE *file_p); 33 | FILE *file_open(char *name, char *mode); 34 | int file_close(FILE *file_p); 35 | 36 | #define stdin (&_io_buffer[0]) 37 | #define stdout (&_io_buffer[1]) 38 | #define stderr (&_io_buffer[2]) 39 | 40 | #define feof(p) ((p->flag & _EOF) != 0) 41 | #define ferror(p) ((p->flag & _ERR) != 0) 42 | #define fileno(p) ((p->file_descriptor) 43 | 44 | #define getc(p) \ 45 | ((--p->counter >= 0) ? (unsigned char)*(p)->next_char_pos_p++ \ 46 | : _fill_buffer(p)) 47 | #define putc(x, p) \ 48 | ((--p->counter >= 0) ? *p->next_char_pos_p++ = x : _flush_buffer(x, p)) 49 | 50 | #define getchar() getc(stdin) 51 | #define putchar(x) putc(x, stdout) 52 | -------------------------------------------------------------------------------- /chapter_3/exercise_3_05/itob.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define MAXLEN 100 6 | 7 | char itoc(int a); 8 | void itob(int n, char s[], int b); 9 | void str_reverse(char str1[], int index, int size); 10 | 11 | int main(void) { 12 | char s[MAXLEN]; 13 | 14 | int n = -2; 15 | 16 | itob(n, s, 16); 17 | 18 | printf("%s", s); 19 | 20 | return 0; 21 | } 22 | 23 | char itoc(int a) { 24 | if (a <= 9) { 25 | return a + '0'; 26 | } 27 | 28 | return a + 'a' - 10; 29 | } 30 | 31 | void itob(int n, char s[], int b) { 32 | int i = 0; 33 | int sign = n; 34 | 35 | do { 36 | s[i++] = itoc(abs(n) % b); 37 | n /= b; 38 | } while (n); 39 | 40 | switch (b) { 41 | case 2: 42 | s[i++] = 'b'; 43 | s[i++] = '0'; 44 | break; 45 | 46 | case 16: 47 | s[i++] = 'x'; 48 | s[i++] = '0'; 49 | break; 50 | } 51 | 52 | if (sign < 0) { 53 | s[i++] = '-'; 54 | } 55 | 56 | s[i] = '\0'; 57 | 58 | int s_len = strlen(s); 59 | str_reverse(s, 0, s_len - 1); 60 | } 61 | 62 | void str_reverse(char str1[], int index, int size) { 63 | char temp; 64 | 65 | temp = str1[index]; 66 | str1[index] = str1[size - index]; 67 | str1[size - index] = temp; 68 | 69 | if (index == size / 2) { 70 | return; 71 | } 72 | 73 | str_reverse(str1, index + 1, size); 74 | } 75 | 76 | // NOTE: It is simple to convert an integer variable to any base just by taking 77 | // the remainder of the number divided by base and then dividing the initial 78 | // number by base. The result need to be reversed to be correct. 79 | -------------------------------------------------------------------------------- /chapter_3/exercise_3_03/expand.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define MAXLEN 10000 5 | 6 | int get_str(char str[], int limit); 7 | void expand(char src[], char dest[]); 8 | 9 | int main(void) { 10 | char str[MAXLEN]; 11 | char expanded_str[MAXLEN]; 12 | 13 | get_str(str, MAXLEN); 14 | expand(str, expanded_str); 15 | printf("%s", expanded_str); 16 | 17 | return 0; 18 | } 19 | 20 | int get_str(char str[], int limit) { 21 | int c; 22 | int i = 0; 23 | 24 | while (i < limit - 1 && (c = getchar()) != EOF) { 25 | str[i++] = c; 26 | } 27 | str[i] = '\0'; 28 | 29 | return i; 30 | } 31 | 32 | void expand(char src[], char dest[]) { 33 | /** 34 | * a-z 35 | * a-b-c 36 | * a-c-h-v 37 | * a-c-b-v 38 | * 0-9 39 | * 1-5 40 | * a-zA-Z 41 | * 0-9a-zA-Z 42 | * -a-z 43 | * a-z- 44 | * -a-z- 45 | */ 46 | int i; 47 | int j = 0; 48 | for (i = 0; i < MAXLEN - 1 && j < MAXLEN - 1 && src[i] != EOF; ++i) { 49 | if (isalnum(src[i]) && src[i + 1] == '-' && src[i] < src[i + 2]) { 50 | do { 51 | int k; 52 | for (k = 0; k <= (src[i + 2] - src[i]); ++k) { 53 | int temp = src[i] + k; 54 | if (dest[j - 1] != temp && 55 | (isdigit(temp) || isalpha(temp))) { 56 | dest[j++] = temp; 57 | } 58 | } 59 | 60 | i += 2; 61 | } while (isalnum(src[i]) && src[i + 1] == '-' && 62 | src[i] < src[i + 2]); 63 | } else { 64 | dest[j++] = src[i]; 65 | } 66 | } 67 | dest[j] = '\0'; 68 | } 69 | -------------------------------------------------------------------------------- /chapter_7/exercise_7_02/print.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAX_LINE_LEN 80 7 | #define OFFSET 10 8 | 9 | typedef enum { false, true } boolean; 10 | 11 | boolean parse_arg_list(int argc, char *argv[]); 12 | int is_ascii(int c); 13 | 14 | boolean octal = true; 15 | 16 | int main(int argc, char *argv[]) { 17 | if (!parse_arg_list(argc, argv)) { 18 | puts("Error: invalid arguments."); 19 | return EXIT_FAILURE; 20 | } 21 | 22 | int c; 23 | size_t col_pos = 1; 24 | while ((c = getc(stdin)) != EOF) { 25 | if (is_ascii(c)) { 26 | if (c == '\n') { 27 | c = ' '; 28 | } 29 | 30 | putc(c, stdout); 31 | ++col_pos; 32 | } else { 33 | if (octal) { 34 | col_pos += printf("\\%o", c) - 1; 35 | } else { 36 | col_pos += printf("\\%x", c) - 1; 37 | } 38 | } 39 | 40 | if (col_pos >= MAX_LINE_LEN - OFFSET) { 41 | if (isblank(c)) { 42 | col_pos = 1; 43 | putc('\n', stdout); 44 | } 45 | } 46 | } 47 | putc('\n', stdout); 48 | 49 | return EXIT_SUCCESS; 50 | } 51 | 52 | boolean parse_arg_list(int argc, char *argv[]) { 53 | if (argc == 2) { 54 | if (strcmp(argv[1], "-o") == 0) { 55 | octal = true; 56 | return true; 57 | } else if (strcmp(argv[1], "-x") == 0) { 58 | octal = false; 59 | return true; 60 | } 61 | } 62 | 63 | return false; 64 | } 65 | 66 | int is_ascii(int c) { 67 | if (c > 127) { 68 | return 0; 69 | } 70 | 71 | return 1; 72 | } 73 | 74 | // NOTE: run: ./print -x < test.txt 75 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_01/getint.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define MAXLEN 1000 5 | #define BUFFSIZE 100 6 | 7 | void printbuf(void); 8 | int getch(void); 9 | void ungetch(int c); 10 | int getint(int *pn); 11 | 12 | int main(void) { 13 | int number = 0; 14 | 15 | getint(&number); 16 | printf("%d\n", number); 17 | 18 | printbuf(); 19 | 20 | return 0; 21 | } 22 | 23 | int bufp = 0; 24 | int buf[BUFFSIZE]; 25 | 26 | void printbuf(void) { 27 | if (bufp) { 28 | printf("Buffer: [ "); 29 | 30 | int i; 31 | for (i = bufp - 1; i >= 0; --i) { 32 | if (i) { 33 | printf("'%c', ", buf[i] != '\n' ? buf[i] : '.'); 34 | } else { 35 | printf("'%c' ", buf[i] != '\n' ? buf[i] : '.'); 36 | } 37 | } 38 | 39 | printf("]\n"); 40 | } 41 | } 42 | 43 | int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar(); } 44 | 45 | void ungetch(int c) { 46 | if (bufp >= BUFFSIZE) { 47 | printf("ungetch: too many characters\n"); 48 | } else { 49 | buf[bufp++] = c; 50 | } 51 | } 52 | 53 | int getint(int *pn) { 54 | int c; 55 | int sign; 56 | 57 | while (isspace(c = getch())) 58 | ; 59 | 60 | if (!isdigit(c) && c != EOF && c != '+' && c != '-') { 61 | ungetch(c); 62 | return 0; 63 | } 64 | 65 | sign = (c == '-') ? -1 : 1; 66 | 67 | if (c == '+' || c == '-') { 68 | if (!isdigit(c = getch())) { 69 | ungetch(c); 70 | ungetch(sign == 1 ? '+' : '-'); 71 | return 0; 72 | } 73 | } 74 | 75 | for (*pn = 0; isdigit(c); c = getch()) { 76 | *pn = 10 * *pn + (c - '0'); 77 | } 78 | 79 | *pn = *pn * sign; 80 | 81 | if (c != EOF) { 82 | ungetch(c); 83 | } 84 | 85 | return c; 86 | } 87 | -------------------------------------------------------------------------------- /chapter_2/exercise_2_03/htoi.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAXLINE 100 7 | 8 | int get_line(char line[], int lim); 9 | int htoi(char hex[]); 10 | 11 | int main(void) { 12 | char hex[MAXLINE]; 13 | 14 | get_line(hex, MAXLINE); 15 | 16 | printf("%d", htoi(hex)); 17 | 18 | return 0; 19 | } 20 | 21 | int get_line(char line[], int lim) { 22 | char c; 23 | int i = 0; 24 | while (i < lim - 1 && (c = getchar()) != EOF && c != '\n') { 25 | line[i++] = c; 26 | } 27 | 28 | line[i] = '\0'; 29 | 30 | return i; 31 | } 32 | 33 | int htoi(char hex[]) { 34 | int result = 0; 35 | 36 | int i = 0; 37 | int len = strlen(hex); 38 | 39 | while (i < len) { 40 | if (hex[i] == '0' && (hex[i + 1] == 'x' || hex[i + 1] == 'X')) { 41 | i += 2; 42 | } 43 | 44 | int temp = tolower(hex[i]); 45 | 46 | if (isdigit(temp)) { 47 | temp -= 48; 48 | } 49 | 50 | if (isalpha(temp) && temp <= 'f') { 51 | temp = temp - 'a' + 10; 52 | } 53 | 54 | if ((hex[i] >= '0' && hex[i] <= '9') || 55 | (hex[i] >= 'a' && hex[i] <= 'f') || 56 | (hex[i] >= 'A' && hex[i] <= 'F')) { 57 | result += temp * (int)pow(16, len - i - 1); 58 | } else { 59 | printf("Error: Not a valid hex value.\n Try this format: 0xHHHH, " 60 | "where H is a hex digit.\n"); 61 | } 62 | 63 | ++i; 64 | } 65 | 66 | return result; 67 | } 68 | 69 | // NOTE: The conversion algorithm from hex to dec is very similar with the 70 | // conversion algorithm from bin to dec, but the base is not 2 but 16. 71 | // The general formula is: x1*B^N + x2*B^(N - 1) + ... + xn*B^(N - N), where B 72 | // is the base from we convert to dec, in this case B = 16. 73 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_11/file_tabs.txt: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define DEFAULT_TAB_LENGTH 8 7 | 8 | int is_str_uint(char *str); 9 | int is_tab_stop_arg_list_valid(int argc, char *argv[]); 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | if (!is_tab_stop_arg_list_valid(argc, argv)) 14 | { 15 | puts("Error: invalid tab stop list.\n"); 16 | return EXIT_FAILURE; 17 | } 18 | 19 | int c; 20 | size_t arg_pos = 1; 21 | size_t line_pos = 0; 22 | size_t tab_stop = DEFAULT_TAB_LENGTH; 23 | size_t nr_of_spaces; 24 | size_t nr_of_custom_tab_stops = argc - 1; 25 | 26 | while ((c = getchar()) != EOF) 27 | { 28 | if (c == '\t') 29 | { 30 | if (nr_of_custom_tab_stops) 31 | { 32 | tab_stop = atoi(argv[arg_pos++]); 33 | --nr_of_custom_tab_stops; 34 | } 35 | else if (argc > 1) 36 | { 37 | tab_stop = 1; 38 | } 39 | 40 | nr_of_spaces = tab_stop - line_pos % tab_stop; 41 | 42 | while (nr_of_spaces) 43 | { 44 | putchar(' '); 45 | ++line_pos; 46 | --nr_of_spaces; 47 | } 48 | } 49 | else 50 | { 51 | putchar(c); 52 | ++line_pos; 53 | 54 | if (c == '\n') 55 | { 56 | arg_pos = 1; 57 | line_pos = 0; 58 | nr_of_custom_tab_stops = argc - 1; 59 | } 60 | } 61 | } 62 | 63 | return EXIT_SUCCESS; 64 | } 65 | 66 | int is_str_uint(char *str) 67 | { 68 | for (size_t i = 0; i < strlen(str); ++i) 69 | { 70 | if (!isdigit(str[i])) 71 | { 72 | return 0; 73 | } 74 | } 75 | return 1; 76 | } 77 | 78 | int is_tab_stop_arg_list_valid(int argc, char *argv[]) 79 | { 80 | for (size_t i = 1; i < argc; ++i) 81 | { 82 | if (!is_str_uint(argv[i]) || (i > 1 && atoi(argv[i - 1]) > atoi(argv[i]))) 83 | { 84 | return 0; 85 | } 86 | } 87 | return 1; 88 | } 89 | 90 | // NOTE: The current program works in a similar fashion as expand. 91 | // run: ./detab 4 8 12 16 < file_tabs.txt > file_spaces.txt 92 | -------------------------------------------------------------------------------- /chapter_8/exercise_8_04/syscalls.h: -------------------------------------------------------------------------------- 1 | #define NULL 0 2 | 3 | #define EOF (-1) 4 | #define BUFFER_SIZE 1024 5 | #define MAX_NR_OF_OPEN_FILES 20 6 | 7 | #define SEEK_SET 0 // Set file offset to offset 8 | #define SEEK_CUR 1 // Set file offset to current plus offset 9 | #define SEEK_END 2 // Set file offset to EOF plus offset 10 | 11 | #define EXIT_FAILURE 1 12 | #define EXIT_SUCCESS 0 13 | 14 | struct _io_buffer_file { 15 | int counter; 16 | char *next_char_pos_p; 17 | char *base; 18 | struct { 19 | // Using unsigned int for bit-fields prevents warnings about 20 | // implicit truncation when assigning 1 to a signed bit-field 21 | unsigned int _READ : 1; 22 | unsigned int _WRITE : 1; 23 | unsigned int _UNBUF : 1; 24 | unsigned int _EOF : 1; 25 | unsigned int _ERR : 1; 26 | } flag; 27 | int file_descriptor; 28 | }; 29 | 30 | typedef struct _io_buffer_file FILE; 31 | 32 | extern FILE _io_buffer[MAX_NR_OF_OPEN_FILES]; 33 | 34 | int _fill_buffer(FILE *); 35 | int _flush_buffer(int c, FILE *file_p); 36 | int file_flush(FILE *file_p); 37 | FILE *file_open(char *name, char *mode); 38 | int file_close(FILE *file_p); 39 | int file_seek(FILE *file_p, long offset, int whence); 40 | 41 | #define stdin (&_io_buffer[0]) 42 | #define stdout (&_io_buffer[1]) 43 | #define stderr (&_io_buffer[2]) 44 | 45 | #define feof(p) ((p->flag & _EOF) != 0) 46 | #define ferror(p) ((p->flag & _ERR) != 0) 47 | #define fileno(p) ((p->file_descriptor) 48 | 49 | #define getc(p) \ 50 | ((--p->counter >= 0) ? (unsigned char)*(p)->next_char_pos_p++ \ 51 | : _fill_buffer(p)) 52 | #define putc(x, p) \ 53 | ((--p->counter >= 0) ? *p->next_char_pos_p++ = x : _flush_buffer(x, p)) 54 | 55 | #define getchar() getc(stdin) 56 | #define putchar(x) putc(x, stdout) 57 | -------------------------------------------------------------------------------- /chapter_7/exercise_7_03/minprintf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void minprintf(const char *format, ...); 6 | 7 | int main(void) { 8 | int a; 9 | minprintf("Let's print %d, %i, %o, %x, %X, %u, %c, %e, %E, %g, %G, %f, %p, " 10 | "and %s.\n", 11 | 2, 3, 8, 16, 16, -1, 97, 0.0025f, 0.0023f, 0.0025f, 0.0023f, 12 | 3.14159f, &a, "hello, world"); 13 | return EXIT_SUCCESS; 14 | } 15 | 16 | void minprintf(const char *format, ...) { 17 | va_list arg_p; 18 | 19 | va_start(arg_p, format); 20 | for (; *format != '\0'; ++format) { 21 | if (*format != '%') { 22 | putc(*format, stdout); 23 | continue; 24 | } 25 | 26 | switch (*++format) { 27 | case 'd': 28 | case 'i': 29 | printf("%d", va_arg(arg_p, int)); 30 | break; 31 | 32 | case 'o': 33 | printf("%o", va_arg(arg_p, int)); 34 | break; 35 | 36 | case 'x': 37 | case 'X': 38 | printf("%x", va_arg(arg_p, int)); 39 | break; 40 | 41 | case 'u': 42 | printf("%u", va_arg(arg_p, int)); 43 | break; 44 | 45 | case 'c': 46 | printf("%c", va_arg(arg_p, int)); 47 | break; 48 | 49 | case 's': 50 | printf("%s", va_arg(arg_p, char *)); 51 | break; 52 | 53 | case 'f': 54 | printf("%f", va_arg(arg_p, double)); 55 | break; 56 | 57 | case 'e': 58 | case 'E': 59 | printf("%e", va_arg(arg_p, double)); 60 | break; 61 | 62 | case 'g': 63 | case 'G': 64 | printf("%g", va_arg(arg_p, double)); 65 | break; 66 | 67 | case 'p': 68 | printf("%p", va_arg(arg_p, void *)); 69 | break; 70 | 71 | default: 72 | putc(*format, stdout); 73 | break; 74 | } 75 | } 76 | va_end(arg_p); 77 | } 78 | -------------------------------------------------------------------------------- /chapter_4/exercise_4_02/atof.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define MAXLEN 500 5 | 6 | int get_line(char line[], unsigned int max_line_len); 7 | double atof(char s[]); 8 | 9 | int main(void) { 10 | char line[MAXLEN]; 11 | 12 | get_line(line, MAXLEN); 13 | printf("%s", line); 14 | 15 | printf("%f", atof(line)); 16 | 17 | return 0; 18 | } 19 | 20 | int get_line(char line[], unsigned int max_line_len) { 21 | // Using unsigned int for i to match the unsigned max_line_len parameter 22 | // This prevents sign comparison warnings 23 | unsigned int i = 0; 24 | int c; 25 | 26 | while (i < max_line_len - 1 && (c = getchar()) != '\n') { 27 | line[i] = c; 28 | ++i; 29 | } 30 | 31 | if (c == '\n') { 32 | line[i++] = c; 33 | } 34 | 35 | line[i] = '\0'; 36 | 37 | return i; 38 | } 39 | 40 | double atof(char s[]) { 41 | double val; 42 | double power; 43 | int i; 44 | int sign; 45 | int exp_sign = 1; 46 | int exp_pwr = 0; 47 | 48 | for (i = 0; isspace(s[i]); ++i) 49 | ; 50 | 51 | sign = (s[i] == '-') ? -1 : 1; 52 | 53 | if (s[i] == '+' || s[i] == '-') { 54 | ++i; 55 | } 56 | 57 | for (val = 0.0; isdigit(s[i]); ++i) { 58 | val = 10.0 * val + (s[i] - '0'); 59 | } 60 | 61 | if (s[i] == '.') { 62 | ++i; 63 | } 64 | 65 | for (power = 1.0; isdigit(s[i]); ++i) { 66 | val = 10.0 * val + (s[i] - '0'); 67 | power *= 10; 68 | } 69 | 70 | if (s[i] == 'e' || s[i] == 'E') { 71 | if (s[++i] == '-') { 72 | exp_sign = -1; 73 | ++i; 74 | } 75 | } 76 | 77 | while (isdigit(s[i])) { 78 | exp_pwr = 10 * exp_pwr + (s[i] - '0'); 79 | ++i; 80 | } 81 | 82 | while (exp_pwr) { 83 | if (exp_sign == -1) { 84 | power *= 10; 85 | } else { 86 | power /= 10; 87 | } 88 | 89 | --exp_pwr; 90 | } 91 | 92 | return sign * val / power; 93 | } 94 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_22/fold_line.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MAXLINE 10000 4 | 5 | #define TRUE (1 == 1) 6 | #define FALSE !TRUE 7 | 8 | #define BREAKING_POINT 40 9 | #define OFFSET 10 10 | 11 | int get_line(char line[], int max_line_len); 12 | void fold_line(char line[], char fold_str[], int n_break); 13 | 14 | int main(void) { 15 | char line[MAXLINE]; 16 | char fold_str[MAXLINE]; 17 | 18 | while ((get_line(line, MAXLINE)) > 0) { 19 | fold_line(line, fold_str, BREAKING_POINT); 20 | printf("%s", fold_str); 21 | } 22 | 23 | return 0; 24 | } 25 | 26 | int get_line(char line[], int max_line_len) { 27 | int c; 28 | int i = 0; 29 | 30 | while (i < max_line_len - 1 && (c = getchar()) != EOF && c != '\n') { 31 | line[i++] = c; 32 | } 33 | 34 | if (c == '\n') { 35 | line[i++] = c; 36 | } 37 | 38 | line[i] = '\0'; 39 | 40 | return i; 41 | } 42 | 43 | void fold_line(char line[], char fold_str[], int n_break) { 44 | int i; 45 | int j; 46 | int column = 0; 47 | int split = FALSE; 48 | int last_blank = 0; 49 | 50 | for (i = 0, j = 0; line[i] != '\0'; ++i, ++j) { 51 | fold_str[j] = line[i]; 52 | 53 | if (fold_str[j] == '\n') { 54 | column = 0; 55 | } 56 | 57 | column++; 58 | 59 | if (column == n_break - OFFSET) { 60 | split = TRUE; 61 | } 62 | 63 | if (split && (fold_str[j] == ' ' || fold_str[j] == '\t')) { 64 | last_blank = j; 65 | } 66 | 67 | if (column == n_break) { 68 | if (last_blank) { 69 | fold_str[last_blank] = '\n'; 70 | column = j - last_blank; 71 | last_blank = 0; 72 | } else { 73 | fold_str[j++] = '-'; 74 | fold_str[j] = '\n'; 75 | 76 | column = 0; 77 | } 78 | 79 | split = FALSE; 80 | } 81 | } 82 | 83 | fold_str[j] = '\0'; 84 | } 85 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | rwildcard=$(foreach d, $(wildcard $(1:=/*)), $(call rwildcard, $d, $2) $(filter $(subst *, %, $2), $d)) 2 | 3 | SOURCES = $(call rwildcard, ., *.c, *.h) 4 | DEBUG_FILES = $(call rwildcard, ., *.dSYM) 5 | EXECS = $(SOURCES:%.c=%) 6 | LDLIBS = -lm 7 | CFLAGS = -Wall -Wextra -Wpedantic 8 | 9 | # Find clang tools: check PATH first, then Homebrew LLVM location 10 | LLVM_PREFIX := $(shell brew --prefix llvm 2>/dev/null) 11 | CLANG_FORMAT := $(or $(shell command -v clang-format 2>/dev/null),$(wildcard $(LLVM_PREFIX)/bin/clang-format)) 12 | CLANG_TIDY := $(or $(shell command -v clang-tidy 2>/dev/null),$(wildcard $(LLVM_PREFIX)/bin/clang-tidy)) 13 | 14 | all: install-hooks $(EXECS) 15 | 16 | lint: 17 | @echo "Linting with compiler warnings..." 18 | @for f in $(filter %.c, $(SOURCES)); do \ 19 | $(CC) $(CFLAGS) -fsyntax-only $$f 2>&1 || exit 1; \ 20 | done 21 | ifneq ($(CLANG_TIDY),) 22 | @echo "Running clang-tidy..." 23 | @for f in $(filter %.c, $(SOURCES)); do \ 24 | $(CLANG_TIDY) $$f -- $(CFLAGS) 2>&1 || exit 1; \ 25 | done 26 | else 27 | @echo "Skipping clang-tidy (not installed). Install with: brew install llvm" 28 | endif 29 | @echo "Lint passed!" 30 | 31 | format: 32 | ifeq ($(CLANG_FORMAT),) 33 | $(error clang-format not found. Install with: brew install llvm) 34 | endif 35 | find . \( -name '*.c' -o -name '*.h' \) -print0 | xargs -0 $(CLANG_FORMAT) -i 36 | 37 | format-check: 38 | ifeq ($(CLANG_FORMAT),) 39 | $(error clang-format not found. Install with: brew install llvm) 40 | endif 41 | find . \( -name '*.c' -o -name '*.h' \) -print0 | xargs -0 $(CLANG_FORMAT) --dry-run --Werror 42 | 43 | check: format-check lint 44 | 45 | install-hooks: 46 | @if [ -d .git ] && [ ! -f .git/hooks/pre-commit ]; then \ 47 | cp scripts/pre-commit .git/hooks/pre-commit; \ 48 | chmod +x .git/hooks/pre-commit; \ 49 | echo "Pre-commit hook installed."; \ 50 | fi 51 | 52 | clean: 53 | @for f in $(EXECS) $(DEBUG_FILES); do \ 54 | if [ -e "$$f" ] || [ -d "$$f" ]; then \ 55 | rm -rf "$$f" && echo "Removed: $$f"; \ 56 | fi; \ 57 | done 58 | 59 | .PHONY: all lint format format-check check install-hooks clean 60 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_11/detab.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define DEFAULT_TAB_LENGTH 8 7 | 8 | int is_str_uint(char *str); 9 | int is_tab_stop_arg_list_valid(int argc, char *argv[]); 10 | 11 | int main(int argc, char *argv[]) { 12 | if (!is_tab_stop_arg_list_valid(argc, argv)) { 13 | puts("Error: invalid tab stop list.\n"); 14 | return EXIT_FAILURE; 15 | } 16 | 17 | int c; 18 | size_t arg_pos = 1; 19 | size_t line_pos = 0; 20 | size_t tab_stop = DEFAULT_TAB_LENGTH; 21 | size_t nr_of_spaces; 22 | size_t nr_of_custom_tab_stops = argc - 1; 23 | 24 | while ((c = getchar()) != EOF) { 25 | if (c == '\t') { 26 | if (nr_of_custom_tab_stops) { 27 | tab_stop = atoi(argv[arg_pos++]); 28 | --nr_of_custom_tab_stops; 29 | } else if (argc > 1) { 30 | tab_stop = 1; 31 | } 32 | 33 | nr_of_spaces = tab_stop - line_pos % tab_stop; 34 | 35 | while (nr_of_spaces) { 36 | putchar(' '); 37 | ++line_pos; 38 | --nr_of_spaces; 39 | } 40 | } else { 41 | putchar(c); 42 | ++line_pos; 43 | 44 | if (c == '\n') { 45 | arg_pos = 1; 46 | line_pos = 0; 47 | nr_of_custom_tab_stops = argc - 1; 48 | } 49 | } 50 | } 51 | 52 | return EXIT_SUCCESS; 53 | } 54 | 55 | int is_str_uint(char *str) { 56 | for (size_t i = 0; i < strlen(str); ++i) { 57 | if (!isdigit(str[i])) { 58 | return 0; 59 | } 60 | } 61 | return 1; 62 | } 63 | 64 | int is_tab_stop_arg_list_valid(int argc, char *argv[]) { 65 | for (int i = 1; i < argc; ++i) { 66 | if (!is_str_uint(argv[i]) || 67 | (i > 1 && atoi(argv[i - 1]) > atoi(argv[i]))) { 68 | return 0; 69 | } 70 | } 71 | return 1; 72 | } 73 | 74 | // NOTE: The current program works in a similar fashion as expand. 75 | // run: ./detab 4 8 12 16 < file_tabs.txt > file_spaces.txt 76 | -------------------------------------------------------------------------------- /chapter_7/exercise_7_05/calculator.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define STACK_MAX_SIZE 100 6 | #define MAX_STR_LEN 1000 7 | 8 | void push(double); 9 | double pop(void); 10 | 11 | int sp = 0; 12 | double stack[STACK_MAX_SIZE]; 13 | 14 | int main(void) { 15 | char c; 16 | double op2; 17 | char str[MAX_STR_LEN]; 18 | 19 | while (scanf("%s", str) != EOF) { 20 | if (sscanf(str, "%lf", &op2) == 1) { 21 | push(op2); 22 | } else if (sscanf(str, "%c", &c) == 1) { 23 | switch (c) { 24 | case '+': 25 | push(pop() + pop()); 26 | break; 27 | 28 | case '-': 29 | op2 = pop(); 30 | push(pop() - op2); 31 | break; 32 | 33 | case '*': 34 | push(pop() * pop()); 35 | break; 36 | 37 | case '/': 38 | op2 = pop(); 39 | 40 | if (op2 != 0.0) { 41 | push(pop() / op2); 42 | } else { 43 | printf("Error: zero divisor.\n"); 44 | } 45 | break; 46 | 47 | case '%': 48 | op2 = pop(); 49 | 50 | if (op2 != 0.0) { 51 | push((int)pop() % (int)op2); 52 | } else { 53 | printf("Error: zero divisor.\n"); 54 | } 55 | break; 56 | 57 | default: 58 | printf("Error: unknown command.\n"); 59 | break; 60 | } 61 | } 62 | } 63 | 64 | printf("result: %.8g\n", pop()); 65 | 66 | return EXIT_SUCCESS; 67 | } 68 | 69 | void push(double f) { 70 | if (sp < STACK_MAX_SIZE) { 71 | stack[sp++] = f; 72 | } else { 73 | printf("Error: stack full, can't push %g.\n", f); 74 | } 75 | } 76 | 77 | double pop(void) { 78 | if (sp > 0) { 79 | return stack[--sp]; 80 | } else { 81 | printf("Error: stack empty.\n"); 82 | return 0.0; 83 | } 84 | } 85 | 86 | // NOTE: run: ./calculator <<< "2 3 4 2 - + +" 87 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_11/file_spaces.txt: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define DEFAULT_TAB_LENGTH 8 7 | 8 | int is_str_uint(char *str); 9 | int is_tab_stop_arg_list_valid(int argc, char *argv[]); 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | if (!is_tab_stop_arg_list_valid(argc, argv)) 14 | { 15 | puts("Error: invalid tab stop list.\n"); 16 | return EXIT_FAILURE; 17 | } 18 | 19 | int c; 20 | size_t arg_pos = 1; 21 | size_t line_pos = 0; 22 | size_t tab_stop = DEFAULT_TAB_LENGTH; 23 | size_t nr_of_spaces; 24 | size_t nr_of_custom_tab_stops = argc - 1; 25 | 26 | while ((c = getchar()) != EOF) 27 | { 28 | if (c == '\t') 29 | { 30 | if (nr_of_custom_tab_stops) 31 | { 32 | tab_stop = atoi(argv[arg_pos++]); 33 | --nr_of_custom_tab_stops; 34 | } 35 | else if (argc > 1) 36 | { 37 | tab_stop = 1; 38 | } 39 | 40 | nr_of_spaces = tab_stop - line_pos % tab_stop; 41 | 42 | while (nr_of_spaces) 43 | { 44 | putchar(' '); 45 | ++line_pos; 46 | --nr_of_spaces; 47 | } 48 | } 49 | else 50 | { 51 | putchar(c); 52 | ++line_pos; 53 | 54 | if (c == '\n') 55 | { 56 | arg_pos = 1; 57 | line_pos = 0; 58 | nr_of_custom_tab_stops = argc - 1; 59 | } 60 | } 61 | } 62 | 63 | return EXIT_SUCCESS; 64 | } 65 | 66 | int is_str_uint(char *str) 67 | { 68 | for (size_t i = 0; i < strlen(str); ++i) 69 | { 70 | if (!isdigit(str[i])) 71 | { 72 | return 0; 73 | } 74 | } 75 | return 1; 76 | } 77 | 78 | int is_tab_stop_arg_list_valid(int argc, char *argv[]) 79 | { 80 | for (size_t i = 1; i < argc; ++i) 81 | { 82 | if (!is_str_uint(argv[i]) || (i > 1 && atoi(argv[i - 1]) > atoi(argv[i]))) 83 | { 84 | return 0; 85 | } 86 | } 87 | return 1; 88 | } 89 | 90 | // NOTE: The current program works in a similar fashion as expand. 91 | // run: ./detab 4 8 12 16 < file_tabs.txt > file_spaces.txt 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The C Programming Language 2nd Edition - Solutions 2 | 3 | ![C/C++ CI](https://github.com/gleesik/the-c-programming-language-2nd-edition-solutions/workflows/C/C++%20CI/badge.svg) 4 | 5 | Solutions to exercises from **The C Programming Language** (2nd Edition) by Brian W. Kernighan and Dennis M. Ritchie, commonly known as **K&R**. 6 | 7 | ## Getting Started 8 | 9 | ### Prerequisites 10 | 11 | You need a C compiler: 12 | 13 | **macOS:** 14 | ```shell 15 | xcode-select --install 16 | ``` 17 | 18 | **Ubuntu / Debian:** 19 | ```shell 20 | sudo apt-get update && sudo apt-get install build-essential 21 | ``` 22 | 23 | **Windows:** 24 | Use [WSL](https://docs.microsoft.com/en-us/windows/wsl/install) (recommended) or [MinGW](http://www.mingw.org). 25 | 26 | ### Building 27 | 28 | Build all exercises: 29 | ```shell 30 | make 31 | ``` 32 | 33 | Build a specific exercise: 34 | ```shell 35 | cd chapter_1/exercise_1_01 36 | make hello_world 37 | ./hello_world 38 | ``` 39 | 40 | Clean up: 41 | ```shell 42 | make clean 43 | ``` 44 | 45 | ## Repository Structure 46 | 47 | ``` 48 | chapter_N/ 49 | exercise_N_XX/ 50 | solution.c # Solution source code 51 | file_in.txt # Input file (if needed) 52 | file_out.txt # Output file (if needed) 53 | ``` 54 | 55 | ## Contributing 56 | 57 | Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. 58 | 59 | Quick start: 60 | ```shell 61 | make format # Format your code 62 | make check # Run all checks before submitting 63 | ``` 64 | 65 | ## About the Book 66 | 67 | **The C Programming Language** is a classic programming book. The exercises are designed so that you can solve them with the knowledge acquired up to that point in the book. 68 | 69 | These solutions are meant to help those learning C. If you're working through the book, try solving the exercises yourself first! 70 | 71 | ## Tools 72 | 73 | - **Editor:** Any text editor works. [VS Code](https://code.visualstudio.com) with the [C/C++ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) is recommended. 74 | - **Debugger:** [LLDB](https://lldb.llvm.org) (macOS) or [GDB](https://www.gnu.org/software/gdb) (Linux) 75 | - **Formatter:** [clang-format](https://clang.llvm.org/docs/ClangFormat.html) 76 | 77 | ## License 78 | 79 | This project is open source. Feel free to use these solutions for learning! 80 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_11/entab.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define DEFAULT_TAB_LENGTH 8 7 | 8 | int is_str_uint(char *str); 9 | int is_tab_stop_arg_list_valid(int argc, char *argv[]); 10 | 11 | int main(int argc, char *argv[]) { 12 | if (!is_tab_stop_arg_list_valid(argc, argv)) { 13 | puts("Error: invalid tab stop list.\n"); 14 | return EXIT_FAILURE; 15 | } 16 | 17 | int c; 18 | size_t arg_pos = 1; 19 | size_t line_pos = 0; 20 | size_t tab_stop = DEFAULT_TAB_LENGTH; 21 | size_t nr_of_spaces = 0; 22 | size_t nr_of_custom_tab_stops = argc - 1; 23 | 24 | while ((c = getchar()) != EOF) { 25 | ++line_pos; 26 | 27 | if (c == ' ') { 28 | ++nr_of_spaces; 29 | 30 | if (nr_of_custom_tab_stops) { 31 | tab_stop = atoi(argv[arg_pos]); 32 | } else if (argc > 1) { 33 | tab_stop = 1; 34 | } 35 | 36 | if (line_pos % tab_stop == 0 && nr_of_spaces > 1) { 37 | putchar('\t'); 38 | 39 | if (nr_of_custom_tab_stops) { 40 | ++arg_pos; 41 | --nr_of_custom_tab_stops; 42 | } 43 | 44 | nr_of_spaces = 0; 45 | } 46 | } else { 47 | while (nr_of_spaces) { 48 | putchar(' '); 49 | --nr_of_spaces; 50 | } 51 | 52 | if (c == '\n') { 53 | arg_pos = 1; 54 | line_pos = 0; 55 | nr_of_custom_tab_stops = argc - 1; 56 | } 57 | 58 | putchar(c); 59 | } 60 | } 61 | 62 | return EXIT_SUCCESS; 63 | } 64 | 65 | int is_str_uint(char *str) { 66 | for (size_t i = 0; i < strlen(str); ++i) { 67 | if (!isdigit(str[i])) { 68 | return 0; 69 | } 70 | } 71 | return 1; 72 | } 73 | 74 | int is_tab_stop_arg_list_valid(int argc, char *argv[]) { 75 | for (int i = 1; i < argc; ++i) { 76 | if (!is_str_uint(argv[i]) || 77 | (i > 1 && atoi(argv[i - 1]) > atoi(argv[i]))) { 78 | return 0; 79 | } 80 | } 81 | return 1; 82 | } 83 | 84 | // NOTE: The current program works in a similar fashion as unexpand. 85 | // run: ./entab 4 8 12 16 > file_tabs.txt < file_spaces.txt 86 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_23/c_remove_comments.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MAXSTR 10000 4 | 5 | #define TRUE (1 == 1) 6 | #define FALSE !TRUE 7 | 8 | // This is a test comment. 9 | 10 | int get_str(char str[], int limit); // This is another test comment. 11 | void remove_comments(char str[], char no_com_str[]); 12 | 13 | int main(void) { 14 | /** 15 | * This is multiline 16 | * block 17 | * comment. 18 | */ 19 | 20 | char str[MAXSTR]; 21 | char no_com_str[MAXSTR]; 22 | 23 | get_str(str, MAXSTR); 24 | 25 | remove_comments(str, no_com_str); 26 | 27 | printf("%s", no_com_str); 28 | 29 | return 0; 30 | } 31 | 32 | int get_str(char str[], int limit) { 33 | int c; 34 | int i = 0; 35 | 36 | while (i < limit - 1 && (c = getchar()) != EOF) { 37 | str[i++] = c; 38 | } 39 | str[i] = '\0'; 40 | 41 | return i; 42 | } 43 | 44 | void remove_comments(char str[], char no_com_str[]) { 45 | int in_quote = FALSE; 46 | int line_comment = FALSE; 47 | int block_comment = FALSE; 48 | 49 | int i = 0; 50 | int j = 0; 51 | while (str[i] != '\0') { 52 | if (!block_comment) { 53 | if (!in_quote && str[i] == '"') { 54 | in_quote = TRUE; 55 | } else if (in_quote && str[i] == '"') { 56 | in_quote = FALSE; 57 | } 58 | } 59 | 60 | if (!in_quote) { 61 | if (str[i] == '/' && str[i + 1] == '*' && !line_comment) { 62 | block_comment = TRUE; 63 | } 64 | 65 | if (str[i] == '*' && str[i + 1] == '/') { 66 | block_comment = FALSE; 67 | i += 2; 68 | } 69 | 70 | if (str[i] == '/' && str[i + 1] == '/') { 71 | line_comment = TRUE; 72 | } 73 | 74 | if (str[i] == '\n') { 75 | line_comment = FALSE; 76 | } 77 | 78 | if (line_comment || block_comment) { 79 | ++i; 80 | } else if (!line_comment || !block_comment) { 81 | no_com_str[j++] = str[i++]; 82 | } 83 | } else { 84 | no_com_str[j++] = str[i++]; 85 | } 86 | } 87 | 88 | no_com_str[j] = '\0'; 89 | } 90 | 91 | // NOTE: run: ./c_remove_comments < c_remove_comments.c 92 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_08/date_conversion.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static char daytab[2][13] = { 4 | {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 5 | {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; 6 | 7 | int day_of_year(int year, int month, int day); 8 | void month_day(int year, int yearday, int *pmonth, int *pda); 9 | 10 | int main(void) { 11 | int day = day_of_year(2020, 10, 30); 12 | printf("day of the year: %d\n", day); 13 | 14 | int month_invalid = day_of_year(2020, 13, 1); 15 | if (month_invalid == -1) { 16 | printf("Warning: invalid month detected.\n"); 17 | } 18 | 19 | int day_invalid = day_of_year(202, 12, 32); 20 | if (day_invalid == -2) { 21 | printf("Warning: invalid day detected.\n"); 22 | } 23 | 24 | int month; 25 | int day_month; 26 | 27 | month_day(2020, 304, &month, &day_month); 28 | printf("month: %d, day: %d\n", month, day_month); 29 | 30 | month_day(2020, 366, &month, &day_month); 31 | printf("month: %d, day: %d\n", month, day_month); 32 | 33 | month_day(2020, 367, &month, &day_month); 34 | printf("month: %d, day: %d\n", month, day_month); 35 | 36 | return 0; 37 | } 38 | 39 | int day_of_year(int year, int month, int day) { 40 | int leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; 41 | 42 | // Additional checks for month. 43 | if (month > 12) { 44 | printf("Error: a year has 12 months, so please choose a number " 45 | "betweeen 1 and 12.\n"); 46 | return -1; 47 | } 48 | 49 | // Additional checks for day. 50 | if (day > daytab[leap][month]) { 51 | printf("Error: the %d month has a maximum of %d days.\n", month, 52 | daytab[leap][month]); 53 | return -2; 54 | } 55 | 56 | int i; 57 | for (i = 1; i < month; i++) { 58 | day += daytab[leap][i]; 59 | } 60 | 61 | return day; 62 | } 63 | 64 | void month_day(int year, int yearday, int *pmonth, int *pda) { 65 | int leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; 66 | 67 | // Additional check for yearday. 68 | if ((!leap && yearday > 365) || (leap && yearday > 366)) { 69 | // Reset the provided month and day. 70 | *pmonth = 0; 71 | *pda = 0; 72 | printf("Error: year %d has %d days.\n", year, leap ? 366 : 365); 73 | 74 | return; 75 | } 76 | 77 | int i; 78 | for (i = 0; yearday > daytab[leap][i]; i++) { 79 | yearday -= daytab[leap][i]; 80 | } 81 | 82 | *pmonth = i; 83 | *pda = yearday; 84 | } 85 | -------------------------------------------------------------------------------- /chapter_7/exercise_7_07/find.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define MAX_LINE_LEN 1000 6 | 7 | typedef enum { false, true } boolean; 8 | 9 | boolean parse_arg_list(int argc, char *argv[]); 10 | void find_pattern(char *pattern, FILE *file_p); 11 | 12 | char *program_name; 13 | 14 | boolean except = false; 15 | boolean number = false; 16 | 17 | int pattern_arg_pos = 1; 18 | 19 | int main(int argc, char *argv[]) { 20 | if (!parse_arg_list(argc, argv)) { 21 | exit(EXIT_FAILURE); 22 | } 23 | 24 | if (argc - pattern_arg_pos > 1) { 25 | for (int file_arg_pos = pattern_arg_pos + 1; file_arg_pos < argc; 26 | file_arg_pos++) { 27 | FILE *file_p; 28 | if ((file_p = fopen(argv[file_arg_pos], "r")) == NULL) { 29 | fprintf(stderr, "%s: can't open %s.\n", program_name, 30 | argv[file_arg_pos]); 31 | exit(EXIT_FAILURE); 32 | } 33 | printf("%s\n", argv[file_arg_pos]); 34 | find_pattern(argv[pattern_arg_pos], file_p); 35 | fclose(file_p); 36 | 37 | if (file_arg_pos < argc - 1) { 38 | putc('\n', stdout); 39 | } 40 | } 41 | } else { 42 | find_pattern(argv[pattern_arg_pos], stdin); 43 | } 44 | 45 | exit(EXIT_SUCCESS); 46 | } 47 | 48 | boolean parse_arg_list(int argc, char *argv[]) { 49 | program_name = argv[0]; 50 | 51 | if (argc < 3) { 52 | fprintf(stderr, "Usage: %s [-xn]... PATTERN [FILE]...\n", program_name); 53 | return false; 54 | } 55 | 56 | while (--argc > 0 && (*++argv)[0] == '-') { 57 | int c; 58 | while ((c = *++argv[0])) { 59 | switch (c) { 60 | case 'x': 61 | except = true; 62 | break; 63 | 64 | case 'n': 65 | number = true; 66 | break; 67 | 68 | default: 69 | fprintf(stderr, "%s: illegal option %c.\n", program_name, c); 70 | return false; 71 | break; 72 | } 73 | } 74 | 75 | ++pattern_arg_pos; 76 | } 77 | 78 | return true; 79 | } 80 | 81 | void find_pattern(char *pattern, FILE *file_p) { 82 | size_t line_number = 1; 83 | char line[MAX_LINE_LEN]; 84 | while (fgets(line, MAX_LINE_LEN, file_p) != NULL) { 85 | if ((strstr(line, pattern) != NULL) != except) { 86 | if (number) { 87 | printf("%ld: ", line_number); 88 | } 89 | printf("%s", line); 90 | } 91 | ++line_number; 92 | } 93 | } 94 | 95 | // NOTE: run: ./find -n "Some people" file_1.txt file_2.txt 96 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_13/histogram.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define TRUE 1 4 | #define FALSE 0 5 | 6 | #define BUFFER 100 7 | 8 | int main(void) { 9 | int histogram[BUFFER]; 10 | int histogram_length = 0; 11 | 12 | int max_word_count = 0; 13 | 14 | // Initialize the histogram array with 0 15 | int i; 16 | for (i = 0; i < BUFFER; ++i) { 17 | histogram[i] = 0; 18 | } 19 | 20 | // Count the words length and store in histogram array at the 21 | // specific index 22 | char c; 23 | int word_count_index = 0; 24 | while ((c = getchar())) { 25 | if (c == ' ' || c == '\t' || c == '\n' || c == EOF) { 26 | if (word_count_index > 0) { 27 | ++histogram[word_count_index - 1]; 28 | 29 | if (histogram[word_count_index - 1] > max_word_count) { 30 | max_word_count = histogram[word_count_index - 1]; 31 | } 32 | 33 | if (histogram_length < word_count_index - 1) { 34 | histogram_length = word_count_index - 1; 35 | } 36 | 37 | word_count_index = 0; 38 | } 39 | if (c == EOF) { 40 | break; 41 | } 42 | } else { 43 | ++word_count_index; 44 | } 45 | } 46 | 47 | // Add in the histogram array a end of useful data char 48 | histogram[histogram_length + 1] = '$'; 49 | 50 | putchar('\n'); 51 | 52 | int column_index = 0; 53 | int line_index = 0; 54 | 55 | // Print horizontal histogram 56 | printf("Horizontal Histogram\n--------------------\n"); 57 | 58 | while (histogram[column_index] != '$') { 59 | printf("%3d: \t", column_index + 1); 60 | 61 | for (line_index = 0; line_index < histogram[column_index]; 62 | ++line_index) { 63 | putchar('#'); 64 | } 65 | 66 | putchar('\n'); 67 | 68 | ++column_index; 69 | } 70 | 71 | putchar('\n'); 72 | 73 | // Print a vertical histogram 74 | printf("Vertical Histogram\n------------------\n"); 75 | 76 | for (line_index = max_word_count; line_index >= 0; --line_index) { 77 | column_index = 0; 78 | while (histogram[column_index] != '$') { 79 | if (line_index == 0) { 80 | printf("%2d ", column_index + 1); 81 | } else if (histogram[column_index] >= line_index) { 82 | printf("## "); 83 | } else { 84 | printf(" "); 85 | } 86 | 87 | ++column_index; 88 | } 89 | 90 | putchar('\n'); 91 | } 92 | 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_09/date_conversion_pointers.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static char year_month_days[] = {0, 31, 28, 31, 30, 31, 30, 4 | 31, 31, 30, 31, 30, 31}; 5 | static char leap_year_month_days[] = {0, 31, 29, 31, 30, 31, 30, 6 | 31, 31, 30, 31, 30, 31}; 7 | 8 | static char *daytab[2] = {year_month_days, leap_year_month_days}; 9 | 10 | int day_of_year(int year, int month, int day); 11 | void month_day(int year, int yearday, int *pmonth, int *pda); 12 | 13 | int main(void) { 14 | int day = day_of_year(2021, 2, 12); 15 | printf("day of the year: %d\n", day); 16 | 17 | int month_invalid = day_of_year(2021, 13, 1); 18 | if (month_invalid == -1) { 19 | printf("Warning: invalid month detected.\n"); 20 | } 21 | 22 | int day_invalid = day_of_year(2021, 12, 32); 23 | if (day_invalid == -2) { 24 | printf("Warning: invalid day detected.\n"); 25 | } 26 | 27 | int month; 28 | int day_month; 29 | 30 | month_day(2021, 43, &month, &day_month); 31 | printf("month: %d, day: %d\n", month, day_month); 32 | 33 | month_day(2021, 365, &month, &day_month); 34 | printf("month: %d, day: %d\n", month, day_month); 35 | 36 | month_day(2021, 366, &month, &day_month); 37 | printf("month: %d, day: %d\n", month, day_month); 38 | 39 | return 0; 40 | } 41 | 42 | int day_of_year(int year, int month, int day) { 43 | int leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; 44 | 45 | // Additional checks for month. 46 | if (month > 12) { 47 | printf("Error: a year has 12 months, so please choose a number " 48 | "betweeen 1 and 12.\n"); 49 | return -1; 50 | } 51 | 52 | // Additional checks for day. 53 | if (day > daytab[leap][month]) { 54 | printf("Error: the %d month has a maximum of %d days.\n", month, 55 | daytab[leap][month]); 56 | return -2; 57 | } 58 | 59 | int i; 60 | for (i = 1; i < month; i++) { 61 | day += daytab[leap][i]; 62 | } 63 | 64 | return day; 65 | } 66 | 67 | void month_day(int year, int yearday, int *pmonth, int *pda) { 68 | int leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; 69 | 70 | // Additional check for yearday. 71 | if ((!leap && yearday > 365) || (leap && yearday > 366)) { 72 | // Reset the provided month and day. 73 | *pmonth = 0; 74 | *pda = 0; 75 | printf("Error: year %d has %d days.\n", year, leap ? 366 : 365); 76 | 77 | return; 78 | } 79 | 80 | int i; 81 | for (i = 0; yearday > daytab[leap][i]; i++) { 82 | yearday -= daytab[leap][i]; 83 | } 84 | 85 | *pmonth = i; 86 | *pda = yearday; 87 | } 88 | -------------------------------------------------------------------------------- /chapter_7/exercise_7_04/minscanf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void minscanf(const char *format, ...); 6 | 7 | int main(void) { 8 | int decimal; 9 | int integer; 10 | // Use unsigned int for %o and %x to match the format specifier requirements 11 | unsigned int octal; 12 | unsigned int unsigned_decimal; 13 | unsigned int hexadecimal_integer; 14 | char character; 15 | char str[100]; 16 | float float_point_number; 17 | 18 | minscanf("%d", &decimal); 19 | minscanf("%i", &integer); 20 | minscanf("%o", &octal); 21 | minscanf("%u", &unsigned_decimal); 22 | minscanf("%x", &hexadecimal_integer); 23 | minscanf("%c", &character); 24 | minscanf("%s", str); 25 | minscanf("%f", &float_point_number); 26 | 27 | printf("decimal: %d\n", decimal); 28 | printf("integer: %i\n", integer); 29 | printf("octal: %o\n", octal); 30 | printf("unsigned_decimal: %u\n", unsigned_decimal); 31 | printf("hexadecimal_integer: %x\n", hexadecimal_integer); 32 | printf("character: %c\n", character); 33 | printf("str: %s\n", str); 34 | printf("float_point_number: %f\n", float_point_number); 35 | 36 | return EXIT_SUCCESS; 37 | } 38 | 39 | void minscanf(const char *format, ...) { 40 | va_list arg_p; 41 | 42 | va_start(arg_p, format); 43 | for (; *format != '\0'; ++format) { 44 | if (*format != '%') { 45 | continue; 46 | } 47 | 48 | switch (*++format) { 49 | case 'd': 50 | scanf("%d", va_arg(arg_p, int *)); 51 | break; 52 | 53 | case 'i': 54 | scanf("%i", va_arg(arg_p, int *)); 55 | break; 56 | 57 | case 'o': 58 | // %o expects unsigned int *, not int * (octal values are unsigned) 59 | scanf("%o", va_arg(arg_p, unsigned int *)); 60 | break; 61 | 62 | case 'u': 63 | scanf("%u", va_arg(arg_p, unsigned int *)); 64 | break; 65 | 66 | case 'x': 67 | // %x expects unsigned int *, not int * (hexadecimal values are 68 | // unsigned) 69 | scanf("%x", va_arg(arg_p, unsigned int *)); 70 | break; 71 | 72 | case 'c': 73 | scanf("%c", va_arg(arg_p, char *)); 74 | break; 75 | 76 | case 's': 77 | scanf("%s", va_arg(arg_p, char *)); 78 | break; 79 | 80 | case 'e': 81 | case 'f': 82 | case 'g': 83 | scanf("%f", va_arg(arg_p, float *)); 84 | break; 85 | 86 | default: 87 | break; 88 | } 89 | } 90 | va_end(arg_p); 91 | } 92 | 93 | // NOTE: run: ./minscanf <<< "1 2 3 4 5r hello 2.3" 94 | // In Unix like systems:< file or directory, << here doc, <<< here string 95 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for your interest in contributing! This guide will help you get started. 4 | 5 | ## Quick Start 6 | 7 | 1. Fork and clone the repository 8 | 2. Create a new branch: `git checkout -b my-fix` 9 | 3. Make your changes 10 | 4. Build: `make` (this also installs git hooks automatically) 11 | 5. Commit and push (the git commit hook will check formatting and lint) 12 | 6. Open a pull request 13 | 14 | ## Requirements 15 | 16 | You'll need: 17 | - A C compiler (GCC or Clang) 18 | - `make` for building 19 | - `clang-format` and `clang-tidy` for code quality checks 20 | 21 | ### Installing Dependencies 22 | 23 | **macOS:** 24 | ```shell 25 | brew install llvm 26 | ``` 27 | > The Makefile automatically finds the tools from Homebrew's LLVM installation - no need to modify your PATH. 28 | 29 | **Ubuntu / Debian:** 30 | ```shell 31 | sudo apt-get install clang-format clang-tidy 32 | ``` 33 | 34 | **Arch Linux:** 35 | ```shell 36 | sudo pacman -S clang 37 | ``` 38 | 39 | **Fedora:** 40 | ```shell 41 | sudo dnf install clang-tools-extra 42 | ``` 43 | 44 | ## Available Commands 45 | 46 | | Command | Description | 47 | |---------|-------------| 48 | | `make` | Build all exercises | 49 | | `make format` | Format all code | 50 | | `make format-check` | Check formatting (used by CI) | 51 | | `make lint` | Run linter checks | 52 | | `make check` | Run all checks (format + lint) | 53 | | `make install-hooks` | Install git pre-commit hook (runs automatically) | 54 | | `make clean` | Remove compiled files | 55 | 56 | ## Before Submitting 57 | 58 | Git hooks are installed automatically when you run `make`, so formatting and linting checks will run before each commit. 59 | 60 | You can also run checks manually: 61 | 62 | ```shell 63 | make format # Format your code 64 | make check # Run all checks 65 | ``` 66 | 67 | The CI will automatically check formatting and linting on your pull request. 68 | 69 | ## Code Style 70 | 71 | We use `clang-format` with minimal configuration (LLVM style, 4-space indentation). Just run `make format` and your code will be properly formatted. 72 | 73 | Additional rules enforced by `clang-tidy`: 74 | 75 | ### One variable per declaration 76 | 77 | Declare each variable on its own line for better readability. 78 | 79 | ```c 80 | // ✗ Avoid 81 | int x, y, z; 82 | float fahr, celsius; 83 | 84 | // ✓ Preferred 85 | int x; 86 | int y; 87 | int z; 88 | float fahr; 89 | float celsius; 90 | ``` 91 | 92 | ## What to Contribute 93 | 94 | - **Bug fixes** - Found an error in a solution? Please fix it! 95 | - **Improvements** - Better algorithms, clearer code, or better comments 96 | - **Missing solutions** - Some exercises may be missing solutions 97 | - **Documentation** - Improve explanations or add helpful comments 98 | 99 | ## Questions? 100 | 101 | Feel free to open an issue if you have questions or need help! 102 | -------------------------------------------------------------------------------- /chapter_3/exercise_3_01/binsearch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define MAXLEN 10000 5 | #define ITERATIONS 100000 6 | 7 | void initVector(int v[], int n); 8 | void printVector(int v[], int n); 9 | 10 | int binsearch_kr(int x, int v[], int n); 11 | int binsearch(int x, int v[], int n); 12 | 13 | void test_binsearch(int binsearch(int x, int v[], int n), int x, int v[], 14 | int n); 15 | 16 | int main(void) { 17 | int v[MAXLEN]; 18 | 19 | initVector(v, MAXLEN); 20 | // printVector(v, MAXLEN); 21 | 22 | int x = -1; 23 | 24 | // Test binsearch_kr() 25 | test_binsearch(binsearch_kr, x, v, MAXLEN); 26 | 27 | // Test binsearch() 28 | test_binsearch(binsearch, x, v, MAXLEN); 29 | 30 | return 0; 31 | } 32 | 33 | void initVector(int v[], int n) { 34 | int i; 35 | for (i = 0; i < n; ++i) { 36 | v[i] = i; 37 | } 38 | } 39 | 40 | void printVector(int v[], int n) { 41 | int i; 42 | 43 | printf("[ "); 44 | for (i = 0; i < n; ++i) { 45 | (i != n - 1) ? printf("%d, ", v[i]) : printf("%d ]\n", v[i]); 46 | } 47 | } 48 | 49 | int binsearch_kr(int x, int v[], int n) { 50 | int low; 51 | int mid; 52 | int high; 53 | 54 | low = 0; 55 | high = n - 1; 56 | while (low <= high) { 57 | mid = (low + high) / 2; 58 | 59 | if (x < v[mid]) { 60 | high = mid - 1; 61 | } else if (x > v[mid]) { 62 | low = mid + 1; 63 | } else { 64 | return mid; 65 | } 66 | } 67 | 68 | return -1; 69 | } 70 | 71 | int binsearch(int x, int v[], int n) { 72 | int low; 73 | int mid; 74 | int high; 75 | 76 | low = 0; 77 | high = n - 1; 78 | while (low <= high) { 79 | mid = (low + high) / 2; 80 | 81 | if (x < v[mid]) { 82 | high = mid - 1; 83 | } else { 84 | low = mid + 1; 85 | } 86 | } 87 | 88 | if (x == v[low - 1]) { 89 | return low - 1; 90 | } 91 | 92 | return -1; 93 | } 94 | 95 | void test_binsearch(int binsearch(int x, int v[], int n), int x, int v[], 96 | int n) { 97 | static int test_nr = 0; 98 | long clocks = clock(); 99 | 100 | int i; 101 | for (i = 0; i < ITERATIONS; ++i) { 102 | binsearch(x, v, n); 103 | } 104 | 105 | clocks = clock() - clocks; 106 | 107 | printf("test_%d: %lu clocks (%.4f seconds)\n", test_nr, clocks, 108 | (double)clocks / CLOCKS_PER_SEC); 109 | 110 | ++test_nr; 111 | } 112 | 113 | // NOTE: By using a simple test in the for loop there is a chance to increase 114 | // the binsearch() execution speed. Just modifying an if-else statement from 3 115 | // branches with two condition testing to 2 branches with 1 condition testing 116 | // will not increase drasticaly the program performance on modern machines, but 117 | // is a better aproach. 118 | -------------------------------------------------------------------------------- /chapter_4/exercise_4_11/getop.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define MAXOP 100 6 | #define MAXVAL 100 7 | #define BUFFSIZE 100 8 | #define NUMBER '0' 9 | 10 | int getop(char[]); 11 | void push(double); 12 | double pop(void); 13 | 14 | int main(void) { 15 | int type; 16 | double op2; 17 | char s[MAXOP]; 18 | 19 | while ((type = getop(s)) != EOF) { 20 | switch (type) { 21 | case NUMBER: 22 | push(atof(s)); 23 | break; 24 | 25 | case '+': 26 | push(pop() + pop()); 27 | break; 28 | 29 | case '-': 30 | op2 = pop(); 31 | push(pop() - op2); 32 | break; 33 | 34 | case '*': 35 | push(pop() * pop()); 36 | break; 37 | 38 | case '/': 39 | op2 = pop(); 40 | 41 | if (op2 != 0.0) { 42 | push(pop() / op2); 43 | } else { 44 | printf("Error: zero divisor.\n"); 45 | } 46 | 47 | break; 48 | 49 | case '%': 50 | op2 = pop(); 51 | 52 | if (op2 != 0.0) { 53 | push((int)pop() % (int)op2); 54 | } else { 55 | printf("Error: zero divisor.\n"); 56 | } 57 | break; 58 | 59 | case '\n': 60 | printf("result: %.8g\n", pop()); 61 | break; 62 | 63 | default: 64 | printf("Error: unknown command %s.\n", s); 65 | break; 66 | } 67 | } 68 | 69 | return 0; 70 | } 71 | 72 | int sp = 0; 73 | double val[MAXVAL]; 74 | 75 | void push(double f) { 76 | if (sp < MAXVAL) { 77 | val[sp++] = f; 78 | } else { 79 | printf("Error: stack full, can't push %g.\n", f); 80 | } 81 | } 82 | 83 | double pop(void) { 84 | if (sp > 0) { 85 | return val[--sp]; 86 | } else { 87 | printf("Error: stack empty.\n"); 88 | return 0.0; 89 | } 90 | } 91 | 92 | int getop(char s[]) { 93 | int i = 0; 94 | int c; 95 | static int buf = EOF; 96 | 97 | while ((s[0] = c = getchar()) == ' ' || c == '\t') 98 | ; 99 | 100 | s[1] = '\0'; 101 | 102 | if (!isdigit(c) && c != '.' && c != '-') { 103 | return c; 104 | } 105 | 106 | if (c == '-') { 107 | int next = getchar(); 108 | if (!isdigit(next) && next != '.') { 109 | return next; 110 | } 111 | 112 | s[i] = c; 113 | c = next = buf; 114 | } else { 115 | c = getchar(); 116 | } 117 | 118 | if (isdigit(c)) { 119 | while (isdigit(s[++i] = c = getchar())) 120 | ; 121 | } 122 | 123 | if (c == '.') { 124 | while (isdigit(s[++i] = c = getchar())) 125 | ; 126 | } 127 | 128 | if (c != EOF) { 129 | buf = c; 130 | } 131 | 132 | return NUMBER; 133 | } 134 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_10/expr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define STACK_SIZE 15 7 | 8 | unsigned char stack_pointer = 0; 9 | float stack[STACK_SIZE]; 10 | 11 | float pop(void); 12 | void push(float element); 13 | 14 | int main(int argc, char *argv[]) { 15 | char Error = 0; 16 | 17 | // Using int instead of size_t for loop variable to match signed argc 18 | // This prevents sign comparison warnings 19 | for (int i = 1; i < argc; ++i) { 20 | float number = atof(argv[i]); 21 | 22 | if (number || strcmp(argv[i], "0") == 0) { 23 | push(number); 24 | } else if (strlen(argv[i]) == 1) { 25 | if (stack_pointer >= 2 && stack_pointer < STACK_SIZE) { 26 | float number2 = pop(); 27 | float number1 = pop(); 28 | 29 | char op = *argv[i]; 30 | switch (op) { 31 | case '+': 32 | push(number1 + number2); 33 | break; 34 | 35 | case '-': 36 | push(number1 - number2); 37 | break; 38 | 39 | case '*': // This char might require to be escaped when passed 40 | // as an argument. 41 | push(number1 * number2); 42 | break; 43 | 44 | case '/': 45 | if (number2 == 0) { 46 | Error = 4; 47 | } else { 48 | push(number1 / number2); 49 | } 50 | break; 51 | 52 | default: 53 | Error = 3; 54 | break; 55 | } 56 | } else { 57 | Error = 2; 58 | } 59 | } else { 60 | Error = 1; 61 | } 62 | } 63 | 64 | if (Error) { 65 | switch (Error) { 66 | case 1: 67 | printf("Error: arguments should be numbers or one of the following " 68 | "mathematical operations: '+', '-', '*', '/'.\n"); 69 | break; 70 | 71 | case 2: 72 | printf("Error: too many or too few arguments.\n"); 73 | break; 74 | 75 | case 3: 76 | printf("Error: invalid operation. use one of the following " 77 | "mathematical operations: '+', '-', '*', '/'.\n"); 78 | break; 79 | 80 | case 4: 81 | printf("Error: division by zero (NaN).\n"); 82 | break; 83 | 84 | default: 85 | break; 86 | } 87 | 88 | return EXIT_FAILURE; 89 | } 90 | 91 | printf("result: %.3f", pop()); 92 | 93 | return EXIT_SUCCESS; 94 | } 95 | 96 | float pop(void) { 97 | if (stack_pointer > 0) { 98 | return stack[stack_pointer--]; 99 | } 100 | 101 | printf("Error: the stack is empty.\n"); 102 | return 0; 103 | } 104 | 105 | void push(float element) { 106 | if (stack_pointer < STACK_SIZE) { 107 | stack[++stack_pointer] = element; 108 | } else { 109 | printf("Error: the stack is full.\n"); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_12/detab.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define DEFAULT_TAB_LENGTH 8 7 | 8 | int is_str_uint(char *str); 9 | int is_arg_list_valid(int argc, char *argv[]); 10 | 11 | int main(int argc, char *argv[]) { 12 | if (!is_arg_list_valid(argc, argv)) { 13 | puts("Error: invalid arguments.\n"); 14 | return EXIT_FAILURE; 15 | } 16 | 17 | int c; 18 | size_t arg_pos = 1; 19 | size_t line_pos = 0; 20 | size_t tab_stop = DEFAULT_TAB_LENGTH; 21 | size_t nr_of_spaces; 22 | size_t nr_of_custom_tab_stops; 23 | size_t initial_nr_of_custom_tab_stops = argc - 1; 24 | 25 | size_t custom_tab_stop = 0; 26 | size_t custom_line_pos_start = 0; 27 | 28 | for (int i = 1; i < argc; i++) { 29 | if (argv[i][0] == '-') { 30 | custom_line_pos_start = atoi(argv[i] + 1); 31 | --initial_nr_of_custom_tab_stops; 32 | } else if (argv[i][0] == '+') { 33 | custom_tab_stop = atoi(argv[i] + 1); 34 | --initial_nr_of_custom_tab_stops; 35 | } 36 | } 37 | 38 | nr_of_custom_tab_stops = initial_nr_of_custom_tab_stops; 39 | 40 | while ((c = getchar()) != EOF) { 41 | if (c == '\t') { 42 | if (nr_of_custom_tab_stops) { 43 | tab_stop = atoi(argv[arg_pos++]); 44 | --nr_of_custom_tab_stops; 45 | } else if (custom_tab_stop) { 46 | tab_stop = custom_tab_stop; 47 | } else if (initial_nr_of_custom_tab_stops) { 48 | tab_stop = 1; 49 | } 50 | 51 | if (custom_line_pos_start) { 52 | if (line_pos >= custom_line_pos_start) { 53 | nr_of_spaces = tab_stop; 54 | } else { 55 | nr_of_spaces = 1; 56 | } 57 | } else { 58 | nr_of_spaces = tab_stop - line_pos % tab_stop; 59 | } 60 | 61 | while (nr_of_spaces) { 62 | putchar(' '); 63 | ++line_pos; 64 | --nr_of_spaces; 65 | } 66 | } else { 67 | putchar(c); 68 | ++line_pos; 69 | 70 | if (c == '\n') { 71 | arg_pos = 1; 72 | line_pos = 0; 73 | nr_of_custom_tab_stops = initial_nr_of_custom_tab_stops; 74 | } 75 | } 76 | } 77 | 78 | return EXIT_SUCCESS; 79 | } 80 | 81 | int is_str_uint(char *str) { 82 | for (size_t i = 0; i < strlen(str); ++i) { 83 | if (!isdigit(str[i])) { 84 | return 0; 85 | } 86 | } 87 | return 1; 88 | } 89 | 90 | int is_arg_list_valid(int argc, char *argv[]) { 91 | for (int i = 1; i < argc; ++i) { 92 | if (argv[i][0] == '-' || argv[i][0] == '+') { 93 | if (argc > 3 || !is_str_uint(argv[i] + 1)) { 94 | return 0; 95 | } 96 | continue; 97 | } 98 | 99 | if (!is_str_uint(argv[i]) || 100 | (i > 1 && atoi(argv[i - 1]) > atoi(argv[i]))) { 101 | return 0; 102 | } 103 | } 104 | return 1; 105 | } 106 | 107 | // NOTE: The current program works in a similar fashion as expand. 108 | // run: ./detab 4 8 12 16 < file_tabs.txt > file_spaces.txt 109 | // run: ./detab +8 -2 < file_tabs.txt > file_spaces.txt 110 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_07/readlines.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define MAXLINES 5000 // max # of lines to be sorted 6 | #define MAXLEN 1000 7 | #define MAXSTORE 10000 // max # of chars from all lines to be stored 8 | 9 | char *line_ptr[MAXLINES]; // pointers to text lines 10 | 11 | size_t get_line(char line[], size_t max_line_len); 12 | 13 | int readlines(char *line_ptr[], int max_nr_of_lines, char *stored_lines); 14 | void writelines(char *line_ptr[], int nr_of_lines); 15 | 16 | void swap(char *v[], int i, int j); 17 | void quick_sort(char *line_ptr[], int left, int right); 18 | 19 | int main(void) { 20 | int nr_of_lines; // # of input lines read 21 | char stored_lines[MAXSTORE]; // # of chars to be stored for all lines 22 | 23 | if ((nr_of_lines = readlines(line_ptr, MAXLINES, stored_lines)) >= 0) { 24 | quick_sort(line_ptr, 0, nr_of_lines - 1); 25 | printf("-----\n"); 26 | writelines(line_ptr, nr_of_lines); 27 | return 0; 28 | } else { 29 | printf("Error: input too big to sort.\n"); 30 | return 1; 31 | } 32 | } 33 | 34 | int readlines(char *line_ptr[], int max_nr_of_lines, char *stored_lines) { 35 | int len; 36 | int nr_of_lines; 37 | 38 | char *p = 39 | stored_lines + strlen(stored_lines); // Init p with the first empty 40 | // position from stored_lines 41 | char line[MAXLEN]; 42 | 43 | nr_of_lines = 0; 44 | while ((len = get_line(line, MAXLEN)) > 0) { 45 | // Checking if the current # of lines exceeds the max # of lines that 46 | // can be stored Also checking if the max # of chars from the 47 | // stored_lines buffer is not exceeded 48 | if (nr_of_lines >= max_nr_of_lines || 49 | stored_lines + MAXSTORE - p < len) { 50 | return -1; 51 | } else { 52 | line[len - 1] = '\0'; // Delete newline 53 | strcpy(p, line); 54 | line_ptr[nr_of_lines++] = p; 55 | p += len; // Move p to the next empty position 56 | } 57 | } 58 | 59 | return nr_of_lines; 60 | } 61 | 62 | void writelines(char *line_ptr[], int nr_of_lines) { 63 | while (nr_of_lines-- > 0) { 64 | printf("%s\n", *line_ptr++); 65 | } 66 | } 67 | 68 | size_t get_line(char line[], size_t max_line_len) { 69 | int c; 70 | size_t i; 71 | 72 | for (i = 0; i < max_line_len - 1 && (c = getc(stdin)) != EOF && c != '\n'; 73 | ++i) { 74 | line[i] = c; 75 | } 76 | 77 | if (c == '\n') { 78 | line[i] = c; 79 | ++i; 80 | } 81 | 82 | line[i] = '\0'; 83 | 84 | return i; 85 | } 86 | 87 | void quick_sort(char *v[], int left, int right) { 88 | int i; 89 | int last; 90 | 91 | // Do nothing if the array contains less than 2 elements 92 | if (left >= right) { 93 | return; 94 | } 95 | 96 | swap(v, left, (left + right) / 2); 97 | last = left; 98 | 99 | for (i = left + 1; i <= right; ++i) { 100 | if (strcmp(v[i], v[left]) < 0) { 101 | swap(v, ++last, i); 102 | } 103 | } 104 | 105 | swap(v, left, last); 106 | quick_sort(v, left, last - 1); 107 | quick_sort(v, last + 1, right); 108 | } 109 | 110 | void swap(char *v[], int i, int j) { 111 | char *temp; 112 | 113 | temp = v[i]; 114 | v[i] = v[j]; 115 | v[j] = temp; 116 | } 117 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_12/entab.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define DEFAULT_TAB_LENGTH 8 7 | 8 | int is_str_uint(char *str); 9 | int is_arg_list_valid(int argc, char *argv[]); 10 | 11 | int main(int argc, char *argv[]) { 12 | if (!is_arg_list_valid(argc, argv)) { 13 | puts("Error: invalid arguments.\n"); 14 | return EXIT_FAILURE; 15 | } 16 | 17 | int c; 18 | size_t arg_pos = 1; 19 | size_t line_pos = 0; 20 | size_t tab_stop = DEFAULT_TAB_LENGTH; 21 | size_t nr_of_spaces = 0; 22 | size_t nr_of_custom_tab_stops; 23 | size_t initial_nr_of_custom_tab_stops = argc - 1; 24 | 25 | size_t custom_tab_stop = 0; 26 | size_t custom_line_pos_start = 0; 27 | 28 | for (int i = 1; i < argc; i++) { 29 | if (argv[i][0] == '-') { 30 | custom_line_pos_start = atoi(argv[i] + 1); 31 | --initial_nr_of_custom_tab_stops; 32 | } else if (argv[i][0] == '+') { 33 | custom_tab_stop = atoi(argv[i] + 1); 34 | --initial_nr_of_custom_tab_stops; 35 | } 36 | } 37 | 38 | nr_of_custom_tab_stops = initial_nr_of_custom_tab_stops; 39 | 40 | while ((c = getchar()) != EOF) { 41 | ++line_pos; 42 | 43 | if (c == ' ') { 44 | ++nr_of_spaces; 45 | 46 | if (nr_of_custom_tab_stops) { 47 | tab_stop = atoi(argv[arg_pos]); 48 | } else if (custom_tab_stop) { 49 | tab_stop = custom_tab_stop; 50 | } else if (initial_nr_of_custom_tab_stops) { 51 | tab_stop = 1; 52 | } 53 | 54 | if ((!custom_line_pos_start && line_pos % tab_stop == 0 && 55 | nr_of_spaces > 1) || 56 | (custom_line_pos_start && nr_of_spaces == tab_stop) || 57 | line_pos <= custom_line_pos_start) { 58 | putchar('\t'); 59 | 60 | if (nr_of_custom_tab_stops) { 61 | ++arg_pos; 62 | --nr_of_custom_tab_stops; 63 | } 64 | 65 | nr_of_spaces = 0; 66 | } 67 | } else { 68 | while (nr_of_spaces) { 69 | putchar(' '); 70 | --nr_of_spaces; 71 | } 72 | 73 | if (c == '\n') { 74 | arg_pos = 1; 75 | line_pos = 0; 76 | nr_of_custom_tab_stops = initial_nr_of_custom_tab_stops; 77 | } 78 | 79 | putchar(c); 80 | } 81 | } 82 | 83 | return EXIT_SUCCESS; 84 | } 85 | 86 | int is_str_uint(char *str) { 87 | for (size_t i = 0; i < strlen(str); ++i) { 88 | if (!isdigit(str[i])) { 89 | return 0; 90 | } 91 | } 92 | return 1; 93 | } 94 | 95 | int is_arg_list_valid(int argc, char *argv[]) { 96 | for (int i = 1; i < argc; ++i) { 97 | if (argv[i][0] == '-' || argv[i][0] == '+') { 98 | if (argc > 3 || !is_str_uint(argv[i] + 1)) { 99 | return 0; 100 | } 101 | continue; 102 | } 103 | 104 | if (!is_str_uint(argv[i]) || 105 | (i > 1 && atoi(argv[i - 1]) > atoi(argv[i]))) { 106 | return 0; 107 | } 108 | } 109 | return 1; 110 | } 111 | 112 | // NOTE: The current program works in a similar fashion as unexpand. 113 | // run: ./entab 4 8 12 16 > file_tabs.txt < file_spaces.txt 114 | -------------------------------------------------------------------------------- /chapter_8/exercise_8_02/syscalls.c: -------------------------------------------------------------------------------- 1 | #include "syscalls.h" 2 | #include 3 | 4 | #define PERMISSIONS 0666 // RW for owners, group, others 5 | 6 | FILE _io_buffer[MAX_NR_OF_OPEN_FILES] = { 7 | {0, (char *)0, (char *)0, {1, 0, 0, 0, 0}, 0}, // stdin 8 | {0, (char *)0, (char *)0, {0, 1, 0, 0, 0}, 1}, // stdout 9 | {0, (char *)0, (char *)0, {0, 1, 1, 0, 0}, 2} // stderr 10 | }; 11 | 12 | void *malloc(long unsigned int size); 13 | long int lseek(int file_descriptor, long int offset, int whence); 14 | long int read(int file_descriptor, void *buffer, long unsigned int nr_of_bytes); 15 | long int write(int file_descriptor, void *buffer, 16 | long unsigned int nr_of_bytes); 17 | 18 | int _fill_buffer(FILE *file_p) { 19 | int buffer_size; 20 | 21 | if (file_p->flag._READ == 0 || file_p->flag._EOF == 1 || 22 | file_p->flag._ERR == 1) { 23 | return EOF; 24 | } 25 | 26 | buffer_size = (file_p->flag._UNBUF == 1) ? 1 : BUFFER_SIZE; 27 | 28 | if (file_p->base == NULL) { 29 | if ((file_p->base = (char *)malloc(buffer_size)) == NULL) { 30 | return EOF; 31 | } 32 | } 33 | 34 | file_p->next_char_pos_p = file_p->base; 35 | file_p->counter = 36 | read(file_p->file_descriptor, file_p->next_char_pos_p, buffer_size); 37 | 38 | if (--file_p->counter < 0) { 39 | if (file_p->counter == -1) { 40 | file_p->flag._EOF = 1; 41 | } else { 42 | file_p->flag._ERR = 1; 43 | } 44 | 45 | file_p->counter = 0; 46 | return EOF; 47 | } 48 | 49 | return (unsigned char)*file_p->next_char_pos_p++; 50 | } 51 | 52 | FILE *file_open(char *name, char *mode) { 53 | int file_descriptor; 54 | FILE *file_p; 55 | 56 | if (*mode != 'r' && *mode != 'w' && *mode != 'a') { 57 | return NULL; 58 | } 59 | 60 | for (file_p = _io_buffer; file_p < _io_buffer + MAX_NR_OF_OPEN_FILES; 61 | ++file_p) { 62 | if (file_p->flag._READ == 0 && file_p->flag._WRITE == 0) { 63 | break; // found free slot 64 | } 65 | } 66 | 67 | if (file_p >= _io_buffer + MAX_NR_OF_OPEN_FILES) { 68 | return NULL; // no free slots 69 | } 70 | 71 | if (*mode == 'w') { 72 | file_descriptor = creat(name, PERMISSIONS); 73 | } else if (*mode == 'a') { 74 | if ((file_descriptor = open(name, O_WRONLY, 0)) == -1) { 75 | file_descriptor = creat(name, PERMISSIONS); 76 | } 77 | lseek(file_descriptor, 0L, 2); 78 | } else { 79 | file_descriptor = open(name, O_RDONLY, 0); 80 | } 81 | 82 | if (file_descriptor == -1) { 83 | return NULL; // couldn't access name 84 | } 85 | 86 | file_p->file_descriptor = file_descriptor; 87 | file_p->counter = 0; 88 | file_p->base = NULL; 89 | file_p->flag._EOF = 0; 90 | file_p->flag._ERR = 0; 91 | file_p->flag._READ = (*mode == 'r') ? 1 : 0; 92 | file_p->flag._WRITE = (*mode == 'r') ? 0 : 1; 93 | 94 | return file_p; 95 | } 96 | 97 | int main(void) { 98 | FILE *file_p; 99 | 100 | if ((file_p = file_open("syscalls.c", "r")) == NULL) { 101 | write(1, "Error: could not open the file.\n", 33); 102 | return EXIT_FAILURE; 103 | } else { 104 | char c; 105 | while ((c = getc(file_p)) != EOF) { 106 | write(1, &c, 1); 107 | } 108 | } 109 | 110 | return EXIT_SUCCESS; 111 | } 112 | 113 | // NOTE: Using bit fields leads to more verbose syntax. However, it's more 114 | // readable. 115 | -------------------------------------------------------------------------------- /chapter_1/exercise_1_24/check_syntax.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MAXSTR 10000 4 | 5 | #define TRUE (1 == 1) 6 | #define FALSE !TRUE 7 | 8 | int get_str(char str[], int limit); 9 | void check_syntax(char str[]); 10 | 11 | int main(void) { 12 | char str[MAXSTR]; 13 | 14 | get_str(str, MAXSTR); 15 | check_syntax(str); 16 | 17 | return 0; 18 | } 19 | 20 | int get_str(char str[], int limit) { 21 | int c; 22 | int i = 0; 23 | 24 | while (i < limit - 1 && (c = getchar()) != EOF) { 25 | str[i++] = c; 26 | } 27 | str[i] = '\0'; 28 | 29 | return i; 30 | } 31 | 32 | void check_syntax(char str[]) { 33 | int parentheses = 0; 34 | int brackets = 0; 35 | int braces = 0; 36 | 37 | int single_quotes = FALSE; 38 | int double_quotes = FALSE; 39 | 40 | int block_comment = FALSE; 41 | int line_comment = FALSE; 42 | 43 | int i = 0; 44 | while (str[i] != '\0' && parentheses >= 0 && brackets >= 0 && braces >= 0) { 45 | if (!line_comment && !block_comment && !single_quotes && 46 | !double_quotes) { 47 | if (str[i] == '(') { 48 | ++parentheses; 49 | } else if (str[i] == ')') { 50 | --parentheses; 51 | } 52 | 53 | if (str[i] == '[') { 54 | ++brackets; 55 | } else if (str[i] == ']') { 56 | --brackets; 57 | } 58 | 59 | if (str[i] == '{') { 60 | ++braces; 61 | } else if (str[i] == '}') { 62 | --braces; 63 | } 64 | } 65 | 66 | if (!line_comment && !block_comment) { 67 | if (str[i] == '\'' && !single_quotes && !double_quotes) { 68 | single_quotes = TRUE; 69 | } else if (single_quotes && str[i] == '\'' && 70 | (str[i - 1] != '\\' || str[i - 2] == '\\')) { 71 | single_quotes = FALSE; 72 | } 73 | 74 | if (str[i] == '"' && !single_quotes && !double_quotes) { 75 | double_quotes = TRUE; 76 | } else if (double_quotes && str[i] == '"' && 77 | (str[i - 1] != '\\' || str[i - 2] == '\\')) { 78 | double_quotes = FALSE; 79 | } 80 | } 81 | 82 | if (!single_quotes && !double_quotes) { 83 | if (str[i] == '/' && str[i + 1] == '*' && !line_comment) { 84 | block_comment = TRUE; 85 | } else if (str[i] == '*' && str[i + 1] == '/') { 86 | block_comment = FALSE; 87 | } 88 | 89 | if (str[i] == '/' && str[i + 1] == '/' && !block_comment) { 90 | line_comment = TRUE; 91 | } else if (str[i] == '\n') { 92 | line_comment = FALSE; 93 | } 94 | } 95 | 96 | ++i; 97 | } 98 | 99 | if (parentheses) { 100 | printf("Error: unbalanced parentheses.\n"); 101 | } 102 | 103 | if (brackets) { 104 | printf("Error: unbalanced brackets.\n"); 105 | } 106 | 107 | if (braces) { 108 | printf("Error: unbalanced braces.\n"); 109 | } 110 | 111 | if (single_quotes) { 112 | printf("Error: unbalanced single quotes.\n"); 113 | } 114 | 115 | if (double_quotes) { 116 | printf("Error: unbalanced double quotes.\n"); 117 | } 118 | 119 | if (block_comment) { 120 | printf("Error: block comment not closed.\n"); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /chapter_6/exercise_6_05/undef.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define HASH_SIZE 101 7 | 8 | enum boolean { FALSE, TRUE }; 9 | 10 | struct list_node { 11 | char *name; 12 | char *definition; 13 | struct list_node *next; 14 | }; 15 | 16 | // There is a strdup available with POSIX, but it's not part of ISO C. 17 | char *str_dup(char *src); 18 | 19 | size_t hash(char *str); 20 | struct list_node *lookup(char *str); 21 | struct list_node *install(char *name, char *definition); 22 | enum boolean undef(char *name); 23 | 24 | static struct list_node *hash_table[HASH_SIZE]; 25 | 26 | int main(void) { 27 | install("TEST", "test"); 28 | 29 | // Install other collision values for the same hash as for "TEST" -> 51. 30 | install("TSHe", "test1"); 31 | install("UPXD", "test2"); 32 | install("9iww", "test3"); 33 | install("mY1a", "test4"); 34 | install("uuoT", "test5"); 35 | 36 | struct list_node *node_p = lookup("TEST"); 37 | if (node_p == NULL) { 38 | puts("Error: hash value not found."); 39 | } else { 40 | printf("%s: %s\n", node_p->name, node_p->definition); 41 | if (undef("TEST") && lookup("TEST") == NULL) { 42 | printf("'%s' was undefined successfully.\n", "TEST"); 43 | } else { 44 | printf("Error: failed to undefine '%s'.\n", "TEST"); 45 | } 46 | } 47 | 48 | return EXIT_SUCCESS; 49 | } 50 | 51 | char *str_dup(char *src) { 52 | char *dest = (char *)malloc(strlen(src) + 1); 53 | if (dest != NULL) { 54 | strcpy(dest, src); 55 | } 56 | return dest; 57 | } 58 | 59 | size_t hash(char *str) { 60 | size_t hash_value = 0; 61 | while (*str != '\0') { 62 | hash_value = *str + 31 * hash_value; 63 | ++str; 64 | } 65 | return hash_value % HASH_SIZE; 66 | } 67 | 68 | struct list_node *lookup(char *str) { 69 | struct list_node *node_p; 70 | for (node_p = hash_table[hash(str)]; node_p != NULL; 71 | node_p = node_p->next) { 72 | if (strcmp(str, node_p->name) == 0) { 73 | return node_p; 74 | } 75 | } 76 | return NULL; 77 | } 78 | 79 | struct list_node *install(char *name, char *definition) { 80 | struct list_node *node_p; 81 | if ((node_p = lookup(name)) == NULL) { 82 | node_p = (struct list_node *)malloc(sizeof(*node_p)); 83 | if (node_p == NULL || (node_p->name = str_dup(name)) == NULL) { 84 | return NULL; 85 | } 86 | size_t hash_value = hash(name); 87 | node_p->next = hash_table[hash_value]; 88 | hash_table[hash_value] = node_p; 89 | } else { 90 | free(node_p->definition); 91 | } 92 | 93 | if ((node_p->definition = str_dup(definition)) == NULL) { 94 | return NULL; 95 | } 96 | 97 | return node_p; 98 | } 99 | 100 | enum boolean undef(char *name) { 101 | struct list_node *node_p; 102 | struct list_node *prev_node_p; 103 | size_t hash_value = hash(name); 104 | for (node_p = hash_table[hash_value], prev_node_p = NULL; node_p != NULL; 105 | prev_node_p = node_p, node_p = node_p->next) { 106 | if (strcmp(name, node_p->name) == 0) { 107 | free(node_p->name); 108 | free(node_p->definition); 109 | 110 | if (prev_node_p == NULL) { 111 | hash_table[hash_value] = node_p->next; 112 | } else { 113 | prev_node_p->next = node_p->next; 114 | } 115 | 116 | free(node_p); 117 | return TRUE; 118 | } 119 | } 120 | 121 | return FALSE; 122 | } 123 | -------------------------------------------------------------------------------- /chapter_4/exercise_4_03/calculator.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define MAXOP 100 6 | #define MAXVAL 100 7 | #define BUFFSIZE 100 8 | #define NUMBER '0' 9 | 10 | int getop(char[]); 11 | void push(double); 12 | double pop(void); 13 | 14 | int main(void) { 15 | int type; 16 | double op2; 17 | char s[MAXOP]; 18 | 19 | while ((type = getop(s)) != EOF) { 20 | switch (type) { 21 | case NUMBER: 22 | push(atof(s)); 23 | break; 24 | 25 | case '+': 26 | push(pop() + pop()); 27 | break; 28 | 29 | case '-': 30 | op2 = pop(); 31 | push(pop() - op2); 32 | break; 33 | 34 | case '*': 35 | push(pop() * pop()); 36 | break; 37 | 38 | case '/': 39 | op2 = pop(); 40 | 41 | if (op2 != 0.0) { 42 | push(pop() / op2); 43 | } else { 44 | printf("Error: zero divisor.\n"); 45 | } 46 | 47 | break; 48 | 49 | case '%': 50 | op2 = pop(); 51 | 52 | if (op2 != 0.0) { 53 | push((int)pop() % (int)op2); 54 | } else { 55 | printf("Error: zero divisor.\n"); 56 | } 57 | break; 58 | 59 | case '\n': 60 | printf("result: %.8g\n", pop()); 61 | break; 62 | 63 | default: 64 | printf("Error: unknown command %s.\n", s); 65 | break; 66 | } 67 | } 68 | 69 | return 0; 70 | } 71 | 72 | int sp = 0; 73 | double val[MAXVAL]; 74 | 75 | void push(double f) { 76 | if (sp < MAXVAL) { 77 | val[sp++] = f; 78 | } else { 79 | printf("Error: stack full, can't push %g.\n", f); 80 | } 81 | } 82 | 83 | double pop(void) { 84 | if (sp > 0) { 85 | return val[--sp]; 86 | } else { 87 | printf("Error: stack empty.\n"); 88 | return 0.0; 89 | } 90 | } 91 | 92 | int bufp = 0; 93 | char buf[BUFFSIZE]; 94 | 95 | int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar(); } 96 | 97 | void ungetch(int c) { 98 | if (bufp >= BUFFSIZE) { 99 | printf("ungetch: too many characters\n"); 100 | } else { 101 | buf[bufp++] = c; 102 | } 103 | } 104 | 105 | int getop(char s[]) { 106 | int i = 0; 107 | int c; 108 | 109 | while ((s[0] = c = getch()) == ' ' || c == '\t') 110 | ; 111 | 112 | s[1] = '\0'; 113 | 114 | if (!isdigit(c) && c != '.' && c != '-') { 115 | return c; 116 | } 117 | 118 | if (c == '-') { 119 | int next = getch(); 120 | if (next == '\n' || next == ' ' || next == '\t') { 121 | ungetch(next); 122 | return c; // return '-' as operator 123 | } else if (!isdigit(next) && next != '.') { 124 | return next; // not a number 125 | } else // number like "-5", "-.6" etc, next is digit or '.' 126 | { 127 | s[++i] = c = next; 128 | } 129 | } else { 130 | c = getch(); 131 | } 132 | 133 | if (isdigit(c)) { 134 | while (isdigit(s[++i] = c = getch())) 135 | ; 136 | } 137 | 138 | if (c == '.') { 139 | while (isdigit(s[++i] = c = getch())) 140 | ; 141 | } 142 | 143 | if (c != EOF) { 144 | ungetch(c); 145 | } 146 | 147 | return NUMBER; 148 | } 149 | 150 | // NOTE: The getch() function check if there are characters in a buffer. If 151 | // there are characters the function will return the last character from the 152 | // buffer, else getchar() function is called. The ungetch() function will push 153 | // the last character in the buffer. 154 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_19/undcl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAX_TOKEN_LEN 100 7 | #define MAX_OUT_LEN 1000 8 | 9 | void skip_blanks(void); 10 | void skip_comments(void); 11 | 12 | void get_name(char *dest, const size_t max_len); 13 | int get_next_token(void); 14 | 15 | enum token_type { 16 | NAME, 17 | PARENS, 18 | BRACKETS, 19 | PAREN_OPEN = '(', 20 | PAREN_CLOSE = ')', 21 | BRACKET_OPEN = '[', 22 | BRACKET_CLOSE = ']' 23 | }; 24 | 25 | int next_token; 26 | 27 | char token[MAX_TOKEN_LEN]; 28 | char out[MAX_OUT_LEN]; 29 | 30 | int main(void) { 31 | char temp[MAX_OUT_LEN + MAX_TOKEN_LEN]; 32 | 33 | while (get_next_token() != EOF) { 34 | strcpy(out, token); 35 | 36 | while (get_next_token() != '\n') { 37 | if (next_token == PARENS) { 38 | if (out[0] == '*') { 39 | sprintf(temp, "(%s)", out); 40 | strcpy(out, temp); 41 | } 42 | 43 | strcat(out, token); 44 | } else if (next_token == BRACKETS) { 45 | if (out[0] == '*') { 46 | sprintf(temp, "(%s)", out); 47 | strcpy(out, temp); 48 | } 49 | 50 | sprintf(temp, "[%s]", token); 51 | strcat(out, temp); 52 | } else if (next_token == '*') { 53 | sprintf(temp, "*%s", out); 54 | strcpy(out, temp); 55 | } else if (next_token == NAME) { 56 | sprintf(temp, "%s %s", token, out); 57 | strcpy(out, temp); 58 | } else { 59 | printf("Syntax Error: Invalid input at %s.\n", token); 60 | } 61 | } 62 | puts(out); 63 | } 64 | 65 | return EXIT_SUCCESS; 66 | } 67 | 68 | void skip_blanks(void) { 69 | int c; 70 | while (isblank(c = getc(stdin))) 71 | ; 72 | ungetc(c, stdin); 73 | } 74 | 75 | void skip_comments(void) { 76 | int c = getc(stdin); 77 | if (c == '/') { 78 | c = getc(stdin); 79 | if (c == '/') { 80 | while ((c = getc(stdin)) != '\n' && c != EOF) 81 | ; 82 | } else if (c == '*') { 83 | while ((c = getc(stdin)) != '*' && c != EOF) 84 | ; 85 | c = getc(stdin); 86 | if (c == '/') { 87 | ungetc('\n', stdin); 88 | return; 89 | } 90 | } 91 | } 92 | ungetc(c, stdin); 93 | } 94 | 95 | void get_name(char *dest, const size_t max_len) { 96 | int c; 97 | size_t i = 0; 98 | while ((isalnum(c = getc(stdin)) || c == '_') && i < max_len) { 99 | dest[i++] = c; 100 | } 101 | dest[i] = '\0'; 102 | ungetc(c, stdin); 103 | } 104 | 105 | int get_next_token(void) { 106 | skip_blanks(); 107 | skip_comments(); 108 | skip_blanks(); 109 | 110 | int c = getc(stdin); 111 | if (c == '(') { 112 | skip_blanks(); 113 | 114 | c = getc(stdin); 115 | if (c == ')') { 116 | strcpy(token, "()"); 117 | return next_token = PARENS; 118 | } 119 | ungetc(c, stdin); 120 | 121 | return next_token = PAREN_OPEN; 122 | } else if (c == '[') { 123 | skip_blanks(); 124 | get_name(token, MAX_TOKEN_LEN); 125 | skip_blanks(); 126 | 127 | c = getc(stdin); 128 | if (c == ']') { 129 | return next_token = BRACKETS; 130 | } 131 | ungetc(c, stdin); 132 | 133 | return next_token = BRACKET_OPEN; 134 | } else if (isalpha(c)) { 135 | ungetc(c, stdin); 136 | get_name(token, MAX_TOKEN_LEN); 137 | return next_token = NAME; 138 | } 139 | 140 | return next_token = c; 141 | } 142 | 143 | // NOTE: run: ./undcl < test.txt 144 | -------------------------------------------------------------------------------- /chapter_6/exercise_6_01/count_c_keywords.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAX_WORD_LEN 100 7 | #define NR_OF_KEYS sizeof(keytab) / sizeof(keytab[0]) 8 | 9 | struct key { 10 | char *word; 11 | int count; 12 | }; 13 | 14 | void skip_blanks(void); 15 | void skip_comments(void); 16 | void skip_chars_between(char start, char end); 17 | void skip_char_literal(void); 18 | void skip_string_literal(void); 19 | 20 | int get_word(char *word, int max_word_len); 21 | int bin_search(char *word, struct key arr[], int arr_len); 22 | 23 | struct key keytab[] = { 24 | {"auto", 0}, {"break", 0}, {"case", 0}, {"char", 0}, 25 | {"const", 0}, {"continue", 0}, {"default", 0}, {"do", 0}, 26 | {"double", 0}, {"else", 0}, {"enum", 0}, {"extern", 0}, 27 | {"float", 0}, {"for", 0}, {"goto", 0}, {"if", 0}, 28 | {"int", 0}, {"long", 0}, {"register", 0}, {"return", 0}, 29 | {"short", 0}, {"signed", 0}, {"size_t", 0}, {"sizeof", 0}, 30 | {"static", 0}, {"struct", 0}, {"switch", 0}, {"typedef", 0}, 31 | {"union", 0}, {"unsigned", 0}, {"void", 0}, {"volatile", 0}, 32 | {"while", 0}, 33 | }; 34 | 35 | int main(void) { 36 | int n; 37 | char word[MAX_WORD_LEN]; 38 | 39 | while (get_word(word, MAX_WORD_LEN) != EOF) { 40 | if (isalpha(word[0])) { 41 | if ((n = bin_search(word, keytab, NR_OF_KEYS)) >= 0) { 42 | keytab[n].count++; 43 | } 44 | } 45 | } 46 | 47 | for (size_t i = 0; i < NR_OF_KEYS; ++i) { 48 | if (keytab[i].count) { 49 | printf("%4d %s\n", keytab[i].count, keytab[i].word); 50 | } 51 | } 52 | 53 | return EXIT_SUCCESS; 54 | } 55 | 56 | void skip_blanks(void) { 57 | int c; 58 | while (isblank(c = getc(stdin))) 59 | ; 60 | ungetc(c, stdin); 61 | } 62 | 63 | void skip_comments(void) { 64 | int c = getc(stdin); 65 | if (c == '/') { 66 | c = getc(stdin); 67 | if (c == '/') { 68 | while ((c = getc(stdin)) != '\n' && c != EOF) 69 | ; 70 | } else if (c == '*') { 71 | while ((c = getc(stdin)) != '*' && c != EOF) 72 | ; 73 | c = getc(stdin); 74 | if (c == '/') { 75 | ungetc('\n', stdin); 76 | return; 77 | } 78 | } 79 | } 80 | ungetc(c, stdin); 81 | } 82 | 83 | void skip_chars_between(char start, char end) { 84 | int c = getc(stdin); 85 | if (c == start) { 86 | while ((c = getc(stdin)) != EOF) { 87 | if (c == '\\') { 88 | if ((c = getc(stdin)) == EOF) { 89 | break; 90 | } 91 | } else if (c == end) { 92 | return; 93 | } 94 | } 95 | } 96 | ungetc(c, stdin); 97 | } 98 | 99 | void skip_char_literal(void) { skip_chars_between('\'', '\''); } 100 | 101 | void skip_string_literal(void) { skip_chars_between('"', '"'); } 102 | 103 | int get_word(char *word, int max_word_len) { 104 | skip_blanks(); 105 | skip_comments(); 106 | skip_char_literal(); 107 | skip_string_literal(); 108 | 109 | int c = getc(stdin); 110 | int i = 0; 111 | 112 | if (c != EOF) { 113 | word[i++] = c; 114 | } 115 | 116 | if (!isalpha(c) && c != '_') { 117 | word[i] = '\0'; 118 | return c; 119 | } 120 | 121 | while ((isalnum(c = getc(stdin)) || c == '_') && i < max_word_len) { 122 | word[i++] = c; 123 | } 124 | ungetc(c, stdin); 125 | word[i] = '\0'; 126 | 127 | return word[0]; 128 | } 129 | 130 | int bin_search(char *word, struct key arr[], int arr_len) { 131 | int low = 0; 132 | int high = arr_len - 1; 133 | int mid; 134 | 135 | while (low <= high) { 136 | mid = (low + high) / 2; 137 | 138 | int cond = strcmp(word, arr[mid].word); 139 | if (cond < 0) { 140 | high = mid - 1; 141 | } else if (cond > 0) { 142 | low = mid + 1; 143 | } else { 144 | return mid; 145 | } 146 | } 147 | 148 | return -1; 149 | } 150 | 151 | // NOTE: run: ./count_c_keywords < count_c_keywords.c 152 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_13/tail.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAX_NR_OF_LINES 5000 7 | #define DEFAULT_NR_OF_LINES_TO_PRINT 10 8 | 9 | #define MAX_LINE_LEN 1000 10 | #define ALLOC_SIZE 10000 11 | 12 | static char alloc_buf[ALLOC_SIZE]; 13 | static char *alloc_p = alloc_buf; 14 | 15 | char *alloc(size_t size); 16 | void afree(char *ptr); 17 | 18 | size_t get_line(char line[], size_t max_line_len); 19 | 20 | int is_str_uint(char *str); 21 | int is_arg_list_valid(int argc, char *argv[]); 22 | 23 | size_t read_lines(char *line_ptr[], const size_t max_nr_of_lines); 24 | void write_lines(char *line_ptr[], const size_t nr_of_lines_to_print, 25 | const size_t total_nr_of_lines); 26 | 27 | int main(int argc, char *argv[]) { 28 | if (!is_arg_list_valid(argc, argv)) { 29 | puts("Error: invalid arguments.\n"); 30 | return EXIT_FAILURE; 31 | } 32 | 33 | size_t nr_of_lines_to_print = DEFAULT_NR_OF_LINES_TO_PRINT; 34 | 35 | if (argc == 2) { 36 | nr_of_lines_to_print = atoi(argv[argc - 1] + 1); 37 | } 38 | 39 | size_t total_nr_of_lines; 40 | char *line_ptr[MAX_NR_OF_LINES]; 41 | 42 | // Cast -1 to (size_t)-1 to compare with unsigned size_t return value 43 | // This prevents sign comparison warnings while maintaining error checking 44 | if ((total_nr_of_lines = read_lines(line_ptr, MAX_NR_OF_LINES)) != 45 | (size_t)-1) { 46 | write_lines(line_ptr, nr_of_lines_to_print, total_nr_of_lines); 47 | } else { 48 | puts("Error: input too large.\n"); 49 | return EXIT_FAILURE; 50 | } 51 | 52 | return EXIT_SUCCESS; 53 | } 54 | 55 | int is_str_uint(char *str) { 56 | for (size_t i = 0; i < strlen(str); ++i) { 57 | if (!isdigit(str[i])) { 58 | return 0; 59 | } 60 | } 61 | return 1; 62 | } 63 | 64 | int is_arg_list_valid(int argc, char *argv[]) { 65 | if (argc > 2 || (argc == 2 && (argv[argc - 1][0] != '-' || 66 | !is_str_uint(argv[argc - 1] + 1)))) { 67 | return 0; 68 | } 69 | return 1; 70 | } 71 | 72 | size_t get_line(char line[], size_t max_line_len) { 73 | int c; 74 | size_t i; 75 | 76 | for (i = 0; i < max_line_len - 1 && (c = getc(stdin)) != EOF && c != '\n'; 77 | ++i) { 78 | line[i] = c; 79 | } 80 | 81 | if (c == '\n') { 82 | line[i] = c; 83 | ++i; 84 | } 85 | 86 | line[i] = '\0'; 87 | 88 | return i; 89 | } 90 | 91 | size_t read_lines(char *line_ptr[], const size_t max_nr_of_lines) { 92 | size_t line_length; 93 | size_t nr_of_lines = 0; 94 | 95 | char *current_line = alloc(MAX_LINE_LEN); 96 | char *current_line_copy = NULL; 97 | 98 | while ((line_length = get_line(current_line, MAX_LINE_LEN))) { 99 | if (nr_of_lines >= max_nr_of_lines || 100 | (current_line_copy = alloc(line_length)) == NULL) { 101 | return -1; 102 | } else { 103 | current_line[line_length - 1] = '\0'; 104 | strcpy(current_line_copy, current_line); 105 | line_ptr[nr_of_lines++] = current_line_copy; 106 | } 107 | } 108 | 109 | afree(current_line); 110 | 111 | return nr_of_lines; 112 | } 113 | 114 | void write_lines(char *line_ptr[], const size_t nr_of_lines_to_print, 115 | const size_t total_nr_of_lines) { 116 | size_t start_pos = 0; 117 | 118 | if (total_nr_of_lines >= nr_of_lines_to_print) { 119 | start_pos = total_nr_of_lines - nr_of_lines_to_print; 120 | } 121 | 122 | for (size_t i = start_pos; i < total_nr_of_lines; ++i) { 123 | puts(line_ptr[i]); 124 | afree(line_ptr[i]); 125 | } 126 | } 127 | 128 | char *alloc(size_t size) { 129 | // Cast pointer difference to size_t to match unsigned size parameter 130 | // This prevents sign comparison warnings 131 | if ((size_t)(alloc_buf + ALLOC_SIZE - alloc_p) >= size) { 132 | alloc_p += size; 133 | return alloc_p - size; 134 | } 135 | 136 | return NULL; 137 | } 138 | 139 | void afree(char *ptr) { 140 | if (ptr >= alloc_buf && ptr < alloc_buf + ALLOC_SIZE) { 141 | alloc_p = ptr; 142 | } 143 | } 144 | 145 | // NOTE: run: ./tail -5 < file_in.txt 146 | -------------------------------------------------------------------------------- /chapter_8/exercise_8_06/calloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MIN_NR_OF_UNITS 1024 7 | 8 | typedef long Aling; 9 | 10 | union header { 11 | struct { 12 | union header *free_block_p; 13 | size_t size; 14 | } s; 15 | Aling x; // Force alignment of blocks. 16 | }; 17 | 18 | typedef union header Header; 19 | 20 | static Header base; 21 | static Header *free_p = NULL; 22 | 23 | void c_free(void *a_p); 24 | void *c_malloc(size_t nr_of_bytes); 25 | void *c_calloc(size_t nr_of_blocks, size_t block_size); 26 | Header *c_morecore(size_t nr_of_units); 27 | 28 | int main(void) { 29 | char *test_malloc_str_p; 30 | if ((test_malloc_str_p = c_malloc(27 * sizeof(char))) == NULL) { 31 | printf("Error: malloc faild to allocate the requrested memory.\n"); 32 | return EXIT_FAILURE; 33 | } 34 | 35 | strcpy(test_malloc_str_p, "Content from malloc here."); 36 | printf("%s\n", test_malloc_str_p); 37 | c_free(test_malloc_str_p); 38 | 39 | char *test_calloc_str_p; 40 | if ((test_calloc_str_p = c_calloc(27, sizeof(char))) == NULL) { 41 | printf("Error: calloc faild to allocate the requrested memory.\n"); 42 | return EXIT_FAILURE; 43 | } 44 | 45 | strcpy(test_calloc_str_p, "Content from calloc here."); 46 | printf("%s\n", test_calloc_str_p); 47 | c_free(test_calloc_str_p); 48 | 49 | return EXIT_SUCCESS; 50 | } 51 | 52 | void c_free(void *a_p) { 53 | Header *p; 54 | Header *b_p = (Header *)a_p - 1; 55 | 56 | for (p = free_p; !(b_p > p && b_p < p->s.free_block_p); 57 | p = p->s.free_block_p) { 58 | if (p >= p->s.free_block_p && (b_p > p || b_p < p->s.free_block_p)) { 59 | break; 60 | } 61 | } 62 | 63 | if (b_p + b_p->s.size == p->s.free_block_p) { 64 | b_p->s.size += p->s.free_block_p->s.size; 65 | b_p->s.free_block_p = p->s.free_block_p->s.free_block_p; 66 | } else { 67 | b_p->s.free_block_p = p->s.free_block_p; 68 | } 69 | 70 | if (p + p->s.size == b_p) { 71 | p->s.size += b_p->s.size; 72 | p->s.free_block_p = b_p->s.free_block_p; 73 | } else { 74 | p->s.free_block_p = b_p; 75 | } 76 | 77 | free_p = p; 78 | } 79 | 80 | void *c_malloc(size_t nr_of_bytes) { 81 | Header *p; 82 | Header *prev_p; 83 | size_t nr_of_units = (nr_of_bytes + sizeof(Header) - 1) / sizeof(Header); 84 | 85 | if ((prev_p = free_p) == NULL) { 86 | base.s.free_block_p = free_p = prev_p = &base; 87 | base.s.size = 0; 88 | } 89 | 90 | for (p = prev_p->s.free_block_p;; prev_p = p, p = p->s.free_block_p) { 91 | if (p->s.size >= nr_of_units) { 92 | if (p->s.size == nr_of_units) { 93 | prev_p->s.free_block_p = p->s.free_block_p; 94 | } else { 95 | p->s.size -= nr_of_units; 96 | p += p->s.size; 97 | p->s.size = nr_of_units; 98 | } 99 | 100 | free_p = prev_p; 101 | return (void *)(p + 1); 102 | } 103 | 104 | if (p == free_p) { 105 | if ((p = c_morecore(nr_of_units)) == NULL) { 106 | return NULL; 107 | } 108 | } 109 | } 110 | 111 | return NULL; 112 | } 113 | 114 | void *c_calloc(size_t nr_of_blocks, size_t block_size) { 115 | void *p = NULL; 116 | if ((p = c_malloc(nr_of_blocks * block_size)) == NULL) { 117 | return NULL; 118 | } 119 | memset(p, 0, nr_of_blocks * block_size); 120 | 121 | return p; 122 | } 123 | 124 | Header *c_morecore(size_t nr_of_units) { 125 | char *c_p; 126 | Header *u_p; 127 | 128 | if (nr_of_units < MIN_NR_OF_UNITS) { 129 | nr_of_units = MIN_NR_OF_UNITS; 130 | } 131 | 132 | // NOTE: sbrk is deprecated on macOS but required for this exercise 133 | // which demonstrates low-level memory allocation 134 | #pragma GCC diagnostic push 135 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 136 | c_p = sbrk(nr_of_units * sizeof(Header)); 137 | #pragma GCC diagnostic pop 138 | if (c_p == (char *)-1) { 139 | return NULL; 140 | } 141 | 142 | u_p = (Header *)c_p; 143 | u_p->s.size = nr_of_units; 144 | c_free((void *)(u_p + 1)); 145 | 146 | return free_p; 147 | } 148 | -------------------------------------------------------------------------------- /chapter_4/exercise_4_04/stack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define MAXOP 100 6 | #define MAXVAL 100 7 | #define BUFFSIZE 100 8 | #define NUMBER '0' 9 | 10 | int getop(char[]); 11 | 12 | void push(double f); 13 | double pop(void); 14 | void view_head(void); 15 | void duplicate(void); 16 | void swap(void); 17 | void clear(void); 18 | 19 | int main(void) { 20 | int type; 21 | double op2; 22 | char s[MAXOP]; 23 | 24 | while ((type = getop(s)) != EOF) { 25 | switch (type) { 26 | case NUMBER: 27 | push(atof(s)); 28 | break; 29 | 30 | case '+': 31 | push(pop() + pop()); 32 | break; 33 | 34 | case '-': 35 | op2 = pop(); 36 | push(pop() - op2); 37 | break; 38 | 39 | case '*': 40 | push(pop() * pop()); 41 | break; 42 | 43 | case '/': 44 | op2 = pop(); 45 | 46 | if (op2 != 0.0) { 47 | push(pop() / op2); 48 | } else { 49 | printf("Error: zero divisor.\n"); 50 | } 51 | 52 | break; 53 | 54 | case '%': 55 | op2 = pop(); 56 | 57 | if (op2 != 0.0) { 58 | push((int)pop() % (int)op2); 59 | } else { 60 | printf("Error: zero divisor.\n"); 61 | } 62 | break; 63 | 64 | case 'h': 65 | view_head(); 66 | break; 67 | 68 | case 'd': 69 | duplicate(); 70 | break; 71 | 72 | case 's': 73 | swap(); 74 | break; 75 | 76 | case 'c': 77 | clear(); 78 | break; 79 | 80 | case '\n': 81 | printf("result: %.8g\n", pop()); 82 | break; 83 | 84 | default: 85 | printf("Error: unknown command %s.\n", s); 86 | break; 87 | } 88 | } 89 | 90 | return 0; 91 | } 92 | 93 | int sp = 0; 94 | double stack[MAXVAL]; 95 | 96 | void push(double f) { 97 | if (sp < MAXVAL) { 98 | stack[sp++] = f; 99 | } else { 100 | printf("Error: stack full.\n"); 101 | } 102 | } 103 | 104 | double pop(void) { 105 | if (sp > 0) { 106 | return stack[--sp]; 107 | } else { 108 | printf("Error: stack empty.\n"); 109 | } 110 | 111 | return 0.0; 112 | } 113 | 114 | void view_head(void) { 115 | if (sp) { 116 | printf("stack_head: %g\n", stack[sp - 1]); 117 | } else { 118 | printf("Error: stack empty.\n"); 119 | } 120 | } 121 | 122 | void duplicate(void) { 123 | double temp = pop(); 124 | push(temp); 125 | push(temp); 126 | } 127 | 128 | void swap(void) { 129 | double temp1 = pop(); 130 | double temp2 = pop(); 131 | 132 | push(temp1); 133 | push(temp2); 134 | } 135 | 136 | void clear(void) { 137 | while (sp > 0) { 138 | stack[--sp] = 0.0; 139 | } 140 | } 141 | 142 | int bufp = 0; 143 | char buf[BUFFSIZE]; 144 | 145 | int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar(); } 146 | 147 | void ungetch(int c) { 148 | if (bufp >= BUFFSIZE) { 149 | printf("ungetch: too many characters\n"); 150 | } else { 151 | buf[bufp++] = c; 152 | } 153 | } 154 | 155 | int getop(char s[]) { 156 | int i = 0; 157 | int c; 158 | 159 | while ((s[0] = c = getch()) == ' ' || c == '\t') 160 | ; 161 | 162 | s[1] = '\0'; 163 | 164 | if (!isdigit(c) && c != '.' && c != '-') { 165 | return c; 166 | } 167 | 168 | if (c == '-') { 169 | int next = getch(); 170 | if (next == '\n' || next == ' ' || next == '\t') { 171 | ungetch(next); 172 | return c; // return '-' as operator 173 | } else if (!isdigit(next) && next != '.') { 174 | return next; // not a number 175 | } else // number like "-5", "-.6" etc, next is digit or '.' 176 | { 177 | s[++i] = c = next; 178 | } 179 | } 180 | 181 | if (isdigit(c)) { 182 | while (isdigit(s[++i] = c = getch())) 183 | ; 184 | } 185 | 186 | if (c == '.') { 187 | while (isdigit(s[++i] = c = getch())) 188 | ; 189 | } 190 | 191 | if (c != EOF) { 192 | ungetch(c); 193 | } 194 | 195 | return NUMBER; 196 | } 197 | -------------------------------------------------------------------------------- /chapter_3/exercise_3_02/escape.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define MAXLEN 1000 4 | 5 | int get_line(char line[], unsigned int limit); 6 | void escape(char dest[], char src[]); 7 | void unescape(char dest[], char src[]); 8 | 9 | int main(void) { 10 | char src[MAXLEN]; 11 | char dest[MAXLEN]; 12 | 13 | get_line(src, MAXLEN); 14 | printf("%s", src); 15 | 16 | escape(dest, src); 17 | printf("%s\n", dest); 18 | 19 | unescape(dest, src); 20 | printf("%s", dest); 21 | 22 | return 0; 23 | } 24 | 25 | int get_line(char line[], unsigned int limit) { 26 | // Using unsigned int for i to match the unsigned limit parameter 27 | // This prevents sign comparison warnings 28 | unsigned int i; 29 | int c; 30 | for (i = 0; i < limit - 1 && (c = getchar()) != EOF && c != '\n'; ++i) { 31 | line[i] = c; 32 | } 33 | 34 | if (c == '\n') { 35 | line[i++] = c; 36 | } 37 | 38 | line[i] = '\0'; 39 | 40 | return i; 41 | } 42 | 43 | void escape(char dest[], char src[]) { 44 | int i; 45 | int j; 46 | for (i = j = 0; src[i] != '\0'; ++i, ++j) { 47 | switch (src[i]) { 48 | case '\a': 49 | dest[j++] = '\\'; 50 | dest[j] = 'a'; 51 | break; 52 | 53 | case '\b': 54 | dest[j++] = '\\'; 55 | dest[j] = 'b'; 56 | break; 57 | 58 | case '\f': 59 | dest[j++] = '\\'; 60 | dest[j] = 'f'; 61 | break; 62 | 63 | case '\n': 64 | dest[j++] = '\\'; 65 | dest[j] = 'n'; 66 | break; 67 | 68 | case '\r': 69 | dest[j++] = '\\'; 70 | dest[j] = 'r'; 71 | break; 72 | 73 | case '\t': 74 | dest[j++] = '\\'; 75 | dest[j] = 't'; 76 | break; 77 | 78 | case '\v': 79 | dest[j++] = '\\'; 80 | dest[j] = 'v'; 81 | break; 82 | 83 | case '\\': 84 | dest[j++] = '\\'; 85 | dest[j] = '\\'; 86 | break; 87 | 88 | case '\?': 89 | dest[j++] = '\\'; 90 | dest[j] = '?'; 91 | break; 92 | 93 | case '\'': 94 | dest[j++] = '\\'; 95 | dest[j] = '\''; 96 | break; 97 | 98 | case '\"': 99 | dest[j++] = '\\'; 100 | dest[j] = '"'; 101 | break; 102 | 103 | default: 104 | dest[j] = src[i]; 105 | break; 106 | } 107 | } 108 | 109 | if (src[i] == '\0') { 110 | dest[j] = src[i]; 111 | } 112 | } 113 | 114 | void unescape(char dest[], char src[]) { 115 | int i; 116 | int j; 117 | for (i = j = 0; src[i] != '\0'; ++i, ++j) { 118 | switch (src[i]) { 119 | case '\\': 120 | switch (src[++i]) { 121 | case 'a': 122 | dest[j] = '\a'; 123 | break; 124 | 125 | case 'b': 126 | dest[j] = '\b'; 127 | break; 128 | 129 | case 'f': 130 | dest[j] = '\f'; 131 | break; 132 | 133 | case 'n': 134 | dest[j] = '\n'; 135 | break; 136 | 137 | case 'r': 138 | dest[j] = '\r'; 139 | break; 140 | 141 | case 't': 142 | dest[j] = '\t'; 143 | break; 144 | 145 | case 'v': 146 | dest[j] = '\v'; 147 | break; 148 | 149 | case '\\': 150 | dest[j] = '\\'; 151 | break; 152 | 153 | case '?': 154 | dest[j] = '\?'; 155 | break; 156 | 157 | case '\'': 158 | dest[j] = '\''; 159 | break; 160 | 161 | case '"': 162 | dest[j] = '\"'; 163 | break; 164 | 165 | default: 166 | dest[j++] = '\\'; 167 | dest[j] = src[i]; 168 | break; 169 | } 170 | break; 171 | 172 | default: 173 | dest[j] = src[i]; 174 | break; 175 | } 176 | } 177 | 178 | if (src[i] == '\0') { 179 | dest[j] = src[i]; 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /chapter_5/exercise_5_18/dcl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAX_TOKEN_LEN 100 7 | #define MAX_OUT_LEN 1000 8 | 9 | // Using (void) instead of () explicitly declares these functions take no 10 | // parameters This is the preferred modern C style and avoids compiler warnings 11 | void skip_blanks(void); 12 | void skip_comments(void); 13 | 14 | void get_name(char *dest, const size_t max_len); 15 | int get_next_token(void); 16 | 17 | void dcl(void); 18 | void dir_dcl(void); 19 | 20 | enum token_type { 21 | NAME, 22 | PARENS, 23 | BRACKETS, 24 | PAREN_OPEN = '(', 25 | PAREN_CLOSE = ')', 26 | BRACKET_OPEN = '[', 27 | BRACKET_CLOSE = ']' 28 | }; 29 | 30 | int next_token; 31 | 32 | char token[MAX_TOKEN_LEN]; 33 | char name[MAX_TOKEN_LEN]; 34 | char data_type[MAX_TOKEN_LEN]; 35 | char out[MAX_OUT_LEN]; 36 | 37 | int main(void) { 38 | while (get_next_token() != EOF) { 39 | if (next_token == '\n') { 40 | continue; 41 | } 42 | 43 | strcpy(data_type, token); 44 | out[0] = '\0'; 45 | 46 | dcl(); 47 | 48 | if (next_token != '\n') { 49 | if (next_token == NAME) { 50 | printf("Syntax Error: '%s' unexpected.\n", token); 51 | } else { 52 | printf("Syntax Error: '%c' unexpected.\n", next_token); 53 | } 54 | 55 | do { 56 | get_next_token(); 57 | } while (next_token != '\n' && next_token != EOF); 58 | } else { 59 | printf("%s:%s %s\n", name, out, data_type); 60 | } 61 | } 62 | 63 | return EXIT_SUCCESS; 64 | } 65 | 66 | void skip_blanks(void) { 67 | int c; 68 | while (isblank(c = getc(stdin))) 69 | ; 70 | ungetc(c, stdin); 71 | } 72 | 73 | void skip_comments(void) { 74 | int c = getc(stdin); 75 | if (c == '/') { 76 | c = getc(stdin); 77 | if (c == '/') { 78 | while ((c = getc(stdin)) != '\n' && c != EOF) 79 | ; 80 | } else if (c == '*') { 81 | while ((c = getc(stdin)) != '*' && c != EOF) 82 | ; 83 | c = getc(stdin); 84 | if (c == '/') { 85 | ungetc('\n', stdin); 86 | return; 87 | } 88 | } 89 | } 90 | ungetc(c, stdin); 91 | } 92 | 93 | void get_name(char *dest, const size_t max_len) { 94 | int c; 95 | size_t i = 0; 96 | while ((isalnum(c = getc(stdin)) || c == '_') && i < max_len) { 97 | dest[i++] = c; 98 | } 99 | dest[i] = '\0'; 100 | ungetc(c, stdin); 101 | } 102 | 103 | int get_next_token(void) { 104 | skip_blanks(); 105 | skip_comments(); 106 | skip_blanks(); 107 | 108 | int c = getc(stdin); 109 | if (c == '(') { 110 | skip_blanks(); 111 | 112 | c = getc(stdin); 113 | if (c == ')') { 114 | strcpy(token, "()"); 115 | return next_token = PARENS; 116 | } 117 | ungetc(c, stdin); 118 | 119 | return next_token = PAREN_OPEN; 120 | } else if (c == '[') { 121 | skip_blanks(); 122 | get_name(token, MAX_TOKEN_LEN); 123 | skip_blanks(); 124 | 125 | c = getc(stdin); 126 | if (c == ']') { 127 | return next_token = BRACKETS; 128 | } 129 | ungetc(c, stdin); 130 | 131 | return next_token = BRACKET_OPEN; 132 | } else if (isalpha(c)) { 133 | ungetc(c, stdin); 134 | get_name(token, MAX_TOKEN_LEN); 135 | return next_token = NAME; 136 | } 137 | 138 | return next_token = c; 139 | } 140 | 141 | void dcl(void) { 142 | int nr_of_stars = 0; 143 | while (get_next_token() == '*') { 144 | ++nr_of_stars; 145 | } 146 | 147 | dir_dcl(); 148 | 149 | while (nr_of_stars--) { 150 | strcat(out, " pointer to"); 151 | } 152 | } 153 | 154 | void dir_dcl(void) { 155 | if (next_token == PAREN_OPEN) { 156 | dcl(); 157 | 158 | if (next_token != PAREN_CLOSE) { 159 | puts("Syntax Error: missing ')'."); 160 | } 161 | } else if (next_token == NAME) { 162 | strcpy(name, token); 163 | } else { 164 | puts("Syntax Error: expected name or (dcl)."); 165 | } 166 | 167 | while ((next_token = get_next_token()) == PARENS || 168 | next_token == BRACKETS) { 169 | if (next_token == PARENS) { 170 | strcat(out, " function returning"); 171 | } else if (next_token == BRACKETS) { 172 | strcat(out, " array["); 173 | strcat(out, token); 174 | strcat(out, "] of"); 175 | } 176 | } 177 | } 178 | 179 | // NOTE: run: ./dcl < test.txt 180 | -------------------------------------------------------------------------------- /chapter_4/exercise_4_05/math.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAXOP 100 7 | #define MAXVAL 100 8 | #define BUFFSIZE 100 9 | #define NUMBER '0' 10 | 11 | int getop(char[]); 12 | 13 | void push(double f); 14 | double pop(void); 15 | void view_head(void); 16 | void duplicate(void); 17 | void swap(void); 18 | void clear(void); 19 | 20 | int main(void) { 21 | int type; 22 | double op2; 23 | char s[MAXOP]; 24 | 25 | while ((type = getop(s)) != EOF) { 26 | switch (type) { 27 | case NUMBER: 28 | push(atof(s)); 29 | break; 30 | 31 | case '+': 32 | push(pop() + pop()); 33 | break; 34 | 35 | case '-': 36 | op2 = pop(); 37 | push(pop() - op2); 38 | break; 39 | 40 | case '*': 41 | push(pop() * pop()); 42 | break; 43 | 44 | case '/': 45 | op2 = pop(); 46 | 47 | if (op2 != 0.0) { 48 | push(pop() / op2); 49 | } else { 50 | printf("Error: zero divisor.\n"); 51 | } 52 | 53 | break; 54 | 55 | case '%': 56 | op2 = pop(); 57 | 58 | if (op2 != 0.0) { 59 | push((int)pop() % (int)op2); 60 | } else { 61 | printf("Error: zero divisor.\n"); 62 | } 63 | break; 64 | 65 | case '^': 66 | op2 = pop(); 67 | push(pow(pop(), op2)); 68 | break; 69 | 70 | case '~': 71 | push(sin(pop())); 72 | break; 73 | 74 | case 'e': 75 | push(exp(pop())); 76 | break; 77 | 78 | case 'h': 79 | view_head(); 80 | break; 81 | 82 | case 'd': 83 | duplicate(); 84 | break; 85 | 86 | case 's': 87 | swap(); 88 | break; 89 | 90 | case 'c': 91 | clear(); 92 | break; 93 | 94 | case '\n': 95 | printf("result: %.8g\n", pop()); 96 | break; 97 | 98 | default: 99 | printf("Error: unknown command %s.\n", s); 100 | break; 101 | } 102 | } 103 | 104 | return 0; 105 | } 106 | 107 | int sp = 0; 108 | double stack[MAXVAL]; 109 | 110 | void push(double f) { 111 | if (sp < MAXVAL) { 112 | stack[sp++] = f; 113 | } else { 114 | printf("Error: stack full.\n"); 115 | } 116 | } 117 | 118 | double pop(void) { 119 | if (sp > 0) { 120 | return stack[--sp]; 121 | } else { 122 | printf("Error: stack empty.\n"); 123 | } 124 | 125 | return 0.0; 126 | } 127 | 128 | void view_head(void) { 129 | if (sp) { 130 | printf("stack_head: %g\n", stack[sp - 1]); 131 | } else { 132 | printf("Error: stack empty.\n"); 133 | } 134 | } 135 | 136 | void duplicate(void) { 137 | double temp = pop(); 138 | push(temp); 139 | push(temp); 140 | } 141 | 142 | void swap(void) { 143 | double temp1 = pop(); 144 | double temp2 = pop(); 145 | 146 | push(temp1); 147 | push(temp2); 148 | } 149 | 150 | void clear(void) { 151 | do { 152 | stack[sp] = 0.0; 153 | } while (sp--); 154 | } 155 | 156 | int bufp = 0; 157 | char buf[BUFFSIZE]; 158 | 159 | int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar(); } 160 | 161 | void ungetch(int c) { 162 | if (bufp >= BUFFSIZE) { 163 | printf("ungetch: too many characters\n"); 164 | } else { 165 | buf[bufp++] = c; 166 | } 167 | } 168 | 169 | int getop(char s[]) { 170 | int i = 0; 171 | int c; 172 | 173 | while ((s[0] = c = getch()) == ' ' || c == '\t') 174 | ; 175 | 176 | s[1] = '\0'; 177 | 178 | if (!isdigit(c) && c != '.' && c != '-') { 179 | return c; 180 | } 181 | 182 | if (c == '-') { 183 | int next = getch(); 184 | if (next == '\n' || next == ' ' || next == '\t') { 185 | ungetch(next); 186 | return c; // return '-' as operator 187 | } else if (!isdigit(next) && next != '.') { 188 | return next; // not a number 189 | } else // number like "-5", "-.6" etc, next is digit or '.' 190 | { 191 | s[++i] = c = next; 192 | } 193 | } else { 194 | c = getch(); 195 | } 196 | 197 | if (isdigit(c)) { 198 | while (isdigit(s[++i] = c = getch())) 199 | ; 200 | } 201 | 202 | if (c == '.') { 203 | while (isdigit(s[++i] = c = getch())) 204 | ; 205 | } 206 | 207 | if (c != EOF) { 208 | ungetch(c); 209 | } 210 | 211 | return NUMBER; 212 | } 213 | -------------------------------------------------------------------------------- /chapter_8/exercise_8_07/malloc_free.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define MIN_NR_OF_UNITS 1024 8 | 9 | typedef long Aling; 10 | 11 | union header { 12 | struct { 13 | union header *free_block_p; 14 | size_t size; 15 | } s; 16 | Aling x; // Force alignment of blocks. 17 | }; 18 | 19 | typedef union header Header; 20 | 21 | static Header base; 22 | static Header *free_p = NULL; 23 | 24 | void c_free(void *a_p); 25 | void *c_malloc(size_t nr_of_bytes); 26 | void *c_calloc(size_t nr_of_blocks, size_t block_size); 27 | Header *c_morecore(size_t nr_of_units); 28 | 29 | int main(void) { 30 | char *test_malloc_str_p; 31 | if ((test_malloc_str_p = c_malloc(27 * sizeof(char))) == NULL) { 32 | printf("Error: malloc faild to allocate the requrested memory.\n"); 33 | return EXIT_FAILURE; 34 | } 35 | 36 | strcpy(test_malloc_str_p, "Content from malloc here."); 37 | printf("%s\n", test_malloc_str_p); 38 | c_free(test_malloc_str_p); 39 | 40 | char *test_calloc_str_p; 41 | if ((test_calloc_str_p = c_calloc(27, sizeof(char))) == NULL) { 42 | printf("Error: calloc faild to allocate the requrested memory.\n"); 43 | return EXIT_FAILURE; 44 | } 45 | 46 | strcpy(test_calloc_str_p, "Content from calloc here."); 47 | printf("%s\n", test_calloc_str_p); 48 | c_free(test_calloc_str_p); 49 | 50 | return EXIT_SUCCESS; 51 | } 52 | 53 | void c_free(void *a_p) { 54 | Header *p; 55 | Header *b_p = (Header *)a_p - 1; 56 | 57 | if (b_p->s.size == 0 || b_p->s.size == UINT_MAX - MIN_NR_OF_UNITS) { 58 | fprintf(stderr, "Error(free): invalid block size %zu\n", b_p->s.size); 59 | return; 60 | } 61 | 62 | for (p = free_p; !(b_p > p && b_p < p->s.free_block_p); 63 | p = p->s.free_block_p) { 64 | if (p >= p->s.free_block_p && (b_p > p || b_p < p->s.free_block_p)) { 65 | break; 66 | } 67 | } 68 | 69 | if (b_p + b_p->s.size == p->s.free_block_p) { 70 | b_p->s.size += p->s.free_block_p->s.size; 71 | b_p->s.free_block_p = p->s.free_block_p->s.free_block_p; 72 | } else { 73 | b_p->s.free_block_p = p->s.free_block_p; 74 | } 75 | 76 | if (p + p->s.size == b_p) { 77 | p->s.size += b_p->s.size; 78 | p->s.free_block_p = b_p->s.free_block_p; 79 | } else { 80 | p->s.free_block_p = b_p; 81 | } 82 | 83 | free_p = p; 84 | } 85 | 86 | void *c_malloc(size_t nr_of_bytes) { 87 | Header *p; 88 | Header *prev_p; 89 | 90 | if (nr_of_bytes == 0 || nr_of_bytes >= UINT_MAX - MIN_NR_OF_UNITS) { 91 | fprintf(stderr, "Error(malloc): invalid size %zu\n", nr_of_bytes); 92 | return NULL; 93 | } 94 | 95 | size_t nr_of_units = (nr_of_bytes + sizeof(Header) - 1) / sizeof(Header); 96 | 97 | if ((prev_p = free_p) == NULL) { 98 | base.s.free_block_p = free_p = prev_p = &base; 99 | base.s.size = 0; 100 | } 101 | 102 | for (p = prev_p->s.free_block_p;; prev_p = p, p = p->s.free_block_p) { 103 | if (p->s.size >= nr_of_units) { 104 | if (p->s.size == nr_of_units) { 105 | prev_p->s.free_block_p = p->s.free_block_p; 106 | } else { 107 | p->s.size -= nr_of_units; 108 | p += p->s.size; 109 | p->s.size = nr_of_units; 110 | } 111 | 112 | free_p = prev_p; 113 | return (void *)(p + 1); 114 | } 115 | 116 | if (p == free_p) { 117 | if ((p = c_morecore(nr_of_units)) == NULL) { 118 | return NULL; 119 | } 120 | } 121 | } 122 | 123 | return NULL; 124 | } 125 | 126 | void *c_calloc(size_t nr_of_blocks, size_t block_size) { 127 | void *p = NULL; 128 | if ((p = c_malloc(nr_of_blocks * block_size)) == NULL) { 129 | return NULL; 130 | } 131 | memset(p, 0, nr_of_blocks * block_size); 132 | 133 | return p; 134 | } 135 | 136 | Header *c_morecore(size_t nr_of_units) { 137 | char *c_p; 138 | Header *u_p; 139 | 140 | if (nr_of_units < MIN_NR_OF_UNITS) { 141 | nr_of_units = MIN_NR_OF_UNITS; 142 | } 143 | 144 | // NOTE: sbrk is deprecated on macOS but required for this exercise 145 | // which demonstrates low-level memory allocation 146 | #pragma GCC diagnostic push 147 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 148 | c_p = sbrk(nr_of_units * sizeof(Header)); 149 | #pragma GCC diagnostic pop 150 | if (c_p == (char *)-1) { 151 | return NULL; 152 | } 153 | 154 | u_p = (Header *)c_p; 155 | u_p->s.size = nr_of_units; 156 | c_free((void *)(u_p + 1)); 157 | 158 | return free_p; 159 | } 160 | -------------------------------------------------------------------------------- /chapter_6/exercise_6_04/words_frequency.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAX_WORD_LEN 100 7 | #define MAX_NR_OF_NODES 1000 8 | 9 | struct tree_node { 10 | char *word; 11 | int count; 12 | struct tree_node *left; 13 | struct tree_node *right; 14 | }; 15 | 16 | struct tree_node *add_to_tree(struct tree_node *node_p, char *word); 17 | void print_tree(struct tree_node *node_p); 18 | void copy_tree_to_array(struct tree_node *arr[], struct tree_node *tree_node_p); 19 | 20 | // There is a strdup available with POSIX, but it's not part of ISO C. 21 | char *str_dup(char *src); 22 | 23 | void skip_blanks(void); 24 | 25 | int get_word(char *word, int max_word_len); 26 | int tree_node_cmp(const struct tree_node *node_p_1, 27 | const struct tree_node *node_p_2); 28 | void swap(void *v[], size_t i, size_t j); 29 | void quick_sort(void *v[], size_t start, size_t end, 30 | int (*comp)(void *, void *)); 31 | 32 | size_t nr_of_nodes = 0; 33 | 34 | int main(void) { 35 | struct tree_node *tree_root = NULL; 36 | char word[MAX_WORD_LEN]; 37 | 38 | while (get_word(word, MAX_WORD_LEN) != EOF) { 39 | if (isalpha(word[0])) { 40 | tree_root = add_to_tree(tree_root, word); 41 | } 42 | } 43 | 44 | struct tree_node *tree_node_list[MAX_NR_OF_NODES] = {NULL}; 45 | copy_tree_to_array(tree_node_list, tree_root); 46 | 47 | quick_sort((void **)tree_node_list, 0, nr_of_nodes - 1, 48 | (int (*)(void *, void *))tree_node_cmp); 49 | 50 | for (size_t i = 0; i < nr_of_nodes; ++i) { 51 | printf("%4d %s\n", tree_node_list[i]->count, tree_node_list[i]->word); 52 | } 53 | 54 | return EXIT_SUCCESS; 55 | } 56 | 57 | char *str_dup(char *src) { 58 | char *dest = (char *)malloc(strlen(src) + 1); 59 | if (dest != NULL) { 60 | strcpy(dest, src); 61 | } 62 | return dest; 63 | } 64 | 65 | void skip_blanks(void) { 66 | int c; 67 | while (isblank(c = getc(stdin))) 68 | ; 69 | ungetc(c, stdin); 70 | } 71 | 72 | int get_word(char *word, int max_word_len) { 73 | skip_blanks(); 74 | 75 | int c = getc(stdin); 76 | int i = 0; 77 | 78 | if (c != EOF) { 79 | word[i++] = c; 80 | } 81 | 82 | if (!isalpha(c) && c != '_') { 83 | word[i] = '\0'; 84 | return c; 85 | } 86 | 87 | while ((isalnum(c = getc(stdin)) || c == '_') && i < max_word_len) { 88 | word[i++] = c; 89 | } 90 | ungetc(c, stdin); 91 | word[i] = '\0'; 92 | 93 | return word[0]; 94 | } 95 | 96 | int tree_node_cmp(const struct tree_node *node_p_1, 97 | const struct tree_node *node_p_2) { 98 | if (node_p_1->count > node_p_2->count) { 99 | return -1; 100 | } else if (node_p_1->count < node_p_2->count) { 101 | return 1; 102 | } 103 | 104 | return 0; 105 | } 106 | 107 | void swap(void *v[], size_t i, size_t j) { 108 | void *temp; 109 | temp = v[i]; 110 | v[i] = v[j]; 111 | v[j] = temp; 112 | } 113 | 114 | void quick_sort(void *v[], size_t start, size_t end, 115 | int (*comp)(void *, void *)) { 116 | if ((long)start >= (long)end) { 117 | return; 118 | } 119 | 120 | swap(v, start, (start + end) / 2); 121 | 122 | size_t last = start; 123 | for (size_t i = start + 1; i <= end; ++i) { 124 | if ((*comp)(v[i], v[start]) < 0) { 125 | swap(v, ++last, i); 126 | } 127 | } 128 | 129 | swap(v, start, last); 130 | quick_sort(v, start, last - 1, comp); 131 | quick_sort(v, last + 1, end, comp); 132 | } 133 | 134 | struct tree_node *add_to_tree(struct tree_node *node_p, char *word) { 135 | int cond; 136 | 137 | if (node_p == NULL) { 138 | node_p = (struct tree_node *)malloc(sizeof(struct tree_node)); 139 | node_p->word = str_dup(word); 140 | node_p->count = 1; 141 | node_p->left = node_p->right = NULL; 142 | } else if ((cond = strcmp(word, node_p->word)) == 0) { 143 | node_p->count++; 144 | } else if (cond < 0) { 145 | node_p->left = add_to_tree(node_p->left, word); 146 | } else if (cond > 0) { 147 | node_p->right = add_to_tree(node_p->right, word); 148 | } 149 | 150 | return node_p; 151 | } 152 | 153 | void print_tree(struct tree_node *node_p) { 154 | if (node_p != NULL) { 155 | print_tree(node_p->left); 156 | printf("%4d %s\n", node_p->count, node_p->word); 157 | print_tree(node_p->right); 158 | } 159 | } 160 | 161 | void copy_tree_to_array(struct tree_node *arr[], 162 | struct tree_node *tree_node_p) { 163 | if (tree_node_p != NULL) { 164 | copy_tree_to_array(arr, tree_node_p->left); 165 | if (nr_of_nodes < MAX_NR_OF_NODES) { 166 | arr[nr_of_nodes++] = tree_node_p; 167 | } 168 | copy_tree_to_array(arr, tree_node_p->right); 169 | } 170 | } 171 | 172 | // NOTE: run: ./words_frequency < words_frequency.c 173 | -------------------------------------------------------------------------------- /chapter_8/exercise_8_05/fsize.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define MAX_PATH_LEN 1024 12 | 13 | void fsize(char *name); 14 | void dir_walk(char *dir_name, void (*func)(char *)); 15 | 16 | void print_file_flags(mode_t st_mode); 17 | void print_file_user(uid_t st_uid); 18 | void print_file_group(gid_t st_gid); 19 | void print_file_size(size_t size); 20 | void print_file_time(time_t time); 21 | 22 | int main(int argc, char *argv[]) { 23 | if (argc == 1) { 24 | fsize("."); 25 | } else { 26 | while (--argc > 0) { 27 | fsize(*++argv); 28 | } 29 | } 30 | 31 | return 0; 32 | } 33 | 34 | void fsize(char *name) { 35 | struct stat buffer; 36 | 37 | if (stat(name, &buffer) == -1) { 38 | fprintf(stderr, "fsize: cannot access %s\n", name); 39 | return; 40 | } 41 | 42 | if ((buffer.st_mode & S_IFMT) == S_IFDIR) { 43 | dir_walk(name, fsize); 44 | } 45 | 46 | /** 47 | * off_t st_size File size, in bytes 48 | * dev_t st_dev ID of device containing file 49 | * ino_t st_ino File serial number 50 | * mode_t st_mode Mode of file (see below) 51 | * nlink_t st_nlink Number of hard links 52 | * uid_t st_uid User ID of the file 53 | * gid_t st_gid Group ID of the file 54 | * dev_t st_rdev Device ID 55 | * time_t st_atime Time of last access 56 | * time_t st_mtime Last data modification time 57 | * time_t st_ctime Time of last status change 58 | * blkcnt_t st_blocks Blocks allocated for file 59 | * blksize_t st_blksize Optimal blocksize for I/O 60 | */ 61 | 62 | // Printed in a similary fashion to ls -l 63 | print_file_flags(buffer.st_mode); 64 | // Cast to unsigned long and use %lu for portability: nlink_t varies by 65 | // platform (unsigned short on macOS, unsigned long on Linux). The cast 66 | // ensures consistent formatting across platforms. 67 | printf("%lu ", (unsigned long)buffer.st_nlink); 68 | print_file_user(buffer.st_uid); 69 | print_file_group(buffer.st_gid); 70 | print_file_size(buffer.st_size); 71 | print_file_time(buffer.st_atime); 72 | printf("%s\n", name); 73 | } 74 | 75 | void dir_walk(char *dir_name, void (*func)(char *)) { 76 | char name[MAX_PATH_LEN]; 77 | struct dirent *dir_entry; 78 | DIR *dir; 79 | 80 | if ((dir = opendir(dir_name)) == NULL) { 81 | fprintf(stderr, "dir_walk: cannot open %s\n", dir_name); 82 | return; 83 | } 84 | 85 | while ((dir_entry = readdir(dir)) != NULL) { 86 | if (strcmp(dir_entry->d_name, ".") == 0 || 87 | strcmp(dir_entry->d_name, "..") == 0) { 88 | continue; // Skip self and parent. 89 | } 90 | 91 | if (strlen(dir_name) + strlen(dir_entry->d_name) + 2 > MAX_PATH_LEN) { 92 | fprintf(stderr, "dir_walk: path too long\n"); 93 | } else { 94 | sprintf(name, "%s/%s", dir_name, dir_entry->d_name); 95 | (*func)(name); 96 | } 97 | } 98 | 99 | closedir(dir); 100 | } 101 | 102 | void print_file_flags(mode_t st_mode) { 103 | printf("%c", ((st_mode & S_IFMT) == S_IFDIR) ? 'd' : '-'); 104 | printf("%c", (st_mode & S_IRUSR) ? 'r' : '-'); 105 | printf("%c", (st_mode & S_IWUSR) ? 'w' : '-'); 106 | printf("%c", (st_mode & S_IXUSR) ? 'x' : '-'); 107 | printf("%c", (st_mode & S_IRGRP) ? 'r' : '-'); 108 | printf("%c", (st_mode & S_IWGRP) ? 'w' : '-'); 109 | printf("%c", (st_mode & S_IXGRP) ? 'x' : '-'); 110 | printf("%c", (st_mode & S_IROTH) ? 'r' : '-'); 111 | printf("%c", (st_mode & S_IWOTH) ? 'w' : '-'); 112 | printf("%c ", (st_mode & S_IXOTH) ? 'x' : '-'); 113 | } 114 | 115 | void print_file_user(uid_t st_uid) { 116 | struct passwd *password; 117 | password = getpwuid(st_uid); 118 | 119 | if (password == NULL) { 120 | fprintf(stderr, "Error: cannot find user\n"); 121 | return; 122 | } 123 | 124 | printf("%s ", password->pw_name); 125 | } 126 | 127 | void print_file_group(gid_t st_gid) { 128 | struct group *group; 129 | group = getgrgid(st_gid); 130 | 131 | if (group == NULL) { 132 | fprintf(stderr, "Error: cannot find group\n"); 133 | return; 134 | } 135 | 136 | printf("%s ", group->gr_name); 137 | } 138 | 139 | void print_file_size(size_t size) { 140 | static const char *SIZES[] = {"B", "K", "M", "G"}; 141 | size_t div = 0; 142 | size_t rem = 0; 143 | 144 | while (size >= 1024 && div < (sizeof SIZES / sizeof *SIZES)) { 145 | rem = (size % 1024); 146 | div++; 147 | size /= 1024; 148 | } 149 | 150 | printf("%6.1f%s ", (float)size + (float)rem / 1024.0, SIZES[div]); 151 | } 152 | 153 | void print_file_time(time_t time) { 154 | char time_str[32]; 155 | strftime(time_str, sizeof(time_str), "%d %b %H:%M", localtime(&time)); 156 | printf("%s ", time_str); 157 | } 158 | --------------------------------------------------------------------------------