diff --git a/chapters/chapter_0.c b/chapters/chapter_0.c index cc7d7fc..2db04a3 100644 --- a/chapters/chapter_0.c +++ b/chapters/chapter_0.c @@ -132,4 +132,96 @@ int string_length (char * string) { return (length); } +int string_compare (char * string_0, char * string_1) { + if (string_length (string_0) != string_length (string_1)) { + return (0); + } else { + return (string_compare_limit (string_0, string_1, string_length (string_0))); + } +} + +char * string_copy (char * string_0, char * string_1) { + int i = 0; + + fatal_failure (string_0 == NULL, "string_copy: Destination string is null pointer."); + fatal_failure (string_1 == NULL, "string_copy: Source string is null pointer."); + + for (i = 0; i != string_length (string_1) + 1; ++i) { + string_0 [i] = string_1 [i]; + } + + return (string_0); +} + +char * string_concatenate (char * string_0, char * string_1) { + fatal_failure (string_0 == NULL, "string_concatenate: Destination string is null pointer."); + fatal_failure (string_1 == NULL, "string_concatenate: Source string is null pointer."); + + string_0 += string_length (string_0); + + while (* string_1 != '\0') { + * string_0++ = * string_1++; + /*++string_0; + ++string_1;*/ + } + + * string_0 = '\0'; + + return (string_0); +} + +int string_compare_limit (char * string_0, char * string_1, int limit) { + int i = 0; + + fatal_failure (string_0 == NULL, "string_compare_limit: Destination string is null pointer."); + fatal_failure (string_1 == NULL, "string_compare_limit: Source string is null pointer."); + + for (i = 0; i != limit; ++i) { + if (string_0 [i] != string_1 [i]) { + return (0); + } + } + + return (1); +} + +char * string_copy_limit (char * string_0, char * string_1, int limit) { + int i = 0; + + fatal_failure (string_0 == NULL, "string_copy_limit: Destination string is null pointer."); + fatal_failure (string_1 == NULL, "string_copy_limit: Source string is null pointer."); + + if (limit <= 0) { + return (string_0); + } + + for (i = 0; i != limit; ++i) { + string_0 [i] = string_1 [i]; + } + + return (string_0); +} + +char * string_concatenate_limit (char * string_0, char * string_1, int limit) { + int i = 0; + int length_0 = 0; + int length_1 = 0; + + fatal_failure (string_0 == NULL, "string_concatenate_limit: Destination string is null pointer."); + fatal_failure (string_1 == NULL, "string_concatenate_limit: Source string is null pointer."); + + if (limit <= 0) { + return (string_0); + } + + length_0 = string_length (string_0); + length_1 = string_length (string_1); + + for (i = 0; (i != length_1) && (i != limit); ++i) { + string_0 [length_0 + i] = string_1 [i]; + } + + return (string_0); +} + #endif diff --git a/chapters/chapter_0.h b/chapters/chapter_0.h index 88773c7..41f5ea1 100644 --- a/chapters/chapter_0.h +++ b/chapters/chapter_0.h @@ -204,4 +204,12 @@ extern void * deallocate (void * data ); extern int string_length (char * string); // We deal with strings a lot in this program, so string functions will be more important than character functions from chapter one. +extern int string_compare (char * string_0, char * string_1); +extern char * string_copy (char * string_0, char * string_1); +extern char * string_concatenate (char * string_0, char * string_1); + +extern int string_compare_limit (char * string_0, char * string_1, int limit); +extern char * string_copy_limit (char * string_0, char * string_1, int limit); +extern char * string_concatenate_limit (char * string_0, char * string_1, int limit); + #endif diff --git a/chapters/chapter_1.c b/chapters/chapter_1.c index f718ae0..2c34978 100644 --- a/chapters/chapter_1.c +++ b/chapters/chapter_1.c @@ -59,7 +59,7 @@ int character_is_invisible (char character) { } int character_is_escape (char character) { - return ((int) (character == '\033')); + return ((int) (character == CHARACTER_ESCAPE)); } int character_is_underscore (char character) { @@ -82,4 +82,99 @@ int character_compare_array (char character, char * character_array) { return (0); } +int file_open (char * name, int mode) { + int descriptor = -1; + + fatal_failure (name == NULL, "file_open: Failed to open file, name is null pointer."); + + descriptor = open (name, mode); + + fatal_failure (descriptor == -1, "file_open: Failed to open file, function open returned invalid descriptor."); + + return (descriptor); +} + +int file_close (int file) { + fatal_failure (file == -1, "file_close: Failed to close file, invalid file descriptor."); + fatal_failure (close (file) == -1, "file_close: Failed to close file, function close returned invalid code."); + + return (-1); +} + +void file_read (int file, void * data, int size) { + fatal_failure (file == -1, "file_read: Failed to read from file, invalid descriptor."); + fatal_failure (data == NULL, "file_read: Failed to read from file, data is null pointer."); + fatal_failure (size == 0, "file_read: Failed to read from file, size is zero."); + + (void) read (file, data, (unsigned long int) size); +} + +void file_write (int file, void * data, int size) { + fatal_failure (file == -1, "file_write: Failed to write to file, invalid descriptor."); + fatal_failure (data == NULL, "file_write: Failed to write to file, data is null pointer."); + fatal_failure (size == 0, "file_write: Failed to write to file, size is zero."); + + (void) write (file, data, (unsigned long int) size); +} + +int file_seek (int file, int whence) { + fatal_failure (file == -1, "file_seek: Failed to seek in file, invalid descriptor."); + + return ((int) lseek (file, 0, whence)); +} + +int file_size (char * name) { + int size = -1; + int file = -1; + + file = file_open (name, O_RDONLY); + + size = lseek (file, 0, SEEK_END); + + fatal_failure (size == -1, "file_size: Failed to get size of file, invalid file size."); + + file = file_close (file); + + return (size); +} + +int file_type (char * name) { + char * file_type_data [FILE_TYPE_COUNT] = { + ".txt", ".s", ".fasm", ".gasm", ".nasm", ".yasm", ".c", ".h", + ".adb", ".ads", ".cpp", ".hpp" + }; + + int type = 0; + + while (* name != '.') { + ++name; + } + + for (type = 0; type != FILE_TYPE_COUNT; ++type) { + if (string_compare (name, file_type_data [type]) != 0) { + return (type); + } + } + + return (-1); +} + +void * file_record (char * name) { + int file = -1; + int size = -1; + char * data = NULL; + + fatal_failure (name == NULL, "file_import: Failed to import file, name is null pointer."); + + file = file_open (name, O_RDONLY); + size = file_size (name); + data = allocate (size); + + file_read (file, data, size); + + file = file_close (file); + + return (data); +} + #endif diff --git a/chapters/chapter_1.h b/chapters/chapter_1.h index e5cdcbd..4685094 100644 --- a/chapters/chapter_1.h +++ b/chapters/chapter_1.h @@ -85,8 +85,33 @@ extern int this_is_kind (kind_type this); // kind_type - we choose the type of arguments in our function, since our function deals with 'kind', we choose proper 'kind_type'. // this - we named our argument about what's it supposed to be, use similar approach in return type, function, argument type and argument names. @ + +Before continuing, lets describe some imporant types in C very simply: + + Word: Bytes: Bits: Kind: Minimum: Maximum: + void / / Black hole / / + void * 8 64 Memory address / / + char 1 8 Integer -128 127 + short 2 16 Integer -32768 32767 + int 4 32 Integer -2147483648 2147483647 + long 8 64 Integer -9223372036854775808 9223372036854775807 + unsigned char 1 8 Natural 0 256 + unsigned short 2 16 Natural 0 65535 + unsigned int 4 32 Natural 0 4294967295 + unsigned long 8 64 Natural 0 18446744073709551615 + float 4 32 Real (IEEE754) / / + double 8 64 Real (IEEE754) / / + +Note that you shouldn't care for now about 'void' and 'void *', because they're special cases, nor about their minimum and maximum. */ +enum { + FILE_TYPE_TEXT, FILE_TYPE_COMMON_ASSEMBLY, FILE_TYPE_FLAT_ASSEMBLY, FILE_TYPE_GNU_ASSEMBLY, + FILE_TYPE_NETWIDE_ASSEMBLY, FILE_TYPE_YET_ANOTHER_ASSEMBLY, FILE_TYPE_C_SOURCE, FILE_TYPE_C_HEADER, + FILE_TYPE_ADA_BODY, FILE_TYPE_ADA_SPECIFICATION, FILE_TYPE_CPP_SOURCE, FILE_TYPE_CPP_HEADER, + FILE_TYPE_COUNT +}; + extern int character_is_uppercase (char character); // Notice how we align those functions, I believe this improves the readability of any program, in any programming language. extern int character_is_lowercase (char character); // Some people would just use 'ischrupp' or something, but I hate reading code written like that... extern int character_is_digit (char character); // Important note is also that a programming language is not, and it should be like natural language, why? @@ -101,4 +126,13 @@ extern int character_is_hexadecimal (char character); extern int character_compare_array (char character, char * character_array); // This function is singled out, because it's different from those above, and we use it internally. +extern int file_open (char * name, int mode); +extern int file_close (int file); +extern void file_read (int file, void * data, int size); +extern void file_write (int file, void * data, int size); +extern int file_seek (int file, int whence); +extern int file_size (char * name); +extern int file_type (char * name); +extern void * file_record (char * name); + #endif diff --git a/chapters/chapter_2.h b/chapters/chapter_2.h index 8626aa5..c49b4da 100644 --- a/chapters/chapter_2.h +++ b/chapters/chapter_2.h @@ -25,13 +25,13 @@ compiler that your function will be used in other files, or if you're compiling declarations should be in C header file, and function definitions should be in C source file. @C -// Function declaration: Output: Input: -extern void function_0 (void); // undefined (none) // undefined (none) -extern float function_1 (double a); // ieee754_32b // ieee754_64b -extern int function_2 (int a, int b); // integer_32b // integer_32b, integer_32b -static unsigned int function_3 (char * a, int b, char c); // natural_32b // integer_8b_pointer, integer_32b, integer_8b -static char * function_4 (char * a); // integer_8b_pointer // integer_8b_pointer -static void * function_5 (struct structure * a, void * b); // undefined_pointer // [structure]_pointer, undefined_pointer +// Function declaration: // # // Output: // Input: +extern void function_0 (void); // 0 // undefined (none) // undefined (none) +extern float function_1 (double a); // 1 // ieee754_32b // ieee754_64b +extern int function_2 (int a, int b); // 2 // integer_32b // integer_32b, integer_32b +static unsigned int function_3 (char * a, int b, char c); // 3 // natural_32b // integer_8b_pointer, integer_32b, integer_8b +static char * function_4 (char * a); // 4 // integer_8b_pointer // integer_8b_pointer +static void * function_5 (struct s * a, void * b); // 5 // undefined_pointer // [structure]_pointer, undefined_pointer extern unsigned long int * PLEASE_NO (const signed short int, void *, const char const * *, long double []); @ @@ -39,21 +39,26 @@ extern unsigned long int * PLEASE_NO (const signed short int, void *, const char So, lets go through those 6 normal functions, and then lets see what's wrong with 'PLEASE_NO' function. 0) This is the simplest form of function, which can be used to make the code more clear (sometimes) by breaking down long functions into smaller ones, and calling them in certain -order. However, don't abuse it for code refactoring, because most of the time, procedural is the easiest to read, write and debug. +order. However, don't abuse it for code refactoring, because most of the time, procedural code is the easiest to read, write and debug. 1) In C (and in non-embedded environment, also known as your normal post-2012 laptop or computer), keyword 'float' is (almost) real number encoded in IEEE754, 32 bits or 4 bytes long, while keyword 'double' is same as float, but enlarged to 64 bits or 8 bytes. They are pain to use in some cases because of limited precision. -2) -3) -4) -5) +2) Functions can have multiple arguments (input variables), but only one return value (output "variable"). In C, you can name the arguments, but not the return value, also keep in +mind that you should choose descriptive names (in most cases) for function arguments, not name them a, b, c, d like in these examples. +3) If you really want the compiler to verify that some return value is of type 'unsigned int' aka 'uint32_t', you can do so, I prefer 'int' since all number literals in C are +integers by default ('int'). We'll use terms like 'int / integer/ int32_t' interchangably, so keep that in mind in future chapters. +4) Also, type 'char *' can be pointer to (address of) of some 'char' typed variable or array of type 'char'. We'll talk more about arrays in future chapters, but know that we +could use 'char a []' in functions 3 and 4 as well, it's same. You could use them both in order to know which is array and which is pointer. +5) Lastly, this function returns pointer to any type, hence compiler won't do type-checking, it's necesary to this sometimes. It also accepts pointer to (previously defined) +structure 's', and if you used 'typedef' with it, you could just say 's * a', we'll talk more about those. Now, lets talk very briefly about what's wrong with 'PLEASE_NO': -- Don't use long types 'unsigned long int', if you really type with those characteristics, use 'size_t : stdlib', 'uint64_t : stdint' or 'unsigned long'. -- Don't use 'const' keyword at all, that's my honest advice, it won't catch bugs in C, it'll cause useless compiler warnings. Live unsafe, think more. +- Don't use long types 'unsigned long int', if you really type with those characteristics, use 'size_t : stdlib', 'uint64_t : stdint' or 'unsigned long' in worst case. +- Don't use 'const' keyword at all, that's my honest advice, it won't catch that many potential bugs in C, it'll just cause useless compiler warnings. Live unsafe, think more. - Don't use 'signed', since it's default for any integer type, as well as you shouldn't use keywords 'register', 'volatile', 'auto', but more on that later... -- You can use '[]' in function declarations, but it doesn't mean much since array is passed as pointer to first element in C, so '*' is enough. -- Keep in mind that newer (non-ANSI) C standards allow some of the things that I'm not using here, but I don't personally like newer C standards. +- You can use '[]' in function declarations, but it doesn't mean much since array is passed as pointer to first element in C (array decay), so '*' is enough. +- Keep in mind that newer (non-ANSI) C standards allow some of the things that I'm not using here, but I don't personally like newer C standards, I'll mention that a lot. +- Last one is tricky, you should name function agruments in function declarations, but some linters will warn you not to do it, since some compiler don't check them. */ extern void hello_world_0 (void); // All 4 functions take no agruments, and return nothing. They just execute the code that's defined in them.