2023-11-13 21:03:29 -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_0_HEADER
# define CHAPTER_0_HEADER
# include <stdlib.h> // We'll need this header file for malloc, calloc, realloc, free and exit.
# include <fcntl.h> // This one for open and O_ flags.
# include <unistd.h> // And this one for read, write, close and lseek.
/*
> Code formatting
It ' s very important that your code is aligned , readable and safe . So , we ' ll talk about it even before the first chapter , the so - called " zero chapter " . I write assembly and Ada
beside C , but still , 90 % of my projects are written in C programming language ! Why ? Because C is like a girl that ' s a bit older than you , quiet but very smart and respectable , and
you don ' t want to piss her off . Ada is more like nerdy girl who doesn ' t know what she wants , while assembly is ghost girl in your closet that you pretend not to see at night . So ,
this Ada girl was my first serious love , and she had a book " Ada Coding Standard " , which is like a bible to her . It ' s a bit fanatical , but it has some good points which I ' ll
discuss below without copy / paste , and apply to C .
Lets see some good examples of C code ( in my opinion obviously ) , I ' ll provide few of them , and note that I use the first style in them . Needless to say , as this book is aimed to
people who don ' t know C programming language , or are in phase of learning it , you should ( and eventually will ) develop your own style of writing it , but you shouldn ' t force it at
the start , it should happen over time , naturally . Just be sure to always be consistent , and to avoid anything that ' ll generate compiler warnings .
General code formatting should follow these rules in C programming language :
- Always use tabs for indentation , and spaces for alignment . You ' ll hear people say the otherwise , don ' t trust them , it ' s pure cancer .
- Separate most ( not all ! ) operators with space before and after it . We ' ll provide exceptional cases for this rule below .
- Align most ( not all ! ) operators horizontally , and logical units vertically . Again , examples are the best showcase .
- Always use curly braces and round braces , even when they ' re not required by the compiler . Most linters warn about this .
- Avoid writing a lot of comments , or avoid them completely , except for license notice , this book is an exception ! Also , don ' t write GPL notice like I do . . .
- Think very carefully if you ' ll limit the line length , it depends on your workflow and attention span . . . For me , 180 characters per line .
- If you want , use pagination , but I don ' t recommend it when working with C . I use it in Ada , because comments there start with " -- " .
- Always use snake case ( this_style ) , for enumerations and macros ( THIS_STYLE ) , and never put underscore before or after the identifier .
For now , don ' t worry about what ' s an identifier , compiler , linter , those scary and unknown words will be explained later , when the time comes .
Spacing :
- Before and after :
A : B : C : D : E : F :
+ + = & & = & & = =
- - = | | = | | ! =
* * = ^ ^ = ! < =
/ / = < < < < = ? > =
% % = > > > > = : <
= ~ >
A ) Standard operators , addition , subtraction , multiplication , division , modulus and assignment ( except assignment obviously ) .
B ) Extension of standard operators , that ' ll use left - hand variable as first operand .
C ) Binary operators , bit - and , bit - or , bit - xor , bit - shift - left , bit - shift - right and bit - not .
D ) Again , same extension but for binary operators , that ' ll use left - hand variable as first operand ( except bit - not as it ' s unary ) .
E ) Logical operators , and , or , not and operators ' ? ' and ' : ' are used together , as they form ternary operator .
F ) Comparison operators , equals , not - equals , less - or - equal , greater - or - equal , less and greater .
Some more symbols are also operators , but my advice is not to treat them as such , just consider them some kind of special cases .
- Before :
( { [
- After :
) } ] , ;
- None ( unary + and - ) :
. - > " ' ++ -- + -
Exceptions :
- When using more complicated expressions , apply rules above , or make an exception in them if you think you ' ll make it more readable by breaking the rules .
- When merging consecutive opened or closed braces , for example , like when you ' re nesting function calls .
- When declaring functions , align return types and open braces , and by taste , argument types for logical units .
- When declaring variables , align types and asignment operators , and by taste , pointer or array operators .
Naming :
- Have you ever wondered , what the hell is stdio , unistd , stdint , termios , errno , strcat , strchr , strndupa , memcpy , atoi , fgetc , puts , lseek . . . ? I did wonder .
- Are identifiers such as string_concatenate , memory_copy , error_id , integer , boolean , constant , string_to_integer , file_offset too long to type . . . ? I think not .
All that aside , C is an old programming language , and people who have made it knew a lot more about how computers work at their time than most people know nowdays . Hardware got
faster and cheaper , but also more complex . We have enough memory today to edit our text files with word - wrapping enabled , highlight the syntax , and to store large files due to
very long identifiers , 8 - space - wide indentation instead of a single tab and overly complicated OOP code . If you can handle those names above , feel free to use them , but be
consistent . I can ' t handle them , so I tolerate them with C keywords , because I ' d actually prefer " constant / integer / character " over " const / int / char " . . . You ' ll notice that
C has mixed them in keywords , and most standard library headers mix them too . . .
- Keywords : register double short signed break while / int char extern auto enum struct
- Headers : string memory limits time complex threads / stdio ctype stdint unistd fcntl termios
- Functions : read write open close exit clock / strncmp memcpy atoi fputc tcsetattr usleep
About that , I don ' t even know where to begin about talking how bad it is for readability . . . But lets ignore that for now and focus on code formatting . Text you see above this
paragraph is enough to get you started , and text below is just expanding a little more on common sense when you ' re formatting your code . For example , how to align consequtive
external or internal function or variable declarations , how to align if statement condition ( s ) or switch statement body , and more .
There ' s not much to say here , we ' ll talk about function declarations more in later chapters , as they are optional , but I like to use them . However , you should avoid to use too
many function agruments , and always place them in consistent and logical order .
@ C
// Internal and external function declaration:
static int string_copy_limit ( char * string_0 , char * string_1 , int limit ) ;
extern int32_t string_copy ( char * string_a ,
char * string_b ,
int32_t count ) ;
extern size_t copy_string_n (
string_t a ,
string_t b ,
size_t n
) ;
@
Internal ( ' static ' ) and external ( ' extern ' ) variable declarations are different from function declarations , because you need to initialize internal variables to some value , I like
to provide either " zero " value for them , with the corresponding type ( 0 , 0.0 , ' \0 ' , NULL . . . ) , or an invalid value ( - 1 , EOF . . . ) . Some C compilers and linters will warn you about
redeclaring them , some will warn you that they ' re already initialized to zero , so when you some across those warnings , you ' ll know how to fix them . I hate this part of C . . .
@ C
// Internal and external variable declaration:
static int curses_active = 0 ;
static char curses_signal = ' \0 ' ;
static char * curses_screen = NULL ;
extern unsigned int positive ;
extern int negative ;
extern int * * coordinate_system ;
static void ( * encode_type_0 [ TYPE_0_COUNT ] ) ( void ) = { NULL } ;
static char * ( * encode_type_1 [ TYPE_1_COUNT ] ) ( int l ) = { NULL } ;
static int ( * encode_type_2 [ TYPE_2_COUNT ] ) ( int l , int r ) = { NULL } ;
extern xcb_window_t blesses_window ;
extern xcb_gcontext_t blesses_context ;
extern xcb_pixmap_t blesses_pixmap ;
extern xcb_connection_t * blesses_connection ;
extern xcb_screen_t * blesses_screen ;
extern xcb_image_t * blesses_image ;
@
Only for ' main ' function , you shouldn ' t add ' static ' or ' extern ' keywords before it . Every C file needs to have exactly one ' main ' function , it ' s the programs ' entry point . Also ,
some people like to define their own types , with keyword ' typedef ' , I ' m not a fan of it , since in C types are also keywords unlike in Ada . Again , it ' ll become more clean in future
chapters why I dislike user - defined types , unions and structures .
@ C
// Main function:
int main ( int argc , char * * argv ) {
int32_t main ( int32_t argc ,
char * argv [ ] ) {
number_t main (
number_t argc ,
string_t argv [ ] ) {
@
Now , I ' ll write some basic functions that ' ll be used later in the program , so you can see that code formatting I ' ve spent whole hour writing . Don ' t mind what they do , just keep an
eye on how are they aligned and named . I ' ll reimplement some standard functions , and you can revisit them after reading few more chapters , it ' ll be more understandable then .
*/
enum { // This is completely unnecesary, but I don't care, it's a good showcase how boolean type can work, true is 1 and false is 0.
FALSE ,
TRUE
} ;
enum { // This is also one of my preferences, to use CHARACTER_NULL or CHARACTER_LINE_FEED instead of '\0' or '\n' in special (non-string) cases.
CHARACTER_NULL , CHARACTER_START_HEADER , CHARACTER_START_TEXT , CHARACTER_END_TEXT ,
CHARACTER_END_TRANSMISSION , CHARACTER_ENQUIRY , CHARACTER_ACKNOWLEDGE , CHARACTER_BELL ,
CHARACTER_BACKSPACE , CHARACTER_TAB_HORIZONTAL , CHARACTER_LINE_FEED , CHARACTER_TAB_VERTICAL ,
CHARACTER_FORM_FEED , CHARACTER_CARRIAGE_RETURN , CHARACTER_SHIFT_OUT , CHARACTER_SHIFT_IN ,
CHARACTER_DATA_LINK_ESCAPE , CHARACTER_DEVICE_CONTROL_1 , CHARACTER_DEVICE_CONTROL_2 , CHARACTER_DEVICE_CONTROL_3 ,
CHARACTER_DEVICE_CONTROL_4 , CHARACTER_NOT_ACKNOWLEDGE , CHARACTER_SYNCHRONOUS_IDLE , CHARACTER_END_TRANSMISSION_BLOCK ,
CHARACTER_CANCEL , CHARACTER_END_MEDIUM , CHARACTER_SUBSTITUTE , CHARACTER_ESCAPE ,
CHARACTER_FILE_SEPARATOR , CHARACTER_GROUP_SEPARATOR , CHARACTER_RECORD_SEPARATOR , CHARACTER_UNIT_SEPARATOR ,
CHARACTER_COUNT // Not an actual ASCII table count (128), but for starting invisible characters.
} ;
enum { // I like to align enumerations with 10, 20 or 40 characters per name, and optionally use NAME_ as prefix and NAME_COUNT as last element.
EFFECT_NORMAL , EFFECT_BOLD , EFFECT_ITALIC , EFFECT_UNDERLINE , EFFECT_BLINK , EFFECT_REVERSE ,
EFFECT_COUNT
} ;
enum { // Because of text auto-completition, it's always easy to find what I want, or to use NAME_COUNT in arrays. In some cases, order matters!
COLOUR_GREY , COLOUR_RED , COLOUR_GREEN , COLOUR_YELLOW , COLOUR_BLUE , COLOUR_PINK , COLOUR_CYAN , COLOUR_WHITE ,
COLOUR_COUNT
} ;
extern void in ( void * data , int size ) ; // We'll use these functions later as core standard input/output functions.
extern void out ( void * data , int size ) ;
extern void echo ( char * data ) ; // Function 'echo' behaves similar to 'out', but it's easier to use because it doesn't require size.
extern void fatal_failure ( int condition , char * message ) ; // Some functions fail, and sometimes we want to abort everything, crash and leak memory, we just don't care.
extern void limit ( int * value , int minimum , int maximum ) ;
extern void * allocate ( int size ) ; // Core memory management functions with some minimal error checking.
extern void * reallocate ( void * data , int size ) ;
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 ) ; // See how nicely they align, right?
extern char * string_copy ( char * string_0 , char * string_1 ) ;
extern char * string_concatenate ( char * string_0 , char * string_1 ) ;
extern char * string_reverse ( char * string ) ; // Notice last function, we didn't align ');'...
extern int string_compare_limit ( char * string_0 , char * string_1 , int limit ) ; // These ones too, it's beautiful (in my opinion), tho some consider it useless.
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 ) ;
extern char * string_reverse_limit ( char * string , int limit ) ; // And we align the last argument in this case, use whatever you prefer.
extern char * string_realign ( char * string , int amount , char character ) ; // This is a simple function that realigns a string to right, we'll use it way later ahead.
2023-11-21 08:43:45 -05:00
extern int memory_compare ( void * memory , void * source , int length ) ; // We'll cover these functions later, they are more complex.
extern void memory_copy ( void * memory , void * source , int length ) ;
2023-11-13 21:03:29 -05:00
// In chapter two, we'll explain ASCII escape sequences, for now, consider this to be some black magic.
extern void terminal_clear ( void ) ; // Offset and clear terminal screen output.
extern void terminal_colour ( int colour , int effect ) ; // Set terminal character attributes.
extern void terminal_cancel ( void ) ; // Reset terminal character attributes.
extern void terminal_show_cursor ( int show ) ; // Toggle rendering of terminal cursor.
// These will be useful in chapter three, where we'll learn about 'printf' function. It's too complex to cover it at this point.
extern char * number_to_string ( int number ) ;
extern char * format_to_string ( int number , int sign , int base , int amount , char character ) ;
# endif