├── .clang-format ├── .gitignore ├── LICENSE ├── README.md ├── demo_v3.gif ├── exercises ├── 01_welcome │ ├── README.md │ └── hello_world.c ├── 02_variables │ ├── README.md │ ├── variables_01.c │ ├── variables_02.c │ └── variables_03.c ├── 03_for_statement │ ├── README.md │ ├── for_statement_01.c │ └── for_statement_02.c ├── 04_symbolic_constants │ ├── README.md │ └── symbolic_constants_01.c ├── 15_variable_names │ ├── README.md │ └── variable_names_01.c ├── 16_data_types │ ├── README.md │ └── data_type_01.c ├── 17_constants │ ├── README.md │ ├── constants_1.c │ ├── constants_2.c │ └── constants_3.c ├── 18_declarations │ ├── README.md │ └── declarations_01.c ├── 19_arithmetic_operators │ ├── README.md │ ├── arithmetic_operators_01.c │ └── arithmetic_operators_02.c ├── 20_relational_logical_operators │ ├── README.md │ ├── relational_logical_operators_01.c │ └── relational_logical_operators_02.c ├── 21_type_conversions │ ├── README.md │ ├── type_conversions_01.c │ └── type_conversions_02.c ├── 22_inc_dec_operators │ ├── README.md │ └── inc_dec_operators_01.c ├── 23_bitwise_operators │ ├── README.md │ ├── bitwise_operators_01.c │ ├── bitwise_operators_02.c │ ├── bitwise_operators_03.c │ ├── bitwise_operators_04.c │ └── bitwise_operators_05.c ├── 24_assignment_operators │ ├── README.md │ └── assignment_operators_01.c ├── 25_conditional_expressions │ ├── README.md │ └── conditional_expressions_01.c ├── 26_operator_precedence │ ├── README.md │ └── operator_precedence_01.c ├── 27_statements_and_blocks │ ├── README.md │ └── statements_and_blocks_01.c ├── 35_functions_basics │ ├── README.md │ └── functions_basics_01.c ├── 36_non_integer_functions │ ├── README.md │ └── non_integer_functions_01.c └── 37_external_variables │ ├── README.md │ └── external_variables.c ├── include ├── display.h ├── execution_state.h ├── exercise.h ├── files.h ├── runna.h └── utils.h ├── makefile ├── src ├── display.c ├── exercise.c ├── files.c ├── main.c ├── runna.c └── utils.c └── start_debian.sh /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignArrayOfStructures: None 7 | AlignConsecutiveMacros: None 8 | AlignConsecutiveAssignments: None 9 | AlignConsecutiveBitFields: None 10 | AlignConsecutiveDeclarations: None 11 | AlignEscapedNewlines: Right 12 | AlignOperands: Align 13 | AlignTrailingComments: true 14 | AllowAllArgumentsOnNextLine: true 15 | AllowAllParametersOfDeclarationOnNextLine: true 16 | AllowShortEnumsOnASingleLine: true 17 | AllowShortBlocksOnASingleLine: Never 18 | AllowShortCaseLabelsOnASingleLine: false 19 | AllowShortFunctionsOnASingleLine: All 20 | AllowShortLambdasOnASingleLine: All 21 | AllowShortIfStatementsOnASingleLine: Never 22 | AllowShortLoopsOnASingleLine: false 23 | AlwaysBreakAfterDefinitionReturnType: None 24 | AlwaysBreakAfterReturnType: None 25 | AlwaysBreakBeforeMultilineStrings: false 26 | AlwaysBreakTemplateDeclarations: MultiLine 27 | AttributeMacros: 28 | - __capability 29 | BinPackArguments: true 30 | BinPackParameters: true 31 | BraceWrapping: 32 | AfterCaseLabel: false 33 | AfterClass: false 34 | AfterControlStatement: Never 35 | AfterEnum: false 36 | AfterFunction: false 37 | AfterNamespace: false 38 | AfterObjCDeclaration: false 39 | AfterStruct: false 40 | AfterUnion: false 41 | AfterExternBlock: false 42 | BeforeCatch: false 43 | BeforeElse: false 44 | BeforeLambdaBody: false 45 | BeforeWhile: false 46 | IndentBraces: false 47 | SplitEmptyFunction: true 48 | SplitEmptyRecord: true 49 | SplitEmptyNamespace: true 50 | BreakBeforeBinaryOperators: None 51 | BreakBeforeConceptDeclarations: true 52 | BreakBeforeBraces: Attach 53 | BreakBeforeInheritanceComma: false 54 | BreakInheritanceList: BeforeColon 55 | BreakBeforeTernaryOperators: true 56 | BreakConstructorInitializersBeforeComma: false 57 | BreakConstructorInitializers: BeforeColon 58 | BreakAfterJavaFieldAnnotations: false 59 | BreakStringLiterals: true 60 | ColumnLimit: 80 61 | CommentPragmas: '^ IWYU pragma:' 62 | QualifierAlignment: Leave 63 | CompactNamespaces: false 64 | ConstructorInitializerIndentWidth: 4 65 | ContinuationIndentWidth: 4 66 | Cpp11BracedListStyle: true 67 | DeriveLineEnding: true 68 | DerivePointerAlignment: false 69 | DisableFormat: false 70 | EmptyLineAfterAccessModifier: Never 71 | EmptyLineBeforeAccessModifier: LogicalBlock 72 | ExperimentalAutoDetectBinPacking: false 73 | PackConstructorInitializers: BinPack 74 | BasedOnStyle: '' 75 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 76 | AllowAllConstructorInitializersOnNextLine: true 77 | FixNamespaceComments: true 78 | ForEachMacros: 79 | - foreach 80 | - Q_FOREACH 81 | - BOOST_FOREACH 82 | IfMacros: 83 | - KJ_IF_MAYBE 84 | IncludeBlocks: Preserve 85 | IncludeCategories: 86 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 87 | Priority: 2 88 | SortPriority: 0 89 | CaseSensitive: false 90 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 91 | Priority: 3 92 | SortPriority: 0 93 | CaseSensitive: false 94 | - Regex: '.*' 95 | Priority: 1 96 | SortPriority: 0 97 | CaseSensitive: false 98 | IncludeIsMainRegex: '(Test)?$' 99 | IncludeIsMainSourceRegex: '' 100 | IndentAccessModifiers: false 101 | IndentCaseLabels: false 102 | IndentCaseBlocks: false 103 | IndentGotoLabels: true 104 | IndentPPDirectives: None 105 | IndentExternBlock: AfterExternBlock 106 | IndentRequires: false 107 | IndentWidth: 4 108 | IndentWrappedFunctionNames: false 109 | InsertTrailingCommas: None 110 | JavaScriptQuotes: Leave 111 | JavaScriptWrapImports: true 112 | KeepEmptyLinesAtTheStartOfBlocks: true 113 | LambdaBodyIndentation: Signature 114 | MacroBlockBegin: '' 115 | MacroBlockEnd: '' 116 | MaxEmptyLinesToKeep: 1 117 | NamespaceIndentation: None 118 | ObjCBinPackProtocolList: Auto 119 | ObjCBlockIndentWidth: 2 120 | ObjCBreakBeforeNestedBlockParam: true 121 | ObjCSpaceAfterProperty: false 122 | ObjCSpaceBeforeProtocolList: true 123 | PenaltyBreakAssignment: 2 124 | PenaltyBreakBeforeFirstCallParameter: 19 125 | PenaltyBreakComment: 300 126 | PenaltyBreakFirstLessLess: 120 127 | PenaltyBreakOpenParenthesis: 0 128 | PenaltyBreakString: 1000 129 | PenaltyBreakTemplateDeclaration: 10 130 | PenaltyExcessCharacter: 1000000 131 | PenaltyReturnTypeOnItsOwnLine: 60 132 | PenaltyIndentedWhitespace: 0 133 | PointerAlignment: Right 134 | PPIndentWidth: -1 135 | ReferenceAlignment: Pointer 136 | ReflowComments: true 137 | RemoveBracesLLVM: false 138 | SeparateDefinitionBlocks: Leave 139 | ShortNamespaceLines: 1 140 | SortIncludes: CaseSensitive 141 | SortJavaStaticImport: Before 142 | SortUsingDeclarations: true 143 | SpaceAfterCStyleCast: false 144 | SpaceAfterLogicalNot: false 145 | SpaceAfterTemplateKeyword: true 146 | SpaceBeforeAssignmentOperators: true 147 | SpaceBeforeCaseColon: false 148 | SpaceBeforeCpp11BracedList: false 149 | SpaceBeforeCtorInitializerColon: true 150 | SpaceBeforeInheritanceColon: true 151 | SpaceBeforeParens: ControlStatements 152 | SpaceBeforeParensOptions: 153 | AfterControlStatements: true 154 | AfterForeachMacros: true 155 | AfterFunctionDefinitionName: false 156 | AfterFunctionDeclarationName: false 157 | AfterIfMacros: true 158 | AfterOverloadedOperator: false 159 | BeforeNonEmptyParentheses: false 160 | SpaceAroundPointerQualifiers: Default 161 | SpaceBeforeRangeBasedForLoopColon: true 162 | SpaceInEmptyBlock: false 163 | SpaceInEmptyParentheses: false 164 | SpacesBeforeTrailingComments: 1 165 | SpacesInAngles: Never 166 | SpacesInConditionalStatement: false 167 | SpacesInContainerLiterals: true 168 | SpacesInCStyleCastParentheses: false 169 | SpacesInLineCommentPrefix: 170 | Minimum: 1 171 | Maximum: -1 172 | SpacesInParentheses: false 173 | SpacesInSquareBrackets: false 174 | SpaceBeforeSquareBrackets: false 175 | BitFieldColonSpacing: Both 176 | Standard: Latest 177 | StatementAttributeLikeMacros: 178 | - Q_EMIT 179 | StatementMacros: 180 | - Q_UNUSED 181 | - QT_REQUIRE_VERSION 182 | TabWidth: 8 183 | UseCRLF: false 184 | UseTab: Never 185 | WhitespaceSensitiveMacros: 186 | - STRINGIZE 187 | - PP_STRINGIZE 188 | - BOOST_PP_STRINGIZE 189 | - NS_SWIFT_NAME 190 | - CF_SWIFT_NAME 191 | ... 192 | 193 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | main 2 | /obj 3 | /bin 4 | *.bin 5 | *.o 6 | clings 7 | clings_debug 8 | .clangd 9 | compile_commands.json 10 | .cache 11 | tags 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 danwritecode 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | _ _ 3 | | (_) 4 | ___| |_ _ __ __ _ ___ 5 | / __| | | '_ \ / _` / __| 6 | | (__| | | | | | (_| \__ \ 7 | \___|_|_|_| |_|\__, |___/ 8 | __/ | 9 | |___/ 10 | 11 | ``` 12 | 13 | # What is clings? 14 | clings is a C version of the popular program "rustlings" made for the Rust programming language. 15 | 16 | The purpose of this program is to take you from a total C noob, to somewhere beyond beginner. 17 | 18 | ![demo](https://raw.githubusercontent.com/danwritecode/clings/master/demo_v3.gif) 19 | 20 | # Dependencies 21 | 1. gcc 22 | 2. make 23 | 24 | You can install everything need (on ubuntu) with: 25 | ``` 26 | sudo apt-get install build-essential 27 | ``` 28 | 29 | # How do I use it? 30 | 1. Clone clings to your local machine 31 | 2. Open clings in your favorite text editor 32 | 3. Open a terminal session 33 | 4. Split your screen so you can view them side by side 34 | 5. Compile and start clings 35 | ``` 36 | make 37 | ``` 38 | ``` 39 | ./clings 40 | ``` 41 | 6. Start at exercise_01 and continue from there 42 | 43 | # Start Script 44 | 45 | If you are on a Debian-based distribution, you can use the `start_debian.sh` script to set up your environment. This script will download the repository, install necessary packages, and set up a Tmux split screen for immediate use. 46 | 47 | ### Instructions: 48 | 49 | 1. **Download and run the script**: 50 | 51 | ```bash 52 | sudo wget https://raw.githubusercontent.com/danwritecode/clings/master/start_debian.sh -O start_debian.sh 53 | sudo chmod +x start_debian.sh 54 | sudo ./start_debian.sh 55 | 56 | 57 | 58 | # Contributions 59 | The best way to contribute is by helping me create new exercises. I have created an issue for each exercise that needs to be created. The issues follow the flow of "The C Programming Language" book. 60 | 61 | To get started, find an exercise you want to work on and create a PR for it. Then refer to The C Programming Language for the specific chapter references in the issue title. 62 | 63 | ### Exercise Creation Instructions: 64 | 1. Create a new directory for the exercise based on the name provided in the issue. 65 | 2. Create a readme with an explanation for the user. This should teach them what they need to know without giving the answer. 66 | 3. Create 1 or more ".c" exercise files. The file(s) should either fail to compile, or run, or both. 67 | - The exercise files should be interactive, they should lead the user to learning the things they need to learn. 68 | - If multiple exercises are needed to have then build up knowledge, then this is fine. 69 | 70 | **Note:** The objective is not to directly copy the examples or code in the book. We want to create exercises based on the concepts covered in the chapter. The book is serving as a rough guideline for the exercises. Once we have all the exercises from the book, all contributors will work together to figure out how to consolidate and come up with a final set of exercises as the book can be redundant in some spots. 71 | 72 | Feel free to ask questions if you need more details. 73 | 74 | 75 | ### Exercise Directory structure 76 | 1. README.md file is required 77 | 2. N number of ".c" exercises can be included, the program will work through them dynamically 78 | 3. At the top of any exercise, provide vague explanation of the objective of the exercise without giving the answer 79 | 80 | # To do 81 | 1. Rustlings has the concept of asking for a hint, will eventually look to integrate this 82 | 83 | ### Note 84 | I did this as a project to help me learn C. The source code is far from perfect. I would gladly accept any code reviews and will work to fix any issues that are submitted. 85 | -------------------------------------------------------------------------------- /demo_v3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danwritecode/clings/e0b74967e1419928c1e12c22c262256ee653f4cc/demo_v3.gif -------------------------------------------------------------------------------- /exercises/01_welcome/README.md: -------------------------------------------------------------------------------- 1 | Welcome to clings! 2 | 3 | clings is an interactive course to help you learn C entirely through the terminal and your text editor. 4 | 5 | Each exercise will due to 1 or both of the following reasons: 6 | 1. A compilation error 7 | 2. A runtime error 8 | 9 | For each exercise, your mission is simple: fix the errors you encounter. 10 | 11 | Once all errors have been fixed, the program will automatically move you to the next exercise. 12 | 13 | Good luck and have fun! 14 | -------------------------------------------------------------------------------- /exercises/01_welcome/hello_world.c: -------------------------------------------------------------------------------- 1 | // Welcome to clings! 2 | // This first exercise is just to show you how things work. 3 | // 4 | // There are two ways an exercise could be failing: 5 | // 1. Compilation Error 6 | // 2. Runtime Error 7 | // 8 | // Your goal is to complete an exercise and get it to both compile and also to run with no errors 9 | // while following the directions of the exercise. 10 | // 11 | // Once there are no errors and you're ready to move on, delete the "I AM NOT DONE" line. 12 | 13 | 14 | // ❌ I AM NOT DONE 15 | 16 | 17 | #include 18 | 19 | int main() 20 | { 21 | char msg[] = "\033[1;32m" 22 | " _ _ \n" 23 | " | (_) \n" 24 | " ___| |_ _ __ __ _ ___ \n" 25 | " / __| | | '_ \\ / _` / __|\n" 26 | "| (__| | | | | | (_| \\__ \\\n" 27 | " \\___|_|_|_| |_|\\__, |___/\n" 28 | " __/ | \n" 29 | " |___/ \n" 30 | "\033[0m"; 31 | 32 | printf("%s\n", msg); 33 | 34 | // Missing something here? What does the compiler say? 35 | printf("Welcome to clings! \n") 36 | 37 | // This is a segmentation fault 38 | // It's added to demonstrate a runtime failure 39 | // For now, just delete these lines to see a successful exercise completion 40 | int *ptr = NULL; 41 | *ptr = 42; 42 | 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /exercises/02_variables/README.md: -------------------------------------------------------------------------------- 1 | For more information please refer to Chapter 1.2 of "The C Programming Language". 2 | -------------------------------------------------------------------------------- /exercises/02_variables/variables_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Variables in C must be declared before they are used. Typically you will see them declared at the top of a function. 3 | * 4 | * Take a look at the program below and figure out what primitive datatype each variable should be and assign them accordingly. 5 | */ 6 | 7 | // ❌ I AM NOT DONE 8 | 9 | int main() { 10 | x = 10; 11 | y = 1.0; 12 | z = 'A'; 13 | 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /exercises/02_variables/variables_02.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Arithmetic in C is pretty straightforward, though there are some gotchas. 3 | * 4 | * If you use integers for division for example, you may lose precision in the output. 5 | * 6 | * The program below is using integers and is thus failing the assertion for a certain level of precision. 7 | * 8 | * Make changes to the program so that precision is not lost. 9 | */ 10 | 11 | // ❌ I AM NOT DONE 12 | 13 | 14 | #include 15 | #include 16 | 17 | #define EPSILON 0.000001 18 | 19 | int main() { 20 | int x = 100.0; 21 | int y = 9.0; 22 | int z = x / y; 23 | 24 | // DO NOT CHANGE THIS 25 | assert(fabs(z - 11.111111) < EPSILON); 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /exercises/02_variables/variables_03.c: -------------------------------------------------------------------------------- 1 | /* 2 | * So what is a practical application of all of this? 3 | * 4 | * How about a program to convert from Fahrenheit to Celcius. 5 | * 6 | * The program below is not accurate enough, how can we fix that? 7 | */ 8 | 9 | // ❌ I AM NOT DONE 10 | 11 | 12 | #include 13 | #include 14 | 15 | #define EPSILON 0.000001 16 | 17 | int main() { 18 | int fahrenheit = 100; 19 | int celcius = (5 / 9) * (fahrenheit - 32); 20 | 21 | // DO NOT CHANGE THIS 22 | assert(fabs(celcius - 37.777779) < EPSILON); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /exercises/03_for_statement/README.md: -------------------------------------------------------------------------------- 1 | For more information please refer to Chapter 1.3 of "The C Programming Language". 2 | -------------------------------------------------------------------------------- /exercises/03_for_statement/for_statement_01.c: -------------------------------------------------------------------------------- 1 | /** LEARNING 2 | * Your goal is to understand how the for statement works. 3 | * 4 | * The for statement has the following syntax: 5 | * 6 | * for (initialization; condition; increment) { 7 | * statement; 8 | * statement; 9 | * ... 10 | * } 11 | * 12 | * The for statement is equivalent to the following while statement: 13 | * 14 | * initialization; 15 | * while (condition) { 16 | * statement; 17 | * statement; 18 | * ... 19 | * increment; 20 | * } 21 | */ 22 | 23 | /** EXERCISE 24 | * Your job is to write a for statement equivalent to the following 25 | * while statement: 26 | * 27 | * i = 0; // initialization 28 | * while (i < 10) { // condition 29 | * printf("%d\n", i); 30 | * verify_count(&count, i); 31 | * i++; // increment 32 | * } 33 | */ 34 | 35 | // ❌ I AM NOT DONE 36 | 37 | #include 38 | #include 39 | #include 40 | 41 | // DO NOT CHANGE THIS 42 | static int track = 0, count = 0; 43 | 44 | // DO NOT CHANGE THIS 45 | void verify_count (int *count, int i) { 46 | assert(i == track++); 47 | (*count)++; 48 | } 49 | 50 | int main() { 51 | int i = rand() % 100 + 11; // DO NOT CHANGE THIS 52 | 53 | // YOUR CODE HERE 54 | for (initialization; condition; increment) { 55 | // END YOUR CODE 56 | printf("%d\n", i); 57 | verify_count(&count, i); // DO NOT CHANGE THIS 58 | } 59 | 60 | // BONUS: print the value of i here and try to understand why it's 10 61 | 62 | // DO NOT CHANGE THIS 63 | assert (count == 10); 64 | return 0; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /exercises/03_for_statement/for_statement_02.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The provided program generates a table of 3 | * temperatures in Fahrenheit and their corresponding 4 | * Celsius equivalents, increasing in steps of 20 degrees from 5 | * 0°F to 300°F. The Celsius temperature is calculated directly 6 | * within the printf statement using a for loop. 7 | * 8 | * Modify the provided temperature conversion 9 | * C program to print the Fahrenheit to Celsius c 10 | * onversion table in reverse order, 11 | * from 0 degrees Fahrenheit up to 300 degrees. 12 | */ 13 | 14 | // ❌ I AM NOT DONE 15 | 16 | #include 17 | #include 18 | 19 | #define TRACK 320 20 | 21 | static int track = 0; 22 | 23 | // DO NOT CHANGE THIS 24 | void verify_count (int i) { 25 | assert(i == track); 26 | track += 20; 27 | } 28 | 29 | int main() { 30 | // YOUR CODE HERE 31 | for (int fahr = 300; fahr >= 0; fahr -= 20) { 32 | // END YOUR CODE 33 | printf("%3d %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32)); 34 | 35 | verify_count(fahr); // DO NOT CHANGE THIS 36 | } 37 | 38 | // BONUS: print the value of track here 39 | // and try to understand why it's 320 40 | 41 | // DO NOT CHANGE THIS 42 | assert (track == TRACK); 43 | 44 | return 0; 45 | } 46 | 47 | -------------------------------------------------------------------------------- /exercises/04_symbolic_constants/README.md: -------------------------------------------------------------------------------- 1 | For more information please refer to Chapter 1.3 of "The C Programming Language". 2 | 3 | -------------------------------------------------------------------------------- /exercises/04_symbolic_constants/symbolic_constants_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * In programming, hardcoding numbers directly into your code 3 | * (often referred to as "magic numbers") can make the code less 4 | * understandable and harder to maintain. 5 | * 6 | * Using symbolic constants can help alleviate these issues by 7 | * providing meaningful names for these numbers. 8 | * 9 | * Below is a simple C program that prints a Fahrenheit-to-Celsius 10 | * conversion table. The program currently uses magic numbers. 11 | * Your task is to refactor this program to use symbolic constants. 12 | * 13 | * Example macro definition: 14 | * #define PI 3.14159 15 | * 16 | * Example usage: 17 | * double circumference = 2 * PI * radius; 18 | */ 19 | 20 | // ❌ I AM NOT DONE 21 | 22 | #include 23 | 24 | // [TODO] DEFINE CONSTANTS LOWER, UPPER, AND STEP WITH APPROPRIATE VALUES. 25 | #define LOWER // lower limit of the table 26 | #define UPPER // upper limit of the table 27 | #define STEP // step size between consecutive temperatures 28 | // END OF THE CONSTANTS 29 | 30 | int main() { 31 | int fahr; 32 | 33 | // [TODO] REPLACE THE MAGIC NUMBERS WITH THE SYMBOLIC CONSTANTS. 34 | for (fahr = 0; fahr <= 300; fahr += 20) { 35 | // END OF THE REPLACEMENT 36 | printf("%3d %6.1f\n", fahr, (5.0/9.0)*(fahr-32)); 37 | } 38 | 39 | return 0; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /exercises/15_variable_names/README.md: -------------------------------------------------------------------------------- 1 | For more information please refer to Chapter 2.1 of "The C Programming Language". 2 | -------------------------------------------------------------------------------- /exercises/15_variable_names/variable_names_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Variable naming seems pretty simple however there are some gotchas. 3 | * 4 | * The primary gotchas are: "reserved words" and "illegal characters". 5 | * 6 | * Take a look at the below exercise and identify the variables that are 7 | * causing the compilation error. 8 | */ 9 | 10 | // ❌ I AM NOT DONE 11 | 12 | int main() { 13 | // [TODO] Identify the variable(s) that is causing the compilation error. 14 | char foo1 = 'a'; 15 | float float = 1.0; 16 | int bar = 1; 17 | char 1baz = 'a'; 18 | 19 | return 0; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /exercises/16_data_types/README.md: -------------------------------------------------------------------------------- 1 | # Data types 2 | 3 | For more information please refer to Chapter 2.2 of "The C Programming Language". 4 | -------------------------------------------------------------------------------- /exercises/16_data_types/data_type_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * In this exercise, you will practice understanding data types and sizes in C. 3 | * The basic data types in C include `char`, `int`, `float`, and `double`. 4 | * You can also apply qualifiers like `short`, `long`, `signed`, and `unsigned` to these types, 5 | * which affect their size and behavior. Understanding how these types and qualifiers 6 | * work at a binary level is crucial for robust C programming. 7 | * 8 | * The provided file will not compile or run correctly until 9 | * the user fixes the mistakes. 10 | * (You can only delete/edit lines with // <-- Fix this line) 11 | */ 12 | 13 | // ❌ I AM NOT DONE 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | // Note: Carefully observe the output to understand the underlying behavior caused by type limits and conversions. 21 | // |char| <= |short| <= |int| <= |long| <= |long long| 22 | 23 | int main() { 24 | unsigned int negativeAsUnsigned = -5; // <-- Fix this kind of misuse 25 | 26 | long long veryLargeNumber = 9223372036854775807; 27 | printf("Maximum long long value: %l\n", veryLargeNumber); // <-- Fix this line 28 | 29 | printf("Range of unsigned long long: 0 to %llu\n", ); // <-- Fix this line (use symbolic constant limits.h) 30 | printf("Range of signed long long: %lld to %lld\n", ); // <-- Fix this line (use symbolic constant) 31 | printf("Range of unsigned char: %d to %d\n", ); // <-- Fix this line (Write a number) 32 | printf("Range of signed char: %d to %d\n", ); // <-- Fix this line (write a number) 33 | 34 | double floatingPointNumber = 0.999999; 35 | int integerRepresentation = floatingPointButNotReally; // <-- Fix this line (use floatingPointNumber) 36 | printf("Double to int casting result: %d\n", integerRepresentation); 37 | 38 | bool understand = false; 39 | unsigned int plus_one = 1; 40 | int minus_one = -1; 41 | 42 | if(plus_one > minus_one) { // <-- Fix this line 43 | understand = true; 44 | } 45 | assert(understand); 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /exercises/17_constants/README.md: -------------------------------------------------------------------------------- 1 | # Constants 2 | 3 | For more information please refer to Chapter 2.1 of "The C Programming Language". 4 | -------------------------------------------------------------------------------- /exercises/17_constants/constants_1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * A constant in programming is a value that remains unchanged throughout the 3 | * execution of a program. 4 | * 5 | * This exercise focuses on defining constants correctly. The provided file will 6 | * not compile until the user correctly defines all constants. 7 | * (You can only delete/edit lines with // <-- Fix this line) 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | // ❌ I AM NOT DONE 15 | 16 | int main() { 17 | // Correctly Define the Constants 18 | 19 | // Define a constant integer with the value 100 20 | int_const = ; // <-- Fix this line 21 | 22 | // Define a constant float with the value 3.14 23 | float_const = ; // <-- Fix this line 24 | 25 | // Define a constant character with the value 'A' 26 | char_const = ; // <-- Fix this line 27 | 28 | // Define a constant string with the value "Hello, World!" 29 | str_const = ; // <-- Fix this line 30 | 31 | // DO NOT CHANGE 32 | assert(int_const == 100); 33 | assert(float_const == 3.14f); 34 | assert(char_const == 'A'); 35 | assert(strcmp(str_const, "Hello, World!") == 0); 36 | 37 | printf("All constants are correctly defined!\n"); 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /exercises/17_constants/constants_2.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * This exercise focuses on using constants correctly in calculations and 4 | * logical operations. The provided file will not compile or run correctly until 5 | * the user fixes the mistakes. 6 | * (You can only delete/edit lines with // <-- Fix this line) 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | // ❌ I AM NOT DONE 13 | 14 | /* 15 | * The first name in an `enum` has value 0, the next 1, and so on, unless 16 | * explicit values are specified 17 | */ 18 | enum boolean { NO, YES }; 19 | 20 | int main() { 21 | // Correctly Use the Constants 22 | const int MAX_VALUE = 100; 23 | const float PI = 3.14f; 24 | 25 | MAX_VALUE = 90; // <-- Fix this line 26 | 27 | int result = +50; // <-- Fix this line: Should be 150 (use MAX_VALUE) 28 | float area = PI * 10 * 10; 29 | 30 | // Logical operation 31 | const char FLAG_TRUE = 'T'; 32 | const char FLAG_FALSE = 'F'; 33 | 34 | char flag = 'T'; 35 | int isTrue = (flag ==) ? YES : NO; // <-- Fix this line 36 | 37 | // DO NOT CHANGE 38 | assert(result == 150); 39 | assert(area == 314.0f); 40 | assert(isTrue == 1); 41 | 42 | printf("Constants used correctly in calculations and logical operations!\n"); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /exercises/17_constants/constants_3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * This exercise focuses on `enumeration constants`. 4 | * The provided file will not compile or run correctly until 5 | * the user fixes the mistakes. 6 | * 7 | */ 8 | 9 | // ❌ I AM NOT DONE 10 | 11 | #include 12 | 13 | // Complete the escape squences: 14 | enum escapes { BELL =, BACKSPACE =, TAB =, NEWLINE =, VTAB =, RETURN = "\r" }; 15 | 16 | int main(void) { 17 | 18 | // DO NOT CHANGE 19 | assert(BELL == 7); 20 | assert(BACKSPACE == 8); 21 | assert(TAB == 9); 22 | assert(NEWLINE == 10); 23 | assert(VTAB == 11); 24 | assert(RETURN == 13); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /exercises/18_declarations/README.md: -------------------------------------------------------------------------------- 1 | # Declarations 2 | 3 | For more information please refer to Chapter 2.4 of "The C Programming Language". 4 | -------------------------------------------------------------------------------- /exercises/18_declarations/declarations_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The provided file will not compile until the user fixes all mistakes. 3 | * (You can only delete/edit lines with // <-- Fix this line) 4 | */ 5 | 6 | // ❌ I AM NOT DONE 7 | 8 | #include 9 | 10 | int main() { 11 | int lower = 0, upper; // <-- Fix this line 12 | char c, line[1000]; 13 | 14 | lower = 10; 15 | upper = 20; 16 | step = 2; // <-- Fix this line 17 | 18 | for (int i = lower; i <= upper; i += step) { 19 | printf("Value: %d\n", i); 20 | } 21 | 22 | float eps; 23 | const double e = 2.71828182845905; 24 | const char msg[] = "warning: "; 25 | 26 | eps = 1.0e-5; 27 | 28 | e = 3.14; // <-- Fix this line 29 | 30 | printf("%s The value of e is: %f\n", msg, e); 31 | printf("Epsilon value is: %f\n", eps); 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /exercises/19_arithmetic_operators/README.md: -------------------------------------------------------------------------------- 1 | # Arithmetic operators 2 | 3 | For more information please refer to Chapter 2.5 of "The C Programming Language". 4 | -------------------------------------------------------------------------------- /exercises/19_arithmetic_operators/arithmetic_operators_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Fix this short program, which checks for a leap year. 3 | * 4 | * You can only delete or edit lines with the comment "// <-- Fix this line". 5 | * Ensure the program correctly identifies leap years according to the following rules: 6 | * 1. A year is a leap year if it is divisible by 4. 7 | * 2. However, if the year is also divisible by 100, it is not a leap year unless it is also divisible by 400. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | // ❌ I AM NOT DONE 15 | 16 | int main() { 17 | 18 | int year = 2024; 19 | bool isLeap = false; 20 | 21 | if ((year / 4 == 0 && year % 100 == 0) || year % 400 == 0) { // <-- Fix this line 22 | isLeap = true; 23 | printf("%d is a leap year\n", year); 24 | } else { 25 | isLeap = false; 26 | printf("%d is not a leap year\n", year); 27 | } 28 | 29 | 30 | assert(isLeap == true); 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /exercises/19_arithmetic_operators/arithmetic_operators_02.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Fix the program 3 | * 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // ❌ I AM NOT DONE 11 | 12 | int main() { 13 | int a = 1; 14 | int b = 2; 15 | int c = 3; 16 | int result; 17 | 18 | // Only add parentheses, so that result == 2 19 | result = b * c / a - b % c; // <-- Fix this line 20 | 21 | assert(result == 2); 22 | printf("The result is %d\n", result); 23 | 24 | return 0; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /exercises/20_relational_logical_operators/README.md: -------------------------------------------------------------------------------- 1 | # Relational and logical Operators 2 | 3 | For more information please refer to Chapter 2.6 of "The C Programming Language". 4 | -------------------------------------------------------------------------------- /exercises/20_relational_logical_operators/relational_logical_operators_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This exercise will focus on relational operators. 3 | * Fix the if statements so that the code correctly compares the values of a and b. 4 | * 5 | * 6 | */ 7 | 8 | // ❌ I AM NOT DONE 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | int main(void) { 16 | int a = 3; 17 | int b = 5; 18 | 19 | if (a b) { 20 | printf("equal\n"); 21 | } else if (a b) { 22 | printf("not equal\n"); 23 | } else if (a b) { 24 | printf("less than\n"); 25 | } else if (a b) { 26 | printf("greater than\n"); 27 | } else if (a b) { 28 | printf("less than or equal\n"); 29 | } else if (a b) { 30 | printf("greater than or equal\n"); 31 | } 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /exercises/20_relational_logical_operators/relational_logical_operators_02.c: -------------------------------------------------------------------------------- 1 | /* 2 | * The goal of this exercise is to understand how to use logical operators. 3 | * Fix the `evaluate_logical` function so it returns the correct value. 4 | */ 5 | 6 | // ❌ I AM NOT DONE 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | bool evaluate_logical(bool a, bool b, bool c) { 13 | return a b c; // <-- Fix here 14 | } 15 | 16 | int main() { 17 | // Only 'a' is true, should return false 18 | assert(evaluate_logical(true, false, false) == false); 19 | 20 | // etc. 21 | assert(evaluate_logical(false, true, false) == false); 22 | assert(evaluate_logical(false, false, true) == true); 23 | assert(evaluate_logical(false, false, false) == false); 24 | assert(evaluate_logical(true, true, true) == true); 25 | 26 | printf("All tests passed.\n"); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /exercises/21_type_conversions/README.md: -------------------------------------------------------------------------------- 1 | # Type conversions 2 | 3 | For more information please refer to Chapter 2.7 of "The C Programming Language". 4 | -------------------------------------------------------------------------------- /exercises/21_type_conversions/type_conversions_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Fix the program to handle type conversions correctly 3 | * 4 | * In this exercise, you will correct the type conversion issues in the given program. 5 | * The program performs arithmetic operations involving integers, floats, and doubles. 6 | * Your task is to ensure that the calculations are correct and the results match the expected values. 7 | * 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | // ❌ I AM NOT DONE 15 | 16 | int main() { 17 | 18 | int i1 = 1; 19 | int i2 = 3; 20 | 21 | int a; 22 | float b; 23 | double c; 24 | 25 | a = i1 / i2; 26 | b = i1 / (i2 + ); // <-- Fix here (b = 0.25) 27 | c = i1 / i2; // <-- Fix here (c = 0.333333) 28 | 29 | printf("a = %d, b = %f, c = %lf\n", a, b, c); 30 | 31 | // DO NOT CHANGE 32 | double tolerance = 0.000001; 33 | assert(a == 0); 34 | assert((b - 0.25) < tolerance); 35 | assert((c - 0.333333) < tolerance); 36 | 37 | printf("All tests passed.\n"); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /exercises/21_type_conversions/type_conversions_02.c: -------------------------------------------------------------------------------- 1 | /* 2 | * In this exercise, you will correct the issues in the given program. 3 | * The program should convert all lowercase letters in a string to uppercase. 4 | * Your task is to implement the correct logic in the `to_upper` function. 5 | * 6 | * We will use the properties of a char. A char has a size of 1 byte (8 bits), 7 | * so it can store numbers from -128 to 127 if signed, or 0 to 255 if unsigned. 8 | * If plain char is signed or unsigned is machine-dependent. 9 | * Printable characters are always positive. 10 | * 11 | * When you define a char, C actually stores it as an integer underneath. 12 | * To display characters, each char is mapped to a number using the ASCII table. 13 | * The ASCII table maps numbers from 0-127 to specific characters. 14 | * For example, the character 'A' is mapped to the number 65, and the character 'a' is mapped to the number 97. 15 | * We can use this mapping to our advantage to convert lowercase letters to uppercase. 16 | * 17 | * 1. Check if the letter is lowercase. 18 | * 2. Convert the letter to uppercase 19 | * 20 | * Do not add any libraries. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | // ❌ I AM NOT DONE 28 | // hint: Take a look at ASCII table 29 | 30 | char to_upper(char c) { 31 | // We can actually campare chars (because in memory they are just numbers) 32 | if(c 'a' && c 'z') { // <-- 1. Fix here 33 | // <-- 2. Fix here 34 | } 35 | 36 | return c; 37 | } 38 | 39 | 40 | int main() { 41 | char s[] = "hello, world!"; 42 | 43 | for(int i = 0, len = strlen(s); i < len; i++) { 44 | s[i] = to_upper(s[i]); 45 | } 46 | 47 | printf("%s\n", s); 48 | 49 | assert(strcmp(s, "HELLO, WORLD!") == 0); 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /exercises/22_inc_dec_operators/README.md: -------------------------------------------------------------------------------- 1 | # Increment and Decrement Operators 2 | 3 | For more information please refer to Chapter 2.8 of "The C Programming Language". 4 | -------------------------------------------------------------------------------- /exercises/22_inc_dec_operators/inc_dec_operators_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This program demonstrates the use of increment (++) and decrement (--) operators in C. 3 | * The increment operator adds 1 to its operand, while the decrement operator subtracts 1. 4 | * The unusual aspect is that these operators can be used either as prefix or postfix. 5 | * 6 | * Prefix (e.g., ++i): Increment/decrement first, then return the new value. 7 | * Postfix (e.g., i++): Return the current value first, then increment/decrement. 8 | */ 9 | 10 | // ❌ I AM NOT DONE 11 | 12 | #include 13 | #include 14 | 15 | int main() { 16 | int i = 0; 17 | int j = 2; 18 | int k = 0; 19 | int l = 3; 20 | 21 | // Use ++/-- instead 22 | i = i + 1; // <-- Fix here 23 | j = j - 1; // <-- Fix here 24 | 25 | // DO NOT CHANGE 26 | assert(i == 1); 27 | assert(j == 1); 28 | 29 | k = (i+j)++; // <-- Fix bug 30 | 31 | i = l++; // <-- Fix bug 32 | 33 | // DO NOT CHANGE 34 | assert(i == 4); 35 | assert(j == 1); 36 | assert(k == 2); 37 | assert(l == 4); 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /exercises/23_bitwise_operators/README.md: -------------------------------------------------------------------------------- 1 | # Bitwise operators 2 | 3 | For more information please refer to Chapter 2.8 of "The C Programming Language". 4 | -------------------------------------------------------------------------------- /exercises/23_bitwise_operators/bitwise_operators_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Now we will start with bitwise operators, which help you manipulate bits directly. 3 | * Bitwise operators are essential for low-level programming and are often used in systems programming, embedded systems, and performance-critical code. 4 | * If you do not know bitwise operators, you can learn more here: https://en.wikipedia.org/wiki/Bitwise_operations_in_C 5 | * 6 | * In this exercise, your task is to find the correct bitwise operator so that each result matches the expected value. 7 | * 8 | * Here are some common bitwise operators in C that you might need: 9 | * - & : Bitwise AND 10 | * - | : Bitwise OR 11 | * - ^ : Bitwise XOR (exclusive OR) 12 | * - ~ : Bitwise NOT (one's complement) 13 | * - << : Left shift 14 | * - >> : Right shift 15 | */ 16 | 17 | // Hint: convert number `a` and `b` to binary. 18 | 19 | // ❌ I AM NOT DONE 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | int main(void) { 27 | int a = 5; 28 | int b = 2; 29 | 30 | int f = a 2; // <-- Fix here (hint: multiply `a` by 2^2) 31 | assert(f == 20); // f should be 20 ... 32 | 33 | int e = a b; // <-- Fix here 34 | assert(e == 7); 35 | 36 | int c = a b; // <-- Fix here 37 | assert(c == 0); 38 | 39 | int k = -1; 40 | int g = // <-- Fix here: (use k somehow) 41 | assert(g == 0); 42 | 43 | int d = a b; // <-- Fix here 44 | assert(d == 7); 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /exercises/23_bitwise_operators/bitwise_operators_02.c: -------------------------------------------------------------------------------- 1 | /* Now we will start with bitwise operators, which helps you manipulate bits. 2 | * If you do not know bitwise operators look here: https://en.wikipedia.org/wiki/Bitwise_operations_in_C 3 | * 4 | * Let's start with simple example. 5 | * Fix `is_even` function, which returns true if number is even. 6 | * Of course we could use %, but we are smarter! 7 | * Write down even and odd numbers in binary and try to find pattern, which you could use for deciding if number is even or not. 8 | * Decide which bitwise operator would work. 9 | * 10 | */ 11 | 12 | // ❌ I AM NOT DONE 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | void print_bits(char num) { 20 | printf("%d: ", num); 21 | for(long i = sizeof(num) * CHAR_BIT - 1; i >= 0; i--) { 22 | printf("%d ", (num >> i) & 1); 23 | } 24 | 25 | printf("\n"); 26 | } 27 | 28 | bool is_even(int num) { 29 | // Pay attention to operator precendence 30 | if(num 1 == 0) { // <-- Fix here 31 | return true; 32 | } 33 | 34 | return false; 35 | } 36 | 37 | int main(void) { 38 | char num = 7; 39 | print_bits(num); 40 | print_bits(1); 41 | 42 | assert(is_even(num) == 0); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /exercises/23_bitwise_operators/bitwise_operators_03.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Now let's try to create a bit mask that will mask out (set to 0) the lower four bits of a byte (8 bits). 3 | * In other words, we want to keep the upper 4 bits unchanged and set the lower 4 bits to 0. 4 | * 5 | * Example: 6 | * 7 | * Let's say we have the following byte: 8 | * i = 10111011 9 | * 10 | * After applying the bit mask to clear the lower four bits, we should get: 11 | * result = 10110000 12 | * 13 | * Steps: 14 | * 1. Create a bit mask that has 1s in the upper four bit positions and 0s in the lower four bit positions. 15 | * 2. Apply the mask to the given byte using the bitwise AND operator. 16 | */ 17 | 18 | // ❌ I AM NOT DONE 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | void print_bits(unsigned char num) { 25 | printf("%3d: ", num); 26 | for(long i = sizeof(num) * CHAR_BIT - 1; i >= 0; i--) { 27 | printf("%d ", (num >> i) & 1); 28 | } 29 | 30 | printf("\n"); 31 | } 32 | 33 | int main(void) { 34 | 35 | unsigned char i = 0xBB; // 10111011 36 | print_bits(i); 37 | 38 | unsigned char mask = ; // <-- Fix here 39 | i = i mask; // <-- Fix here 40 | 41 | print_bits(i); 42 | assert(i == 0xB0); // 10110000 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /exercises/23_bitwise_operators/bitwise_operators_04.c: -------------------------------------------------------------------------------- 1 | /* 2 | * XOR Swap Algorithm 3 | * 4 | * The XOR (exclusive or) operator has special properties that allow us to swap two values 5 | * without using a temporary variable. This technique is called the XOR swap. 6 | * 7 | * How XOR works: 8 | * - When you XOR a bit with 0, it remains unchanged 9 | * - When you XOR a bit with 1, it flips (0 becomes 1, and 1 becomes 0) 10 | * - XOR is commutative: a XOR b = b XOR a 11 | * - XOR is associative: (a XOR b) XOR c = a XOR (b XOR c) 12 | * 13 | * Example: 14 | * Let a = 1010 and b = 0011 15 | * Step 1: a = a XOR b -> a = 1010 XOR 0011 = 1001, b = 0011 16 | * Step 2: b = a XOR b -> a = 1001, b = 1001 XOR 0011 = 1010 17 | * Step 3: a = a XOR b -> a = 1001 XOR 1010 = 0011, b = 1010 18 | * 19 | * Result: The values of 'a' and 'b' have been swapped. 20 | * 21 | * Task: Use these properties of XOR to swap the values of 'a' and 'b' without using a temporary variable. 22 | */ 23 | 24 | // ❌ I AM NOT DONE 25 | 26 | #include 27 | 28 | int main(void) { 29 | 30 | int a = 11; 31 | int b = 42; 32 | 33 | if(a == b) return 0; 34 | 35 | // Fix code 36 | a = a ^ b; 37 | 38 | a = a ^ b; 39 | // Fix code 40 | 41 | 42 | assert(a == 42); 43 | assert(b == 11); 44 | 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /exercises/23_bitwise_operators/bitwise_operators_05.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Bit Manipulation Exercise: Swap Most Significant Bit (MSB) and Least Significant Bit (LSB) 3 | * 4 | * Your task is to implement the `swap_edge_bits` function, which swaps the MSB and LSB of an 8-bit unsigned char. 5 | * 6 | * MSB = Most Significant Bit (leftmost bit) 7 | * LSB = Least Significant Bit (rightmost bit) 8 | * 9 | * Example: 10 | * 11 | * Input: 10000000 (decimal 128) 12 | * Output: 00000001 (decimal 1) 13 | * 14 | * The function should follow these steps: 15 | * 1. Extract the MSB, move to LSB 16 | * 2. Extract the LSB, move to MSB 17 | * 3. Create a mask to clear both MSB and LSB 18 | * 4. Combine the extracted bits with the masked value 19 | * 20 | * This exercise demonstrates bitwise operations and manipulations on individual bits. 21 | */ 22 | 23 | // ❌ I AM NOT DONE 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | void print_bits(unsigned char num) { 30 | printf("%3d: ", num); 31 | for(long i = sizeof(num) * CHAR_BIT - 1; i >= 0; i--) { 32 | printf("%d ", (num >> i) & 1); 33 | } 34 | 35 | printf("\n"); 36 | } 37 | 38 | // unsigned char has 8 bits 39 | unsigned char swap_edge_bits(unsigned char num) { 40 | unsigned char msb = ; // <-- Fix here 41 | unsigned char lsb = ; // <-- Fix here 42 | unsigned char mask = ; // <-- Fix here 43 | 44 | num = num mask msb lsb; // <-- Fix here 45 | 46 | return num; 47 | } 48 | 49 | 50 | int main(void) { 51 | 52 | unsigned char i = 128; 53 | print_bits(i); 54 | 55 | i = swap_edge_bits(i); 56 | 57 | print_bits(i); 58 | assert(i == 1); 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /exercises/24_assignment_operators/README.md: -------------------------------------------------------------------------------- 1 | # Assignment operators 2 | 3 | For more information please refer to Chapter 2.10 of "The C Programming Language". 4 | -------------------------------------------------------------------------------- /exercises/24_assignment_operators/assignment_operators_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This expression: 3 | * a = a + 1 4 | * can be written like this: 5 | * a += 1 6 | * 7 | * The += operator is an assignment operator. 8 | * 9 | * Most binary operators have corresponding assignment operators. 10 | * These can be useful for writing cleaner and more understandable code. 11 | */ 12 | 13 | // ❌ I AM NOT DONE 14 | 15 | #include 16 | #include 17 | 18 | int main() { 19 | int a = 4; 20 | int b = 1; 21 | 22 | a = a / 2; // <-- Fix here (use an assignment operator) 23 | b = b * 3; // <-- Fix here (use an assignment operator) 24 | 25 | assert(a == 2); 26 | assert(b == 3); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /exercises/25_conditional_expressions/README.md: -------------------------------------------------------------------------------- 1 | # Conditional expressions 2 | 3 | For more information please refer to Chapter 2.11 of "The C Programming Language". 4 | -------------------------------------------------------------------------------- /exercises/25_conditional_expressions/conditional_expressions_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Rewrite the `to_upper` function from the previous exercise without using an `if` statement. 3 | * Instead, use a conditional (ternary) expression. 4 | * 5 | * A conditional expression looks like this: 6 | * condition ? value_if_true : value_if_false 7 | * 8 | * For example: 9 | * z = (a > b) ? a : b; 10 | * 11 | * In this example, if `a` is greater than `b`, then `z` will be assigned the value of `a`. 12 | * Otherwise, `z` will be assigned the value of `b`. 13 | * 14 | * Conditional expressions can sometimes make the code easier to read. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | // ❌ I AM NOT DONE 22 | 23 | char to_upper(char c) { 24 | // Rewrite 25 | if(c >= 'a' && c <= 'z') { 26 | return c - 32; 27 | } 28 | 29 | return c; 30 | // Rewrite 31 | } 32 | 33 | int main() { 34 | char s[] = "hello, world!"; 35 | 36 | for(int i = 0, len = strlen(s); i < len; i++) { 37 | s[i] = to_upper(s[i]); 38 | } 39 | 40 | printf("%s\n", s); 41 | 42 | assert(strcmp(s, "HELLO, WORLD!") == 0); 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /exercises/26_operator_precedence/README.md: -------------------------------------------------------------------------------- 1 | # Precedence and Order of Evaluation 2 | 3 | For more information please refer to Chapter 2.12 of "The C Programming Language". 4 | -------------------------------------------------------------------------------- /exercises/26_operator_precedence/operator_precedence_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This program simulates a banking system with various operations. 3 | * It contains errors related to operator precedence, associativity, and side effects. 4 | * 5 | * Your task is to identify and fix the mistakes in the program. 6 | * The program should pass all assertions when corrected. 7 | * 8 | * Common mistakes to look out for: 9 | * - Incorrect operator precedence 10 | * - Unintended side effects 11 | * - Misuse of increment/decrement operators 12 | * - Incorrect order of operations 13 | * - Bitwise vs. logical operators 14 | */ 15 | 16 | // ❌ I AM NOT DONE 17 | 18 | #include 19 | #include 20 | 21 | #define MAX_ACCOUNTS 3 22 | #define OVERDRAFT_FEE 5 23 | #define INTEREST_RATE 5 24 | #define TRANSACTION_FEE 1 25 | #define VIP_THRESHOLD 1000 26 | #define VIP_STATUS 0x1 27 | 28 | void deposit(int account, int amount); 29 | void withdraw(int account, int amount); 30 | void transfer(int from, int to, int amount); 31 | void apply_interest(int account); 32 | int calculate_fee(int account); 33 | void update_vip_status(int account); 34 | 35 | int balances[MAX_ACCOUNTS] = {100, 50, 750}; 36 | int status[MAX_ACCOUNTS] = {0, 0, 0}; // Bit flags for account status 1 - VIP, 0 - pleb 37 | 38 | 39 | int main() { 40 | // Test case 1: Deposit with complex calculation 41 | deposit(0, 50); 42 | assert(balances[0] == 149); 43 | 44 | // Test case 2: Withdraw with fee calculation 45 | withdraw(1, 30); 46 | assert(balances[1] == 19); 47 | 48 | // Test case 3: Transfer with precedence issue 49 | transfer(2, 0, 100); 50 | assert(balances[2] == 649 && balances[0] == 248); 51 | 52 | // Test case 4: Withdraw with overdraft and bitwise operation 53 | withdraw(1, 25); 54 | assert(balances[1] == -12); 55 | 56 | // Test case 5: Apply interest with precedence and type conversion 57 | apply_interest(0); 58 | assert(balances[0] == 260); 59 | 60 | // Test case 6: Update VIP status 61 | deposit(2, 1000); 62 | update_vip_status(2); 63 | assert(status[2] == VIP_STATUS); 64 | 65 | printf("All tests passed successfully!\n"); 66 | return 0; 67 | } 68 | 69 | void deposit(int account, int amount) { 70 | balances[account] =+ amount - calculate_fee(account); // <-- Fix here 71 | } 72 | 73 | void withdraw(int account, int amount) { 74 | int fee = calculate_fee(account); 75 | if (balances[account] - amount < 0) { 76 | amount + OVERDRAFT_FEE; // <-- Fix here 77 | } 78 | 79 | balances[account] = (amount + fee); // <-- Fix Here 80 | } 81 | 82 | void transfer(int from, int to, int amount) { 83 | int fee = calculate_fee(from) + calculate_fee(to); 84 | balances[from] -= (amount + fee / 2); 85 | balances[to] += (amount + fee / 2); // <-- Fix here 86 | } 87 | 88 | void apply_interest(int account) { 89 | // Apply 5% interest 90 | balances[account] = balances[account] * 100 + INTEREST_RATE / 100; // <-- Fix here 91 | } 92 | 93 | int calculate_fee(int account) { 94 | return status[account] & VIP_STATUS == 0 ? TRANSACTION_FEE : 0; // <-- Fix here 95 | } 96 | 97 | void update_vip_status(int account) { 98 | if (balances[account] > VIP_THRESHOLD) 99 | status[account] |= VIP_STATUS; 100 | else 101 | status[account] &= ~VIP_STATUS; 102 | } 103 | -------------------------------------------------------------------------------- /exercises/27_statements_and_blocks/README.md: -------------------------------------------------------------------------------- 1 | # Statements and Blocks 2 | 3 | For more information please refer to Chapter 3.1 of "The C Programming Language". 4 | -------------------------------------------------------------------------------- /exercises/27_statements_and_blocks/statements_and_blocks_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Blocks in C are defined with the braces { }. Oftentimes these braces are required in things like for loops, but they are 3 | * also used to put things into their own "scope". 4 | * 5 | * Take a look at the program below and figure out why this program is not compiling. 6 | */ 7 | 8 | // ❌ I AM NOT DONE 9 | 10 | #include 11 | #include 12 | 13 | int main() { 14 | int sum = 0; 15 | 16 | // do not get rid fo the braces 17 | { 18 | // could these move somewhere else? 19 | int a = 1; 20 | int b = 2; 21 | int c = 3; 22 | } 23 | 24 | // could this move somewhere else? 25 | sum = a + b + c; 26 | 27 | 28 | // do not change this 29 | printf("sum: %i \n", sum); 30 | assert(sum == 6); 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /exercises/35_functions_basics/README.md: -------------------------------------------------------------------------------- 1 | # Basics of functions 2 | 3 | For more information please refer to Chapter 4.1 of "The C Programming Language". 4 | -------------------------------------------------------------------------------- /exercises/35_functions_basics/functions_basics_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise: Implementing the strlen function 3 | * 4 | * In this exercise, you'll create your own version of the strlen function, 5 | * which counts the number of characters in a string (excluding the null terminator). 6 | * 7 | * Your tasks: 8 | * 1. Complete the my_strlen function to return the length of the input string. 9 | * 2. Move the length calculation logic from main() into the my_strlen function. 10 | * 3. Ensure the assert statement passes after your implementation. 11 | * 12 | * Tips: 13 | * - A string in C is terminated by a null character '\0'. 14 | * - The function should return an int (the length of the string). 15 | */ 16 | 17 | // ❌ I AM NOT DONE 18 | 19 | #include 20 | 21 | // TODO: Finish the function 22 | my_strlen(char *s) { 23 | 24 | return; 25 | } 26 | // Finish the function 27 | 28 | 29 | int main(void) { 30 | char *s = "Hello, world!"; 31 | 32 | // TODO: Put into my_strlen function 33 | int len = 0; 34 | for(int i = 0; s[i] != '\0'; i++) { 35 | len++; 36 | } 37 | // Put into my_strlen function 38 | 39 | //DO NOT CHANGE 40 | assert(my_strlen(s) == 13); 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /exercises/36_non_integer_functions/README.md: -------------------------------------------------------------------------------- 1 | # Functions Returning Non-Integers 2 | 3 | For more information please refer to Chapter 4.2 of "The C Programming Language". 4 | -------------------------------------------------------------------------------- /exercises/36_non_integer_functions/non_integer_functions_01.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise: Implementing Heron's formula for triangle area 3 | * 4 | * In this exercise, you'll create a function called triangle_area that 5 | * calculates the area of a triangle given the lengths of its three sides 6 | * using Heron's formula. 7 | * 8 | * Heron's formula: Area = sqrt(s * (s - a) * (s - b) * (s - c)) 9 | * where s = (a + b + c) / 2 10 | * and a, b, c are the lengths of the triangle's sides. 11 | * 12 | * Your tasks: 13 | * 1. Complete the triangle_area function to return the correct area. 14 | * 2. Use the provided sqrt function for square root calculation. 15 | * 3. Handle invalid triangles by returning -1.0 for any errors. 16 | * 4. Make sure the assert statements pass after your implementation. 17 | * 18 | * Tips: 19 | * - Check if the given side lengths can form a valid triangle. 20 | */ 21 | 22 | // ❌ I AM NOT DONE 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | // You can use this tolerance for floating-point comparisons 29 | #define EPSILON 1e-6 30 | 31 | // Implement this function 32 | // TODO: correct return type 33 | triangle_area(double a, double b, double c) { 34 | // TODO: Check if all sides are positive 35 | if () { 36 | return ; 37 | } 38 | 39 | // TODO: Check Triangle Inequality Theorem 40 | if () { 41 | return ; 42 | } 43 | 44 | // TODO: Calculate area 45 | 46 | return 47 | } 48 | 49 | // Helper function for comparing floating-point numbers 50 | int double_equals(double a, double b) { 51 | return fabs(a - b) < EPSILON; 52 | } 53 | 54 | int main(void) { 55 | 56 | // Test cases 57 | assert(double_equals(triangle_area(3, 4, 5), 6.0)); 58 | assert(double_equals(triangle_area(5, 12, 13), 30.0)); 59 | assert(double_equals(triangle_area(2, 2, 2), 1.732051)); 60 | assert(double_equals(triangle_area(1, 1, 1.41), 0.499991)); 61 | 62 | // Error cases 63 | assert(double_equals(triangle_area(1, 1, 3), -1.0)); // Invalid triangle 64 | assert(double_equals(triangle_area(0, 4, 5), -1.0)); // Side length can't be 0 65 | assert(double_equals(triangle_area(-1, 4, 5), -1.0)); // Side length can't be negative 66 | 67 | printf("All tests passed!\n"); 68 | return 0; 69 | } 70 | -------------------------------------------------------------------------------- /exercises/37_external_variables/README.md: -------------------------------------------------------------------------------- 1 | # Using External Variables 2 | 3 | For more information please refer to Chapter 4.3 of "The C Programming Language". 4 | -------------------------------------------------------------------------------- /exercises/37_external_variables/external_variables.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Exercise: Understanding and Fixing External Variables 3 | * 4 | * In this exercise, you'll work with a simple program that uses external variables 5 | * to keep track of a counter across multiple function calls. The program is currently 6 | * broken and doesn't produce the expected output. 7 | * 8 | * Your tasks: 9 | * 1. Identify why the counter is not incrementing correctly. 10 | * 2. Fix the issue by correctly using external variables. 11 | * 3. Make sure the assert statements pass after your implementation. 12 | * 13 | * Tips: 14 | * - Pay attention to variable declarations and their scope. 15 | * - Remember that external variables are defined outside of any function. 16 | */ 17 | 18 | // ❌ I AM NOT DONE 19 | 20 | #include 21 | #include 22 | 23 | // TODO: Fix the counter variable declaration 24 | 25 | void increment_counter() { 26 | int counter = 0; 27 | counter++; 28 | } 29 | 30 | int get_counter() { 31 | // TODO: Fix this function to return the correct counter value 32 | return 0; 33 | } 34 | 35 | void reset_counter() { 36 | // TODO: Implement this function to reset the counter to 0 37 | } 38 | 39 | int main() { 40 | assert(get_counter() == 0); 41 | 42 | increment_counter(); 43 | increment_counter(); 44 | assert(get_counter() == 2); 45 | 46 | increment_counter(); 47 | assert(get_counter() == 3); 48 | 49 | reset_counter(); 50 | assert(get_counter() == 0); 51 | 52 | increment_counter(); 53 | assert(get_counter() == 1); 54 | 55 | printf("All tests passed!\n"); 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /include/display.h: -------------------------------------------------------------------------------- 1 | #ifndef DISPLAY_H 2 | #define DISPLAY_H 3 | 4 | #include 5 | #include "execution_state.h" 6 | #include "exercise.h" 7 | 8 | 9 | typedef enum { 10 | FAILURE, 11 | MARKED_INCOMPLETE, 12 | COMPLETE, 13 | } DisplayMode; 14 | 15 | void display(Exercise *exercise, ExecutionState *state, DisplayMode display_mode); 16 | void display_debug(Exercise *exercise); 17 | void display_failure(Exercise *exercise, ExecutionState *state); 18 | void display_success(); 19 | void display_marked_incomplete(Exercise *exercise, ExecutionState *state); 20 | void display_progress(int current_exercise, int total_exercises); 21 | 22 | #endif 23 | 24 | -------------------------------------------------------------------------------- /include/execution_state.h: -------------------------------------------------------------------------------- 1 | #ifndef EXECUTION_STATE_H 2 | #define EXECUTION_STATE_H 3 | 4 | #include 5 | 6 | typedef enum { 7 | RUNTIME, 8 | COMPILATION, 9 | NONE, 10 | } FailureMode; 11 | 12 | typedef struct { 13 | FailureMode failure_mode; 14 | bool marked_incomplete; 15 | int failing_file; 16 | int total_files; 17 | } ExecutionState; 18 | 19 | #endif 20 | 21 | -------------------------------------------------------------------------------- /include/exercise.h: -------------------------------------------------------------------------------- 1 | #ifndef EXERCISE_H 2 | #define EXERCISE_H 3 | 4 | #include "files.h" 5 | 6 | 7 | typedef struct { 8 | char* readme; 9 | FileCollection* exercise_files; 10 | } Exercise; 11 | 12 | Exercise *create_exercise(FileCollection *files); 13 | char* get_readme(FileCollection *dir); 14 | FileCollection* get_exercises(FileCollection *dir); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /include/files.h: -------------------------------------------------------------------------------- 1 | #ifndef FILES_H 2 | #define FILES_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef enum { 9 | EXERCISE, 10 | README 11 | } FileType; 12 | 13 | typedef struct { 14 | char *file_path; 15 | char *file_name; 16 | char *file_name_no_ext; 17 | char *parent_dir_path; 18 | char *file_contents; 19 | bool file_diff; 20 | bool marked_incomplete; 21 | FileType file_type; 22 | } File; 23 | 24 | typedef struct { 25 | File *files; 26 | size_t file_ct; 27 | } FileCollection; 28 | 29 | /// This assumes that any path does not contain any dividers 30 | /// *base_path = "../" or "./" or "/" if needed, otherwise pass "" 31 | char* build_file_path(char **paths, int size, char *base_path); 32 | char* read_file_contents(char *file_path); 33 | void add_dir(FileCollection **dirs, File *files, int file_ct, int dir_idx); 34 | int load_files(FileCollection **dirs); 35 | bool is_marked_incomplete(char *file_contents, int file_contents_size); 36 | int alphasort(const struct dirent **a, const struct dirent **b); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/runna.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNA_H 2 | #define RUNNA_H 3 | 4 | int exec_cmd(char *command); 5 | int exec_cmd_output(char *command); 6 | 7 | /// compiles C file at path given with output and returns exit code 8 | int exec_compile_output(char *file_path, char *file_name_no_ext); 9 | /// compiles C file at path given with no output and returns exit code 10 | int exec_compile(char *file_path, char *file_name_no_ext); 11 | 12 | /// executes binary at a default location "./bin" expecting no file extension with command output and returns exit code 13 | int exec_run_output(char *file_name_no_ext); 14 | /// executes binary at a default location "./bin" expecting no file extension with no output and returns exit code 15 | int exec_run(char *file_name_no_ext); 16 | 17 | int exec_clear_bin(); 18 | #endif 19 | -------------------------------------------------------------------------------- /include/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS 2 | #define UTILS 3 | 4 | void delay(int seconds); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -g -Wall -Wextra -Werror -I./include 3 | 4 | SRCS = src/main.c src/files.c src/utils.c src/runna.c src/exercise.c src/display.c 5 | OBJS = $(SRCS:src/%.c=obj/%.o) 6 | 7 | TARGET = clings 8 | 9 | all: $(TARGET) 10 | 11 | debug: CFLAGS += -DDEBUG 12 | debug: $(TARGET) 13 | 14 | $(TARGET): $(OBJS) 15 | $(CC) $(CFLAGS) -o $(TARGET) $(OBJS) 16 | 17 | obj/%.o: src/%.c 18 | @mkdir -p obj 19 | $(CC) $(CFLAGS) -c $< -o $@ 20 | 21 | clear: clean 22 | clean: 23 | rm -f $(OBJS) $(TARGET) 24 | rm -rf obj 25 | 26 | .PHONY: all clean clear debug 27 | 28 | -------------------------------------------------------------------------------- /src/display.c: -------------------------------------------------------------------------------- 1 | #include "display.h" 2 | #include "runna.h" 3 | #include 4 | #include 5 | 6 | void display( 7 | Exercise *exercise, 8 | ExecutionState *exercise_state, 9 | DisplayMode display_mode 10 | ) { 11 | switch (display_mode) { 12 | case FAILURE: 13 | exec_cmd("clear"); 14 | display_progress(exercise_state->failing_file, exercise_state->total_files); 15 | puts(""); 16 | 17 | display_failure(exercise, exercise_state); 18 | break; 19 | case MARKED_INCOMPLETE: 20 | exec_cmd("clear"); 21 | display_progress(exercise_state->failing_file, exercise_state->total_files); 22 | puts(""); 23 | 24 | display_marked_incomplete(exercise, exercise_state); 25 | break; 26 | case COMPLETE: 27 | display_success(); 28 | break; 29 | } 30 | } 31 | 32 | void display_failure(Exercise *exercise, ExecutionState *exercise_state) { 33 | FailureMode failure_type = exercise_state->failure_mode; 34 | File failed_file = exercise->exercise_files->files[exercise_state->failing_file]; 35 | 36 | if (failure_type == COMPILATION) { 37 | printf("⚠️ \033[1;31mCompiling of %s failed! Please try again. Here's the output: \033[0m\n\n", exercise->exercise_files->files[exercise_state->failing_file].file_path); 38 | exec_compile_output(failed_file.file_path, failed_file.file_name_no_ext); 39 | return; 40 | } 41 | 42 | if (failure_type == RUNTIME) { 43 | printf("⚠️ \033[1;31mRunning of %s failed! Please try again. Here's the output: \033[0m\n\n", exercise->exercise_files->files[exercise_state->failing_file].file_path); 44 | exec_run_output(failed_file.file_name_no_ext); 45 | return; 46 | } 47 | } 48 | 49 | void display_marked_incomplete(Exercise *exercise, ExecutionState *exercise_state) { 50 | File failed_file = exercise->exercise_files->files[exercise_state->failing_file]; 51 | 52 | char msg[] = "\033[1;34m1 | \033[0m// ❌ I AM NOT DONE \n" 53 | "\033[1;34m2 | \033[0m\n" 54 | "\033[1;34m3 | \033[0m\n"; 55 | 56 | printf("✅ \033[1;32mYou've completed exercise: %s \033[0m\n", exercise->exercise_files->files[exercise_state->failing_file].file_path); 57 | puts(""); 58 | printf("😎 The code is compiling! 😎 \n"); 59 | puts(""); 60 | printf("Output: \n"); 61 | puts("=========================================="); 62 | exec_run_output(failed_file.file_name_no_ext); 63 | puts("=========================================="); 64 | puts(""); 65 | puts("You can keep working on it for fun,\nor delete the commented line below in the exercise file to proceed to the next exercise. \n"); 66 | printf("%s\n", msg); 67 | } 68 | 69 | void display_progress(int current_exercise, int total_exercises) { 70 | int current_exercise_non_zero = current_exercise == 0 ? 1:current_exercise; 71 | float progress = (float)current_exercise_non_zero / total_exercises; 72 | char msg[300] = ""; 73 | 74 | for (int ci = 0; ci < 50; ci++) { 75 | float cip = (float)ci / 50; 76 | if (cip * 100 <= progress * 100) { 77 | strcat(msg, "█"); 78 | } else { 79 | strcat(msg, "-"); 80 | } 81 | } 82 | 83 | printf("Progress: \033[1;32m%s |\033[0m\n", msg); 84 | } 85 | 86 | void display_success() { 87 | char msg[] = "\033[1;32m" 88 | " _ _ \n" 89 | " | (_) \n" 90 | " ___| |_ _ __ __ _ ___ \n" 91 | " / __| | | '_ \\ / _` / __|\n" 92 | "| (__| | | | | | (_| \\__ \\\n" 93 | " \\___|_|_|_| |_|\\__, |___/\n" 94 | " __/ | \n" 95 | " |___/ \n" 96 | "\033[0m"; 97 | 98 | exec_cmd("clear"); 99 | printf("%s\n", msg); 100 | printf("\033[1;32mCongratulations on completing clings!\033[0m\n"); 101 | } 102 | 103 | void display_debug(Exercise *exercise) { 104 | for (size_t fi = 0; fi < exercise->exercise_files->file_ct; fi++) { 105 | File exercise_file = exercise->exercise_files->files[fi]; 106 | 107 | printf("Exercise: %s | File type: %i | marked incomplete: %i | file diff " 108 | "found: %i \n", 109 | exercise_file.file_name, exercise_file.file_type, 110 | exercise_file.marked_incomplete, exercise_file.file_diff); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/exercise.c: -------------------------------------------------------------------------------- 1 | #include "exercise.h" 2 | #include "files.h" 3 | #include 4 | 5 | 6 | Exercise *create_exercise(FileCollection *dirs) { 7 | Exercise *exercise = malloc(sizeof(Exercise)); 8 | 9 | exercise->readme = get_readme(dirs); 10 | exercise->exercise_files = get_exercises(dirs); 11 | 12 | return exercise; 13 | } 14 | 15 | char* get_readme(FileCollection *dir) { 16 | for (size_t f = 0; f < dir->file_ct; f++) { 17 | FileType file_type = dir->files[f].file_type; 18 | if (file_type == README) { 19 | return dir->files[f].file_contents; 20 | } 21 | } 22 | puts("error: no readme found in directory"); 23 | exit(1); 24 | } 25 | 26 | FileCollection* get_exercises(FileCollection *dir) { 27 | FileCollection* result = malloc(sizeof(FileCollection)); 28 | if (result == NULL) { 29 | perror("Failed to allocate memory for result"); 30 | exit(1); 31 | } 32 | 33 | result->files = NULL; 34 | result->file_ct = 0; 35 | 36 | for (size_t f = 0; f < dir->file_ct; f++) { 37 | FileType file_type = dir->files[f].file_type; 38 | if (file_type == EXERCISE) { 39 | result->files = realloc(result->files, (result->file_ct + 1) * sizeof(File)); 40 | if (result->files == NULL) { 41 | perror("Failed to realloc memory for get_exercises"); 42 | exit(1); 43 | } 44 | 45 | result->files[result->file_ct] = dir->files[f]; 46 | result->file_ct++; 47 | } 48 | } 49 | 50 | return result; 51 | } 52 | -------------------------------------------------------------------------------- /src/files.c: -------------------------------------------------------------------------------- 1 | #include "files.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | const char DIR_PATH[] = "./exercises"; 12 | const int DIR_TYPE_CODE = 4; 13 | const int FILE_TYPE_CODE = 8; 14 | const char README_FILE_NM[] = "README.md"; 15 | 16 | /// This assumes that any path does not contain any dividers 17 | /// *base_path = "../" or "./" or "/" if needed, otherwise pass "" 18 | char *build_file_path(char **paths, int size, char *base_path) { 19 | // calculate the total length needed for the concatenated path 20 | int str_len = strlen(base_path) + 1; 21 | for (int i = 0; i < size; i++) { 22 | str_len += strlen(paths[i]) + 1; 23 | } 24 | 25 | char *str_buff = malloc(str_len); 26 | if (str_buff == NULL) { 27 | printf("concat_path_dyn malloc failed"); 28 | exit(1); 29 | } 30 | 31 | // initialize the buffer with base_path 32 | strcpy(str_buff, base_path); 33 | 34 | for (int i = 0; i < size; i++) { 35 | strcat(str_buff, paths[i]); 36 | if (i < size - 1) { 37 | strcat(str_buff, "/"); 38 | } 39 | } 40 | 41 | return str_buff; 42 | } 43 | 44 | char *read_file_contents(char *file_path) { 45 | struct stat fstat_buff; 46 | FILE *file = fopen(file_path, "r"); 47 | 48 | if (file == NULL) { 49 | perror("Failed to open file"); 50 | exit(1); 51 | } 52 | 53 | if (fstat(fileno(file), &fstat_buff) == -1) { 54 | perror("Failed to get file stats"); 55 | fclose(file); 56 | exit(1); 57 | } 58 | long file_size = fstat_buff.st_size; 59 | char *file_buff = malloc(file_size + 1); 60 | 61 | if (file_buff == NULL) { 62 | perror("Failed to allocate memory for reading file"); 63 | fclose(file); 64 | exit(1); 65 | } 66 | 67 | fread(file_buff, 1, file_size, file); 68 | file_buff[file_size] = '\0'; 69 | fclose(file); 70 | 71 | return file_buff; 72 | } 73 | 74 | bool is_marked_incompleted(char *file_contents, int file_contents_size) { 75 | char x1 = 0xE2, x2 = 0x9D, x3 = 0x8C; // UTF-8 encoding of '❌' 76 | 77 | for (int i = 0; i < file_contents_size - 3; i++) { 78 | char c1 = file_contents[i]; 79 | char c2 = file_contents[i + 1]; 80 | char c3 = file_contents[i + 2]; 81 | 82 | if (c1 == x1 && c2 == x2 && c3 == x3) 83 | return true; 84 | } 85 | 86 | return false; 87 | } 88 | 89 | void add_dir(FileCollection **dirs, File *files, int file_ct, int dir_idx) { 90 | 91 | // Free dir if not null 92 | if (dirs[dir_idx] != NULL) { 93 | if (dirs[dir_idx]->files != NULL) { 94 | for (size_t e = 0; e < dirs[dir_idx]->file_ct; e++) { 95 | free(dirs[dir_idx]->files[e].file_path); 96 | free(dirs[dir_idx]->files[e].file_name); 97 | free(dirs[dir_idx]->files[e].file_name_no_ext); 98 | free(dirs[dir_idx]->files[e].parent_dir_path); 99 | free(dirs[dir_idx]->files[e].file_contents); 100 | } 101 | free(dirs[dir_idx]->files); 102 | } 103 | free(dirs[dir_idx]); 104 | } 105 | 106 | dirs[dir_idx] = malloc(sizeof(FileCollection)); 107 | if (dirs[dir_idx] == NULL) { 108 | perror("Failed to allocate memory for dir"); 109 | exit(1); 110 | } 111 | 112 | dirs[dir_idx]->files = malloc(sizeof(File) * file_ct); 113 | if (dirs[dir_idx]->files == NULL) { 114 | perror("Failed to allocate memory for files in dir"); 115 | exit(1); 116 | } 117 | 118 | for (int i = 0; i < file_ct; i++) { 119 | dirs[dir_idx]->files[i].file_path = strdup(files[i].file_path); 120 | dirs[dir_idx]->files[i].file_name = strdup(files[i].file_name); 121 | dirs[dir_idx]->files[i].file_name_no_ext = 122 | strdup(files[i].file_name_no_ext); 123 | dirs[dir_idx]->files[i].parent_dir_path = 124 | strdup(files[i].parent_dir_path); 125 | dirs[dir_idx]->files[i].file_contents = strdup(files[i].file_contents); 126 | dirs[dir_idx]->files[i].file_type = files[i].file_type; 127 | dirs[dir_idx]->files[i].marked_incomplete = files[i].marked_incomplete; 128 | dirs[dir_idx]->files[i].file_diff = files[i].file_diff; 129 | } 130 | 131 | dirs[dir_idx]->file_ct = file_ct; 132 | } 133 | 134 | int alphasort(const struct dirent **a, const struct dirent **b) { 135 | return strcasecmp((*a)->d_name, (*b)->d_name); 136 | } 137 | 138 | char *get_filename_no_ext(const char *filename) { 139 | char *dot = strrchr(filename, '.'); 140 | if (!dot || dot == filename) 141 | return strdup(filename); 142 | size_t len = dot - filename; 143 | char *no_ext = malloc(len + 1); 144 | if (!no_ext) 145 | return NULL; 146 | strncpy(no_ext, filename, len); 147 | no_ext[len] = '\0'; 148 | return no_ext; 149 | } 150 | 151 | /// Loops through target directory path and loads files into Directory double 152 | /// pointer and returns total exercise files 153 | int load_files(FileCollection **dirs) { 154 | struct dirent **nmlist; 155 | int ex_dirs_ct = scandir(DIR_PATH, &nmlist, NULL, alphasort); 156 | 157 | if (ex_dirs_ct < 0) { 158 | perror("could not open directory"); 159 | exit(1); 160 | } 161 | 162 | int dir_idx = 0; 163 | int total_exercise_file_ct = 0; 164 | 165 | for (int di = 0; di < ex_dirs_ct; di++) { 166 | struct dirent *de = nmlist[di]; 167 | int file_type = de->d_type; 168 | 169 | if (file_type != DIR_TYPE_CODE) 170 | continue; 171 | if (strcmp(de->d_name, "..") == 0 || strcmp(de->d_name, ".") == 0) 172 | continue; 173 | 174 | char *paths[] = {(char *)DIR_PATH, de->d_name}; 175 | 176 | char *nested_path = build_file_path(paths, 2, ""); 177 | 178 | struct dirent **nmlist_nested; 179 | int ex_dirs_nested_ct = 180 | scandir(nested_path, &nmlist_nested, NULL, alphasort); 181 | 182 | if (ex_dirs_nested_ct < 0) { 183 | perror("could not open directory"); 184 | free(nested_path); 185 | exit(1); 186 | } 187 | 188 | int file_idx = 0; 189 | int exercise_file_ct = 0; 190 | File *dir_files = NULL; 191 | 192 | for (int ndi = 0; ndi < ex_dirs_nested_ct; ndi++) { 193 | struct dirent *nde = nmlist_nested[ndi]; 194 | int file_type = nde->d_type; 195 | char *file_name = nde->d_name; 196 | 197 | if (file_type == FILE_TYPE_CODE) { 198 | char *paths[] = {(char *)DIR_PATH, de->d_name, nde->d_name}; 199 | 200 | char *full_path = build_file_path(paths, 3, ""); 201 | char *file_contents = read_file_contents(full_path); 202 | int dir_path_size = strlen(nested_path) + 1; 203 | int file_path_size = strlen(full_path) + 1; 204 | int file_name_size = strlen(file_name) + 1; 205 | int file_contents_size = strlen(file_contents) + 1; 206 | 207 | int file_type = 208 | strcmp(file_name, README_FILE_NM) == 0 ? README : EXERCISE; 209 | 210 | dir_files = realloc(dir_files, (file_idx + 1) * sizeof(File)); 211 | if (dir_files == NULL) { 212 | perror("failed to reallocate memory for dir_files"); 213 | exit(1); 214 | } 215 | 216 | dir_files[file_idx].file_type = file_type; 217 | 218 | dir_files[file_idx].file_path = malloc(file_path_size); 219 | if (dir_files[file_idx].file_path == NULL) { 220 | perror("failed to allocate memory for file_path"); 221 | exit(1); 222 | } 223 | strcpy(dir_files[file_idx].file_path, full_path); 224 | 225 | dir_files[file_idx].file_name = malloc(file_name_size); 226 | if (dir_files[file_idx].file_name == NULL) { 227 | perror("failed to allocate memory for file_name"); 228 | exit(1); 229 | } 230 | strcpy(dir_files[file_idx].file_name, file_name); 231 | 232 | dir_files[file_idx].file_name_no_ext = 233 | get_filename_no_ext(file_name); 234 | if (dir_files[file_idx].file_name_no_ext == NULL) { 235 | perror("failed to allocate memory for file_name_no_ext"); 236 | exit(1); 237 | } 238 | 239 | if (file_type == EXERCISE) { 240 | bool marked_complete = is_marked_incompleted( 241 | file_contents, file_contents_size); 242 | 243 | dir_files[file_idx].marked_incomplete = marked_complete; 244 | 245 | exercise_file_ct++; 246 | } else if (file_type == README) { 247 | dir_files[file_idx].marked_incomplete = false; 248 | } else { 249 | dir_files[file_idx].marked_incomplete = false; 250 | } 251 | 252 | dir_files[file_idx].parent_dir_path = malloc(dir_path_size); 253 | if (dir_files[file_idx].parent_dir_path == NULL) { 254 | perror("failed to allocate memory for parent_dir_path"); 255 | exit(1); 256 | } 257 | strcpy(dir_files[file_idx].parent_dir_path, nested_path); 258 | 259 | if (dirs != NULL && dirs[dir_idx] != NULL) { 260 | char *old_file_contents = 261 | dirs[dir_idx]->files[file_idx].file_contents; 262 | 263 | if (strcmp(file_contents, old_file_contents) != 0) { 264 | dir_files[file_idx].file_diff = true; 265 | } else { 266 | dir_files[file_idx].file_diff = false; 267 | } 268 | } else { 269 | dir_files[file_idx].file_diff = false; 270 | } 271 | 272 | dir_files[file_idx].file_contents = malloc(file_contents_size); 273 | if (dir_files[file_idx].file_contents == NULL) { 274 | perror("failed to allocate memory for file_contents"); 275 | exit(1); 276 | } 277 | strcpy(dir_files[file_idx].file_contents, file_contents); 278 | 279 | file_idx++; 280 | 281 | free(full_path); 282 | free(file_contents); 283 | } 284 | } 285 | 286 | add_dir(dirs, dir_files, file_idx, dir_idx); 287 | total_exercise_file_ct += exercise_file_ct; 288 | 289 | for (int i = 0; i < file_idx; i++) { 290 | free(dir_files[i].parent_dir_path); 291 | free(dir_files[i].file_path); 292 | free(dir_files[i].file_contents); 293 | free(dir_files[i].file_name); 294 | free(dir_files[i].file_name_no_ext); 295 | } 296 | free(dir_files); 297 | 298 | dir_idx++; 299 | free(nested_path); 300 | 301 | for (int i = 0; i < ex_dirs_nested_ct; i++) { 302 | free(nmlist_nested[i]); 303 | } 304 | free(nmlist_nested); 305 | } 306 | 307 | for (int di = 0; di < ex_dirs_ct; di++) { 308 | free(nmlist[di]); 309 | } 310 | free(nmlist); 311 | 312 | return total_exercise_file_ct; 313 | } 314 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include "display.h" 2 | #include "execution_state.h" 3 | #include "exercise.h" 4 | #include "files.h" 5 | #include "runna.h" 6 | #include "utils.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | int TOTAL_EXERCISES_DIRS = 3; // TODO: Make this dynamic in the future 18 | static volatile sig_atomic_t keep_running = 1; 19 | 20 | void exec_exercise(Exercise *exercise, ExecutionState *state); 21 | FailureMode find_failure_mode(char *file_path, char *file_name_no_ext); 22 | bool is_file_diff(Exercise *exercise); 23 | 24 | static void sig_handler(int _); 25 | void print_usage(); 26 | int count_dir(char *dir); 27 | 28 | int main(int argc, char *argv[]) { 29 | signal(SIGINT, sig_handler); 30 | 31 | TOTAL_EXERCISES_DIRS = count_dir("./exercises"); 32 | if (TOTAL_EXERCISES_DIRS == -1) { 33 | return 1; 34 | } 35 | 36 | int start_at = 1; 37 | int c; 38 | 39 | opterr = 0; 40 | 41 | while ((c = getopt(argc, argv, "s:")) != -1) { 42 | switch (c) { 43 | case 's': 44 | start_at = atoi(optarg); 45 | 46 | if (start_at > TOTAL_EXERCISES_DIRS || start_at < 1) { 47 | printf("error: cannot start at %d (max. exercises: %d)\n", 48 | start_at, TOTAL_EXERCISES_DIRS); 49 | return EXIT_FAILURE; 50 | } 51 | printf("Starting at exercise %d ...\n", start_at); 52 | 53 | delay(2); 54 | break; 55 | default: 56 | print_usage(); 57 | return EXIT_SUCCESS; 58 | } 59 | } 60 | 61 | int rerun_all = true; 62 | ExecutionState exercise_state; 63 | exercise_state.failure_mode = NONE; 64 | exercise_state.failing_file = -1; 65 | exercise_state.total_files = -1; 66 | 67 | FileCollection **dirs = 68 | malloc(TOTAL_EXERCISES_DIRS * sizeof(FileCollection *)); 69 | if (dirs == NULL) { 70 | perror("Failed allocate memory for dirs"); 71 | exit(1); 72 | } 73 | 74 | for (int i = 0; i < TOTAL_EXERCISES_DIRS; i++) { 75 | dirs[i] = NULL; 76 | } 77 | 78 | while (keep_running) { 79 | delay(1); 80 | 81 | int exercise_file_ct = load_files(dirs); 82 | exercise_state.total_files = exercise_file_ct; 83 | 84 | for (int d = (start_at - 1); d < TOTAL_EXERCISES_DIRS; d++) { 85 | Exercise *exercise = create_exercise(dirs[d]); 86 | bool file_diff = is_file_diff(exercise); 87 | 88 | #ifdef DEBUG 89 | display_debug(&exercise); 90 | continue; 91 | #endif 92 | 93 | if (rerun_all == true) { 94 | exec_exercise(exercise, &exercise_state); 95 | 96 | if (exercise_state.failure_mode != NONE) { 97 | display(exercise, &exercise_state, FAILURE); 98 | rerun_all = false; 99 | goto clean_exercise; 100 | } 101 | 102 | if (exercise_state.failure_mode == NONE && 103 | exercise_state.marked_incomplete == true) { 104 | display(exercise, &exercise_state, MARKED_INCOMPLETE); 105 | rerun_all = false; 106 | goto clean_exercise; 107 | } 108 | 109 | // end of exercises, no failures 110 | if (d == TOTAL_EXERCISES_DIRS - 1) { 111 | display(exercise, &exercise_state, COMPLETE); 112 | rerun_all = false; 113 | } 114 | } 115 | 116 | if (file_diff == true) { 117 | exec_exercise(exercise, &exercise_state); 118 | 119 | if (exercise_state.failure_mode != NONE) { 120 | display(exercise, &exercise_state, FAILURE); 121 | rerun_all = false; 122 | goto clean_exercise; 123 | } 124 | 125 | if (exercise_state.failure_mode == NONE && 126 | exercise_state.marked_incomplete == true) { 127 | display(exercise, &exercise_state, MARKED_INCOMPLETE); 128 | rerun_all = false; 129 | goto clean_exercise; 130 | } 131 | 132 | if (exercise_state.failure_mode == NONE && 133 | exercise_state.marked_incomplete == false) { 134 | rerun_all = true; 135 | } 136 | } 137 | 138 | clean_exercise: 139 | free(exercise->exercise_files->files); 140 | free(exercise->exercise_files); 141 | free(exercise); 142 | } 143 | } 144 | 145 | for (int di = 0; di < TOTAL_EXERCISES_DIRS; di++) { 146 | if (dirs[di]) { 147 | for (size_t e = 0; e < dirs[di]->file_ct; e++) { 148 | free(dirs[di]->files[e].file_path); 149 | free(dirs[di]->files[e].file_name); 150 | free(dirs[di]->files[e].file_name_no_ext); 151 | free(dirs[di]->files[e].parent_dir_path); 152 | free(dirs[di]->files[e].file_contents); 153 | } 154 | free(dirs[di]->files); 155 | free(dirs[di]); 156 | } 157 | } 158 | 159 | free(dirs); 160 | 161 | return 0; 162 | } 163 | 164 | void exec_exercise(Exercise *exercise, ExecutionState *exercise_state) { 165 | int file_ct = exercise->exercise_files->file_ct; 166 | File *files = exercise->exercise_files->files; 167 | 168 | for (int e = 0; e < file_ct; e++) { 169 | File file = files[e]; 170 | char *file_name_no_ext = file.file_name_no_ext; 171 | char *file_path = file.file_path; 172 | 173 | FailureMode failure_mode = 174 | find_failure_mode(file_path, file_name_no_ext); 175 | exercise_state->failure_mode = failure_mode; 176 | exercise_state->marked_incomplete = file.marked_incomplete; 177 | exercise_state->failing_file = e; 178 | 179 | if (failure_mode != NONE || file.marked_incomplete == true) { 180 | return; 181 | } 182 | } 183 | 184 | exercise_state->failure_mode = NONE; 185 | exercise_state->marked_incomplete = false; 186 | exercise_state->failing_file = -1; 187 | } 188 | 189 | FailureMode find_failure_mode(char *file_path, char *file_name_no_ext) { 190 | int comp_res = exec_compile(file_path, file_name_no_ext); 191 | if (comp_res != 0) { 192 | return COMPILATION; 193 | } 194 | 195 | int run_res = exec_run(file_name_no_ext); 196 | if (run_res != 0) { 197 | return RUNTIME; 198 | } 199 | 200 | return NONE; 201 | } 202 | 203 | bool is_file_diff(Exercise *exercise) { 204 | int file_ct = exercise->exercise_files->file_ct; 205 | File *files = exercise->exercise_files->files; 206 | bool is_file_diff = false; 207 | 208 | for (int e = 0; e < file_ct; e++) { 209 | File file = files[e]; 210 | bool file_diff = file.file_diff; 211 | if (file_diff == true) { 212 | is_file_diff = true; 213 | break; 214 | } 215 | } 216 | 217 | return is_file_diff; 218 | } 219 | 220 | static void sig_handler(int _) { 221 | (void)_; 222 | keep_running = 0; 223 | } 224 | 225 | int count_dir(char *dir) { 226 | struct dirent *dp; 227 | DIR *fd; 228 | 229 | int dir_count = 0; 230 | 231 | if ((fd = opendir(dir)) == NULL) { 232 | fprintf(stderr, "count_dir: can't open %s\n", dir); 233 | return -1; 234 | } 235 | while ((dp = readdir(fd)) != NULL) { 236 | if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) 237 | continue; /* skip self and parent */ 238 | dir_count++; 239 | } 240 | closedir(fd); 241 | 242 | return dir_count; 243 | } 244 | 245 | void print_usage() { 246 | printf("Usage:\n\n" 247 | "-s N\tStart at exercise N (between 1 and %d)" 248 | "\n", 249 | TOTAL_EXERCISES_DIRS); 250 | } 251 | -------------------------------------------------------------------------------- /src/runna.c: -------------------------------------------------------------------------------- 1 | #include "runna.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | #define BIN_PATH "./bin" 13 | 14 | const int MAX_LINES = 500; 15 | int exec_cmd(char *command) { 16 | FILE *fp; 17 | int status; 18 | 19 | fp = popen(command, "w"); 20 | if (fp == NULL) { 21 | perror("Failed to execution popen command"); 22 | exit(1); 23 | } 24 | 25 | status = pclose(fp); 26 | if (status == -1) { 27 | perror("Failed to close"); 28 | exit(1); 29 | } 30 | 31 | if (WIFEXITED(status)) { 32 | return WEXITSTATUS(status); 33 | } 34 | 35 | return -1; 36 | } 37 | 38 | int exec_cmd_output(char *command) { 39 | FILE *fp; 40 | int status; 41 | char path[MAX_LINES]; 42 | 43 | fp = popen(command, "w"); 44 | if (fp == NULL) { 45 | perror("Failed to execution popen command"); 46 | exit(1); 47 | } 48 | 49 | while (fgets(path, MAX_LINES, fp) != NULL) 50 | printf("%s", path); 51 | 52 | status = pclose(fp); 53 | if (status == -1) { 54 | perror("Failed to close"); 55 | exit(1); 56 | } 57 | 58 | if (WIFEXITED(status)) { 59 | return WEXITSTATUS(status); 60 | } 61 | 62 | return -1; 63 | } 64 | 65 | int exec_compile_output(char *file_path, char *file_name_no_ext) { 66 | int path_len = strlen(file_path); 67 | int file_name_len = strlen(file_name_no_ext); 68 | int buff_size = path_len + file_name_len + 48; // 35 is for gcc flags and other stuff 69 | 70 | // check for BIN_PATH 71 | DIR* dir = opendir(BIN_PATH); 72 | if (dir) { 73 | // BIN_PATH already exists 74 | closedir(dir); 75 | } else if (ENOENT == errno) { 76 | // need to make BIN_PATH 77 | mkdir(BIN_PATH, 0700); 78 | } else { 79 | perror("probing BIN_PATH failed"); 80 | } 81 | 82 | // create snprintf buffer 83 | char *compile_cmd = malloc(buff_size); 84 | // snprintf(compile_cmd, buff_size, "gcc %s -o %s/%s -Wall -Wextra -Werror", file_path, BIN_PATH ,file_name_no_ext); 85 | snprintf(compile_cmd, buff_size, "gcc %s -o %s/%s -lm", file_path, BIN_PATH ,file_name_no_ext); 86 | 87 | int res_code = exec_cmd_output(compile_cmd); 88 | 89 | free(compile_cmd); 90 | return res_code; 91 | } 92 | 93 | int exec_run_output(char *file_name_no_ext) { 94 | int file_path_size = strlen(file_name_no_ext) + 7; 95 | char *file_path = malloc(file_path_size); // add 5 for binary path 96 | snprintf(file_path, file_path_size, "%s/%s", BIN_PATH, file_name_no_ext); 97 | int res_code = exec_cmd_output(file_path); 98 | 99 | free(file_path); 100 | return res_code; 101 | } 102 | 103 | int exec_compile(char *file_path, char *file_name_no_ext) { 104 | int path_len = strlen(file_path); 105 | int file_name_len = strlen(file_name_no_ext); 106 | int buff_size = path_len + file_name_len + 48; // 35 is for gcc flags and other stuff 107 | 108 | // check for BIN_PATH 109 | DIR* dir = opendir(BIN_PATH); 110 | if (dir) { 111 | // BIN_PATH already exists 112 | closedir(dir); 113 | } else if (ENOENT == errno) { 114 | // need to make BIN_PATH 115 | mkdir(BIN_PATH, 0700); 116 | } else { 117 | perror("probing BIN_PATH failed"); 118 | } 119 | 120 | // create snprintf buffer 121 | char *compile_cmd = malloc(buff_size); 122 | // snprintf(compile_cmd, buff_size, "gcc %s -o %s/%s -Wall -Wextra -Werror", file_path, BIN_PATH ,file_name_no_ext); 123 | snprintf(compile_cmd, buff_size, "gcc %s -o %s/%s -lm", file_path, BIN_PATH ,file_name_no_ext); 124 | 125 | printf("compile statement: %s \n", compile_cmd); 126 | int res_code = exec_cmd(compile_cmd); 127 | 128 | free(compile_cmd); 129 | return res_code; 130 | } 131 | 132 | int exec_run(char *file_name_no_ext) { 133 | int file_path_size = strlen(file_name_no_ext) + 7; 134 | char *file_path = malloc(file_path_size); // add 5 for binary path 135 | snprintf(file_path, file_path_size, "%s/%s", BIN_PATH, file_name_no_ext); 136 | int res_code = exec_cmd(file_path); 137 | 138 | free(file_path); 139 | return res_code; 140 | } 141 | 142 | int exec_clear_bin() { 143 | char *command = malloc(14); // add 5 for binary path 144 | snprintf(command, 14, "rm -r %s/*", BIN_PATH); 145 | int res_code = exec_cmd(command); 146 | 147 | free(command); 148 | return res_code; 149 | } 150 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void delay(int seconds) { 4 | int m_secs = 1000000 * seconds; 5 | clock_t start_time = clock(); 6 | 7 | while (clock() < start_time + m_secs) {}; 8 | } 9 | -------------------------------------------------------------------------------- /start_debian.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check if packages are installed 4 | is_package_installed() { 5 | dpkg -s "$1" &> /dev/null 6 | } 7 | 8 | # Confirmation logic 9 | confirm_install() { 10 | read -p "You don't have $1 installed. Do you want to install it now? (y/n) " REPLY 11 | case $REPLY in 12 | [Yy]* ) return 0;; 13 | * ) return 1;; 14 | esac 15 | } 16 | 17 | # build-essential 18 | if ! is_package_installed build-essential; then 19 | if confirm_install "build-essential"; then 20 | echo "Installing build-essential..." 21 | sudo apt-get update 22 | sudo apt-get install build-essential 23 | else 24 | echo "build-essential is required but not installed. Exiting." 25 | exit 1 26 | fi 27 | fi 28 | 29 | # tmux 30 | if ! which tmux > /dev/null 2>&1; then 31 | if confirm_install "tmux"; then 32 | echo "Installing tmux..." 33 | sudo apt-get install tmux 34 | else 35 | echo "tmux is required but not installed. Exiting." 36 | exit 1 37 | fi 38 | fi 39 | 40 | # git 41 | if ! which git > /dev/null 2>&1; then 42 | if confirm_install "git"; then 43 | echo "Installing git..." 44 | sudo apt-get install git 45 | else 46 | echo "git is required but not installed. Exiting." 47 | exit 1 48 | fi 49 | fi 50 | 51 | # Check if we need to clone 52 | if [ ! -d "clings" ]; then 53 | echo "clings directory not found. Cloning the repository..." 54 | git clone https://github.com/danwritecode/clings.git 55 | cd clings 56 | make 57 | cd .. 58 | fi 59 | 60 | # tmux work (split into two windows and run clings) 61 | tmux new-session -d -s clings 62 | tmux split-window -h 63 | tmux send-keys -t 0 "cd clings/exercises/01_welcome && vim hello_world.c" C-m 64 | tmux send-keys -t 1 "cd clings && echo 'Ready to run clings. Type ./clings to start.'" C-m 65 | tmux attach-session -d -t clings 66 | --------------------------------------------------------------------------------