2023-11-07 10:14:07 -05:00
/*
Copyright ( c ) 2023 : Ognjen ' xolatile ' Milan Robovic
Xhartae is free software ! You will redistribute it or modify it under the terms of the GNU General Public License by Free Software Foundation .
And when you do redistribute it or modify it , it will use either version 3 of the License , or ( at yours truly opinion ) any later version .
It is distributed in the hope that it will be useful or harmful , it really depends . . . But no warranty what so ever , seriously . See GNU / GPLv3 .
*/
# ifndef CHAPTER_1_SOURCE
# define CHAPTER_1_SOURCE
2023-11-10 08:31:22 -05:00
# include "chapter_1.h" // Get used to this... We're pasting macros, enumerations and function declarations again...
2023-11-09 09:41:53 -05:00
2023-11-10 08:31:22 -05:00
int character_is_uppercase ( char character ) { // Returns a boolean value, aka FALSE or TRUE, aka 0 or 1...
2023-11-09 07:33:47 -05:00
return ( ( int ) ( ( character > = ' A ' ) & & ( character < = ' Z ' ) ) ) ;
}
int character_is_lowercase ( char character ) {
return ( ( int ) ( ( character > = ' a ' ) & & ( character < = ' z ' ) ) ) ;
}
int character_is_digit ( char character ) {
return ( ( int ) ( ( character > = ' 0 ' ) & & ( character < = ' 9 ' ) ) ) ;
}
2023-11-09 19:01:22 -05:00
int character_is_blank ( char character ) { // Standard implementation also considers vertical tab and form feed as blank, we don't...
2023-11-09 09:41:53 -05:00
return ( ( int ) ( ( character = = ' ' ) | | ( character = = CHARACTER_TAB_HORIZONTAL ) | | ( character = = CHARACTER_CARRIAGE_RETURN ) | | ( character = = CHARACTER_LINE_FEED ) ) ) ;
// If you like smaller line length limit, you can align it like this:
// return ((character == ' ')
// || (character == CHARACTER_TAB_HORIZONTAL)
// || (character == CHARACTER_CARRIAGE_RETURN)
// || (character == CHARACTER_LINE_FEED));
// Or:
// return ((character == ' ') ||
// (character == CHARACTER_TAB_HORIZONTAL) ||
// (character == CHARACTER_CARRIAGE_RETURN) ||
// (character == CHARACTER_LINE_FEED));
// Or even use literal characters:
// return ((character == ' ') ||
// (character == '\t') ||
// (character == '\r') ||
// (character == '\n'));
2023-11-09 07:33:47 -05:00
}
2023-11-09 19:01:22 -05:00
int character_is_alpha ( char character ) { // Returns TRUE / 1 or FALSE / 0 depending on if the character is either uppercase or lowercase.
2023-11-09 07:33:47 -05:00
return ( ( character_is_uppercase ( character ) ! = 0 ) | | ( character_is_lowercase ( character ) ! = 0 ) ) ;
}
2023-11-09 19:01:22 -05:00
int character_is_symbol ( char character ) { // Returns TRUE / 1 if character is one of the characters in that string (array of characters), otherwise it returns FALSE / 0.
2023-11-09 07:33:47 -05:00
return ( character_compare_array ( character , " ~!@#$%^&*()+{}|: \" <>?`-=[] \\ ;',./ " ) ) ;
}
2023-11-09 19:01:22 -05:00
int character_is_visible ( char character ) { // This is visible (printable) character range, and space is included in there.
2023-11-09 07:33:47 -05:00
return ( ( int ) ( ( character > = ' ' ) & & ( character < = ' ~ ' ) ) ) ;
}
2023-11-09 19:01:22 -05:00
int character_is_invisible ( char character ) { // If character is not visible, then guess what? It's invisible.
return ( character_is_visible ( character ) = = FALSE ) ;
2023-11-09 07:33:47 -05:00
}
2023-11-09 19:01:22 -05:00
int character_is_escape ( char character ) { // We might use this function...
2023-11-09 13:22:59 -05:00
return ( ( int ) ( character = = CHARACTER_ESCAPE ) ) ;
2023-11-09 07:33:47 -05:00
}
2023-11-09 19:01:22 -05:00
int character_is_underscore ( char character ) { // I don't even know if I'll ever use this one, we'll see, I'm in the process of writing this "book"...
2023-11-09 07:33:47 -05:00
return ( ( int ) ( character = = ' _ ' ) ) ;
}
2023-11-09 19:01:22 -05:00
int character_is_hexadecimal ( char character ) { // Same as function 'character_is_symbol', but for hexadecimal digits.
2023-11-09 07:33:47 -05:00
return ( character_compare_array ( character , " 0123456789ABCDEF " ) ) ;
}
2023-11-10 06:24:57 -05:00
/*
Now , we can see how function ' character_compare_array ' was implemented , but know that it could be even shorter , like you see below . However , I really think it ' s for the best to
use curly and round braces , when even the compiler won ' t warn about them . You can easily see the scope of something if you have a text editor capable of highlighting matching
braces , and almost all of them have that feature .
@ C
int character_compare_array ( char character , char * character_array ) {
for ( ; * character_array ! = CHARACTER_NULL ; + + character_array )
if ( character = = * character_array )
return ( TRUE ) ;
return ( FALSE ) ;
}
@
*/
2023-11-09 19:01:22 -05:00
int character_compare_array ( char character , char * character_array ) { // I didn't use name "string", but "character_array", to explicitly show the intention of argument.
int offset ;
2023-11-09 07:33:47 -05:00
2023-11-09 19:01:22 -05:00
for ( offset = 0 ; offset ! = string_length ( character_array ) ; + + offset ) { // We iterate through string (character array!) and return TRUE / 1 if we found it.
if ( character = = character_array [ offset ] ) { // If we don't find it in that string, we return FALSE / 0 since it's not there.
return ( TRUE ) ; // Note that we could do this without the variable 'offset', similar to string functions.
2023-11-09 07:33:47 -05:00
}
}
2023-11-09 19:01:22 -05:00
return ( FALSE ) ;
2023-11-09 07:33:47 -05:00
}
2023-11-10 08:31:22 -05:00
/*
You can see important information about some functions on manual pages in every Linux distro , with ' man ( [ optional ] number ) function_name ' , for example ' man 2 open ' , and I ' ll list
few important ones down below with some copy & paste magic , but I ' ll keep it short .
2023-11-09 13:22:59 -05:00
2023-11-10 08:31:22 -05:00
@ C
# include <sys/types.h> // Core types.
# include <sys/stat.h> // Core something, I don't even know...
# include <fcntl.h> // Few system calls.
int open ( const char * pathname , int flags ) ;
int open ( const char * pathname , int flags , mode_t mode ) ;
int creat ( const char * pathname , mode_t mode ) ;
int openat ( int dirfd , const char * pathname , int flags ) ;
int openat ( int dirfd , const char * pathname , int flags , mode_t mode ) ;
// Flags (modes, one of the first three must always be present in mode mask):
// - O_RDONLY: Open or create file as 'read only', prohibit writing to that file.
// - O_WRONLY: Open or create file as 'write only', so you have permission to modify it.
// - O_RDWR: Open or create file as 'read and write', so you can do whatever you want with it...
// - O_APPEND: Before each write system call, the file offset is positioned at the end of the file, as if with lseek system call. Like, continue writing...
// - O_CREAT: If path name doesn't exist, create a new file with the name you specified. The owner of the new file is set to the effective user ID of the process.
// - O_TRUNC: If the file already exists and is a regular file and the access mode allows writing (is O_RDWR or O_WRONLY) it will be truncated to length 0.
@
2023-11-09 13:22:59 -05:00
2023-11-10 08:31:22 -05:00
There ' s a lot more to read in manual pages , about various functions and libraries , they are old ( and somewhat outdated , since not everyone use them nowdays , and some don ' t even
update them ) source of information , but can be good for standard library . Also keep in mind that most functions below return - 1 on error .
*/
2023-11-09 13:22:59 -05:00
2023-11-10 08:31:22 -05:00
int file_open ( char * name , int mode ) {
int descriptor = - 1 ; // Assume error value as default.
fatal_failure ( name = = NULL , " file_open: Failed to open file, name is null pointer. " ) ; // We must provide non-null address.
fatal_failure ( ( descriptor = open ( name , mode ) ) = = - 1 , " file_open: Failed to open file, function open returned invalid descriptor. " ) ; // We abort of 'open' error...
// We could write something like this too:
// descriptor = open (name, mode);
// fatal_failure (descriptor == -1, "file_open: Failed to open file, function open returned invalid descriptor.");
// Or align it to break two longer function arguments:
// fatal_failure ((descriptor = open (name, mode)) == -1,
// "file_open: Failed to open file, function open returned invalid descriptor.");
2023-11-09 13:22:59 -05:00
2023-11-10 08:31:22 -05:00
return ( descriptor ) ; // Return opened file descriptor.
2023-11-09 13:22:59 -05:00
}
int file_close ( int file ) {
2023-11-10 08:31:22 -05:00
fatal_failure ( file = = - 1 , " file_close: Failed to close file, invalid file descriptor. " ) ; // If 'file' was already closed or corrupted, we abort.
fatal_failure ( close ( file ) = = - 1 , " file_close: Failed to close file, function close returned invalid code. " ) ; // Keep in mind that this isn't always safe.
2023-11-09 13:22:59 -05:00
return ( - 1 ) ;
}
void file_read ( int file , void * data , int size ) {
2023-11-10 08:31:22 -05:00
fatal_failure ( file = = - 1 , " file_read: Failed to read from file, invalid descriptor. " ) ; // We'll comment this out once, since it's all similar with 'file_write'.
fatal_failure ( data = = NULL , " file_read: Failed to read from file, data is null pointer. " ) ; // This function is very similar to 'in', but it accepts a file descriptor.
fatal_failure ( size = = 0 , " file_read: Failed to read from file, size is zero. " ) ; // That means we handle the files, not standard input or output.
2023-11-09 13:22:59 -05:00
2023-11-10 08:31:22 -05:00
( void ) read ( file , data , ( unsigned long int ) size ) ; // If there was no errors, we read, and don't check for errors at all...
2023-11-09 13:22:59 -05:00
}
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 ) {
2023-11-10 08:31:22 -05:00
fatal_failure ( file = = - 1 , " file_seek: Failed to seek in file, invalid descriptor. " ) ; // Make sure we have a valid file descriptor (it's also unsafe to assume it)...
2023-11-09 13:22:59 -05:00
2023-11-10 08:31:22 -05:00
return ( ( int ) lseek ( file , 0 , whence ) ) ; // Keep in mind that C isn't safe language. It's safe only if you use your brain.
2023-11-09 13:22:59 -05:00
}
int file_size ( char * name ) {
2023-11-10 08:31:22 -05:00
int size = - 1 ; // Lets just assume that everything is wrong, everything falls apart...
int file = - 1 ; // Everything is just -1 around us...
2023-11-09 13:22:59 -05:00
2023-11-10 08:31:22 -05:00
file = file_open ( name , O_RDONLY ) ; // We open a file to read it.
size = lseek ( file , 0 , SEEK_END ) ; // We set the offset to the end of the file.
2023-11-09 13:22:59 -05:00
2023-11-10 08:31:22 -05:00
fatal_failure ( size = = - 1 , " file_size: Failed to get size of file, invalid file size. " ) ; // Again, error of 'lseek' would be -1, so we check for that...
2023-11-09 13:22:59 -05:00
2023-11-10 08:31:22 -05:00
file = file_close ( file ) ; // We close the file, meaning we didn't edit it.
2023-11-09 13:22:59 -05:00
2023-11-10 08:31:22 -05:00
return ( size ) ; // And we return file size in bytes.
2023-11-09 13:22:59 -05:00
}
2023-11-10 08:31:22 -05:00
/*
Lets pretend that each file type has only one extension and save our selves from headaches . Fuck C + + with it ' s 10 extensions like ' . cpp ' , ' . c + + ' , ' . cxx ' , ' . cc ' , and variations for
header files , with prefix ' h ' , it ' s cancer . Why the extension wasn ' t just ' . c = c + 1 ' , huh ?
*/
2023-11-09 13:22:59 -05:00
int file_type ( char * name ) {
2023-11-10 13:30:15 -05:00
// Keep in mind that I'm intentionally being inconsistent, so you can see several ways to properly align your code, readability is the key to safety!
// Spacing between separate strings in array below is 10 characters, including comma and double quotes, and I "joined" curly braces too, it fits in 180 characters.
// You could break it up on curly braces, or put each string in it's own line if you wanted.
char * file_type_data [ FILE_TYPE_COUNT ] = { " .txt " , " .s " , " .fasm " , " .gasm " , " .nasm " , " .yasm " , " .c " , " .h " , " .adb " , " .ads " , " .cpp " , " .hpp " } ;
2023-11-09 13:22:59 -05:00
2023-11-10 08:31:22 -05:00
int type ;
2023-11-09 13:22:59 -05:00
2023-11-10 13:30:15 -05:00
for ( ; * name ! = ' . ' ; + + name ) ; // We offset the 'name' until we reach fullstop character.
2023-11-09 13:22:59 -05:00
2023-11-10 13:30:15 -05:00
for ( type = 0 ; type ! = FILE_TYPE_COUNT ; + + type ) { // Then we check if it's one from this array by comparing them sequentially.
if ( string_compare ( name , file_type_data [ type ] ) ! = 0 ) { // If it is, we return the value of enumeration of file types.
2023-11-09 13:22:59 -05:00
return ( type ) ;
}
}
2023-11-10 13:30:15 -05:00
return ( - 1 ) ; // If it's not in array, we return -1, so we don't access the wrong value in some other array.
2023-11-09 13:22:59 -05:00
}
void * file_record ( char * name ) {
2023-11-12 07:19:44 -05:00
int file = - 1 ; // You can also initialize local variables in this way.
2023-11-09 13:22:59 -05:00
int size = - 1 ;
char * data = NULL ;
fatal_failure ( name = = NULL , " file_import: Failed to import file, name is null pointer. " ) ;
2023-11-10 08:31:22 -05:00
file = file_open ( name , O_RDONLY ) ; // Again, we open the file just in order to read it.
size = file_size ( name ) ; // We do it again, but only to get it's size.
data = allocate ( size ) ; // And we allocate new memory for data in that file.
2023-11-09 13:22:59 -05:00
2023-11-10 08:31:22 -05:00
file_read ( file , data , size ) ; // Rest if obvious. This could be implemented smarter. Try to notice why.
2023-11-09 13:22:59 -05:00
file = file_close ( file ) ;
2023-11-10 08:31:22 -05:00
return ( data ) ; // We return pointer to new memory, but remember, we have to free it later.
2023-11-09 13:22:59 -05:00
}
2023-11-07 10:14:07 -05:00
# endif