forked from xolatile/xhartae
Static source and more about variadic functions...
This commit is contained in:
parent
1c3cc4c779
commit
bbef2a609e
@ -174,8 +174,8 @@ int file_size (char * name) {
|
|||||||
int size = -1; // Lets just assume that everything is wrong, everything falls apart...
|
int size = -1; // Lets just assume that everything is wrong, everything falls apart...
|
||||||
int file = -1; // Everything is just -1 around us...
|
int file = -1; // Everything is just -1 around us...
|
||||||
|
|
||||||
file = file_open (name, O_RDONLY); // We open a file to read it.
|
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.
|
size = (int) lseek (file, 0, SEEK_END); // We set the offset to the end of the file.
|
||||||
|
|
||||||
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...
|
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...
|
||||||
|
|
||||||
|
@ -288,10 +288,10 @@ static char * curses_format_character (char character, int colour, int effect) {
|
|||||||
switch (effect) { // Effects aren't enumerated nicely as colours, so here we'll use switch statement instead of nested if-else statements.
|
switch (effect) { // Effects aren't enumerated nicely as colours, so here we'll use switch statement instead of nested if-else statements.
|
||||||
case EFFECT_NORMAL: effect = 0; break; // We could break these into any combination of new lines, we'll just show one example below of how else you can do it.
|
case EFFECT_NORMAL: effect = 0; break; // We could break these into any combination of new lines, we'll just show one example below of how else you can do it.
|
||||||
// case EFFECT_NORMAL:
|
// case EFFECT_NORMAL:
|
||||||
// effect = 0;
|
// effect = 0;
|
||||||
// break;
|
// break;
|
||||||
// case EFFECT_NORMAL: {
|
// case EFFECT_NORMAL: {
|
||||||
// effect = 0;
|
// effect = 0;
|
||||||
// } break;
|
// } break;
|
||||||
case EFFECT_BOLD: effect = 1; break;
|
case EFFECT_BOLD: effect = 1; break;
|
||||||
case EFFECT_ITALIC: effect = 3; break;
|
case EFFECT_ITALIC: effect = 3; break;
|
||||||
@ -342,11 +342,11 @@ void curses_synchronize (void) {
|
|||||||
|
|
||||||
if (signal == '\033') { // And then we modify the actual 'curses_signal'.
|
if (signal == '\033') { // And then we modify the actual 'curses_signal'.
|
||||||
curses_signal |= SIGNAL_ESCAPE;
|
curses_signal |= SIGNAL_ESCAPE;
|
||||||
} else if (character_is_digit (signal) != 0) {
|
} else if (character_is_digit ((char) signal) != 0) {
|
||||||
curses_signal |= SIGNAL_0 + (int) (signal - '0');
|
curses_signal |= SIGNAL_0 + (int) (signal - '0');
|
||||||
} else if (character_is_lowercase (signal) != 0) {
|
} else if (character_is_lowercase ((char) signal) != 0) {
|
||||||
curses_signal |= SIGNAL_A + (int) (signal - 'a');
|
curses_signal |= SIGNAL_A + (int) (signal - 'a');
|
||||||
} else if (character_is_uppercase (signal) != 0) {
|
} else if (character_is_uppercase ((char) signal) != 0) {
|
||||||
curses_signal |= SIGNAL_A + (int) (signal - 'A');
|
curses_signal |= SIGNAL_A + (int) (signal - 'A');
|
||||||
curses_signal |= SIGNAL_SHIFT;
|
curses_signal |= SIGNAL_SHIFT;
|
||||||
} else {
|
} else {
|
||||||
|
@ -11,10 +11,56 @@ It is distributed in the hope that it will be useful or harmful, it really depen
|
|||||||
|
|
||||||
#include "chapter_3.h"
|
#include "chapter_3.h"
|
||||||
|
|
||||||
static void print_colour (char colour_id) {
|
/*
|
||||||
|
This is probably a good time to show you how switch statement works, and two (in my opinion best) ways to align them, but I advise against mixing two alignments you'll see in
|
||||||
|
'print_colour' and 'print_format' functions. Also, it's a good practice to always use 'default' keyword at the end of you switch statements. You don't always need to use 'break'
|
||||||
|
at the end of 'case', but we intend to "end" the switch statement there, so it's fine. Cases can be "fall-through", so if you don't put 'break', it'll execute the code in the next
|
||||||
|
one, or you could have several of cases before any code. I don't like to use them like that, but here are few examples:
|
||||||
|
|
||||||
|
@C
|
||||||
|
// If argument 'character' is any of the uppercase letters, it returns TRUE, if lowercase, it returns FALSE, otherwise exits the program.
|
||||||
|
|
||||||
|
static bool hexadecimal_letter_is_uppercase (char character)
|
||||||
|
switch (character) {
|
||||||
|
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': return (TRUE); // We don't need to break here, because these two return from function.
|
||||||
|
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': return (FALSE); // If there was code after the switch statement, we would use 'break' keyword.
|
||||||
|
default: exit (EXIT_FAILURE); // We don't need to break here yet again, because we exit a program!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@
|
||||||
|
|
||||||
|
You can use only integers in cases, and remember, 'char' is implicitly promoted to 'int', aka integer. And important thing to keep in mind is that you can write every switch
|
||||||
|
statement as if-else statement, but it would be longer, more error-prone, some lines of code would repeat, and many more inconveniences would come, especially because in C, cases
|
||||||
|
can fall-through, like in the example below. Sad truth is, people are dumb, and many compilers and linters will output a warning message complaining that you used fall-through
|
||||||
|
case(s) in your switch statement. Lets explain what 'echo_one_by_one' function would do.
|
||||||
|
|
||||||
|
@C
|
||||||
|
// Print out some stuff, it'll look like this for these inputs (and it'll do nothing for input that isn't between 0 and 3 (inclusive)):
|
||||||
|
// echo_one_by_one (0) => "Zero One Two Three"
|
||||||
|
// echo_one_by_one (1) => "One Two Three"
|
||||||
|
// echo_one_by_one (2) => "Two Three"
|
||||||
|
// echo_one_by_one (3) => "Three"
|
||||||
|
|
||||||
|
static void echo_one_by_one (int number) {
|
||||||
|
switch (number) {
|
||||||
|
case 0: echo ("Zero ");
|
||||||
|
case 1: echo ("One ");
|
||||||
|
case 2: echo ("Two ");
|
||||||
|
case 3: echo ("Three ");
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
You can find situations in which fall-through cases are good, for example, they can be very useful when encoding some CPU instructions into machine code, but guess what? The
|
||||||
|
compiler will think you've made some kind of mistake, like that you forgot to break from those cases, and it'll warn you about it. I like to clean all compiler warnings (and some
|
||||||
|
linter warnings, if they're not totally brain-dead), so I just don't use them. I know, sounds stupid, but there's usually some other way to do it, and to get the same solution.
|
||||||
|
@
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void print_colour (char colour_id) { // We use "special" character '/' to use terminal colours.
|
||||||
switch (colour_id) {
|
switch (colour_id) {
|
||||||
case '/': out ("/", 1); break;
|
case '/': out ("/", 1); break; // If we have literally typed "//" in our 'format' string, it'll just output "/".
|
||||||
case '0': terminal_colour (COLOUR_GREY, EFFECT_BOLD); break;
|
case '0': terminal_colour (COLOUR_GREY, EFFECT_BOLD); break; // Making the use of our 'terminal_colour' function.
|
||||||
case '1': terminal_colour (COLOUR_RED, EFFECT_BOLD); break;
|
case '1': terminal_colour (COLOUR_RED, EFFECT_BOLD); break;
|
||||||
case '2': terminal_colour (COLOUR_GREEN, EFFECT_BOLD); break;
|
case '2': terminal_colour (COLOUR_GREEN, EFFECT_BOLD); break;
|
||||||
case '3': terminal_colour (COLOUR_YELLOW, EFFECT_BOLD); break;
|
case '3': terminal_colour (COLOUR_YELLOW, EFFECT_BOLD); break;
|
||||||
@ -22,59 +68,80 @@ static void print_colour (char colour_id) {
|
|||||||
case '5': terminal_colour (COLOUR_PINK, EFFECT_BOLD); break;
|
case '5': terminal_colour (COLOUR_PINK, EFFECT_BOLD); break;
|
||||||
case '6': terminal_colour (COLOUR_CYAN, EFFECT_BOLD); break;
|
case '6': terminal_colour (COLOUR_CYAN, EFFECT_BOLD); break;
|
||||||
case '7': terminal_colour (COLOUR_WHITE, EFFECT_BOLD); break;
|
case '7': terminal_colour (COLOUR_WHITE, EFFECT_BOLD); break;
|
||||||
case '-': terminal_colour (COLOUR_WHITE, EFFECT_NORMAL); break;
|
case '-': terminal_cancel (); break; // Making the use of our 'terminal_cancel' function, "/-" will end colouring.
|
||||||
default: terminal_colour (COLOUR_WHITE, EFFECT_NORMAL); break;
|
default: out ("?", 1); break; // Now, if we provided some other character after "/", I like to make an intentional mismatch.
|
||||||
}
|
} // It's not such a big mistake to abort the program, since it's obvious that results are bad.
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_format (char format_id, va_list argument_list) {
|
static void print_format (char format_id, va_list argument_list) { // We use character '%' this time, same as 'printf' function does, lets see...
|
||||||
switch (format_id) {
|
switch (format_id) {
|
||||||
case '%': {
|
case '%': {
|
||||||
out ("%", 1);
|
out ("%", 1);
|
||||||
} break;
|
} break;
|
||||||
case 'i': {
|
case 'i': {
|
||||||
int integer;
|
int integer; // Leave these local variables uninitialized (don't assign a value to them).
|
||||||
integer = va_arg (argument_list, int);
|
integer = va_arg (argument_list, int); // Macro 'va_arg' will move argument from the list into our local variable, with the provided type.
|
||||||
echo (number_to_string (integer));
|
echo (number_to_string (integer)); // You might get the feeling that this isn't type safe, and you'd be totally right.
|
||||||
} break;
|
} break;
|
||||||
case 'f': {
|
case 'f': {
|
||||||
double ieee754;
|
double ieee754; // Because we used curly braces, we can declare local variables in these blocks of code.
|
||||||
ieee754 = va_arg (argument_list, double);
|
ieee754 = va_arg (argument_list, double); // I intentionally call this IEEE754 because I hate to use 'float' and 'double'.
|
||||||
echo (number_to_string ((int) ieee754));
|
echo (number_to_string ((int) ieee754)); // And we're printing to terminal our (rounded) number.
|
||||||
} break;
|
} break;
|
||||||
case 's': {
|
case 's': {
|
||||||
char * string;
|
char * string; // Really simple stuff, but needs some time getting used to it, we're writing in an old language.
|
||||||
string = va_arg (argument_list, char *);
|
string = va_arg (argument_list, char *); // In my opinion, this should be the part of the C language itself, not some macro black magic.
|
||||||
echo (string);
|
echo (string); // You can write a bunch of variadic functions yourself if you want, to easy your workflow in some cases.
|
||||||
} break;
|
} break;
|
||||||
default: {
|
default: {
|
||||||
out ("?", 1);
|
out ("?", 1); // Again, I think it's best to print a bad thing, see it and fix it, then to abort the program in this case...
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This entire switch statement can be written more shortly (in terms of lines of code) like this:
|
||||||
|
// switch (format_id) {
|
||||||
|
// case '%': { out ("%", 1); } break;
|
||||||
|
// case 'i': { int integer; integer = va_arg (argument_list, int); echo (number_to_string (integer)); } break;
|
||||||
|
// case 'f': { double ieee754; ieee754 = va_arg (argument_list, double); echo (number_to_string ((int) ieee754)); } break;
|
||||||
|
// case 's': { char * string; string = va_arg (argument_list, char *); echo (string); } break;
|
||||||
|
// default: { out ("?", 1); } break;
|
||||||
|
// }
|
||||||
|
// Choose your own preference with switch statement (and any other one), and stay consistent with how you format it.
|
||||||
|
// Some people prefer more shorter lines of code, others prefer less longer lines of code (like I do).
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Before we take a look at the actual implementation of our simple 'print' function, here's how you'd use it.
|
||||||
|
|
||||||
|
@C
|
||||||
|
print ("My integer is %i.\n", 404); // Prints "My integer is 404." with new line.
|
||||||
|
print ("Heyo %s %s", "world", "#6!\n"); // Prints "Heyo world #6!" with new line.
|
||||||
|
print ("/1Cyaa world!/-\n"); // Prints red "Cyaa world!" with new line.
|
||||||
|
@
|
||||||
|
*/
|
||||||
|
|
||||||
void print (char * format, ...) {
|
void print (char * format, ...) {
|
||||||
va_list argument_list;
|
va_list argument_list; // This is mandatory local variable if we'll use variadic arguments in this function.
|
||||||
|
|
||||||
int offset, length;
|
int offset, length;
|
||||||
|
|
||||||
length = string_length (format);
|
length = string_length (format);
|
||||||
|
|
||||||
va_start (argument_list, format);
|
va_start (argument_list, format); // Every variadic function needs to start with this (before using 'va_arg').
|
||||||
|
|
||||||
for (offset = 0; offset != length; ++offset) {
|
for (offset = 0; offset != length; ++offset) { // We start iterating through our 'format' string, and looking for special characters below.
|
||||||
if (format [offset] == '/') {
|
if (format [offset] == '/') { // Colouring special character is '/', and colours are specified from '0'...'7', and '-' to cancel them.
|
||||||
++offset;
|
++offset;
|
||||||
print_colour (format [offset]);
|
print_colour (format [offset]); // We're calling function that will colour our printed text, this one is simple.
|
||||||
} else if (format [offset] == '%') {
|
} else if (format [offset] == '%') { // And formatting special character is '%', it'll use variadic arguments!
|
||||||
++offset;
|
++offset;
|
||||||
print_format (format [offset], argument_list);
|
print_format (format [offset], argument_list); // We're calling function that will format our agruments, so we pass variable 'argument_list'.
|
||||||
} else {
|
} else {
|
||||||
out (& format [offset], 1);
|
out (& format [offset], 1); // Not a special character? Okay, we'll just print them one by one.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
va_end (argument_list);
|
va_end (argument_list); // And every variadic function needs to end with this. Puns intended.
|
||||||
}
|
}
|
||||||
|
|
||||||
void file_print (int file, char * format, ...) {
|
void file_print (int file, char * format, ...) {
|
||||||
|
12
compile.sh
12
compile.sh
@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
set -xe
|
set -xe
|
||||||
|
|
||||||
gcc -g -Wall -Wextra -Wpedantic -Werror -O0 -c -o chapters/chapter_0.o chapters/chapter_0.c
|
clang -DSTATIC_SOURCE -g -Weverything -O0 -o xhartae xhartae.c
|
||||||
gcc -g -Wall -Wextra -Wpedantic -Werror -O0 -c -o chapters/chapter_1.o chapters/chapter_1.c
|
|
||||||
gcc -g -Wall -Wextra -Wpedantic -Werror -O0 -c -o chapters/chapter_2.o chapters/chapter_2.c
|
|
||||||
gcc -g -Wall -Wextra -Wpedantic -Werror -O0 -c -o chapters/chapter_3.o chapters/chapter_3.c
|
|
||||||
|
|
||||||
gcc -g -Wall -Wextra -Wpedantic -Werror -O0 -c -o xhartae.o xhartae.c
|
gcc -g -Wall -Wextra -Wpedantic -O0 -c -o chapters/chapter_0.o chapters/chapter_0.c
|
||||||
|
gcc -g -Wall -Wextra -Wpedantic -O0 -c -o chapters/chapter_1.o chapters/chapter_1.c
|
||||||
|
gcc -g -Wall -Wextra -Wpedantic -O0 -c -o chapters/chapter_2.o chapters/chapter_2.c
|
||||||
|
gcc -g -Wall -Wextra -Wpedantic -O0 -c -o chapters/chapter_3.o chapters/chapter_3.c
|
||||||
|
|
||||||
|
gcc -g -Wall -Wextra -Wpedantic -O0 -c -o xhartae.o xhartae.c
|
||||||
|
|
||||||
gcc -o xhartae xhartae.o chapters/chapter_0.o chapters/chapter_1.o chapters/chapter_2.o chapters/chapter_3.o
|
gcc -o xhartae xhartae.o chapters/chapter_0.o chapters/chapter_1.o chapters/chapter_2.o chapters/chapter_3.o
|
||||||
|
|
||||||
|
@ -6,10 +6,17 @@ And when you do redistribute it or modify it, it will use either version 3 of th
|
|||||||
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.
|
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 STATIC_SOURCE
|
||||||
#include "chapters/chapter_0.h"
|
#include "chapters/chapter_0.h"
|
||||||
#include "chapters/chapter_1.h"
|
#include "chapters/chapter_1.h"
|
||||||
#include "chapters/chapter_2.h"
|
#include "chapters/chapter_2.h"
|
||||||
#include "chapters/chapter_3.h"
|
#include "chapters/chapter_3.h"
|
||||||
|
#else
|
||||||
|
#include "chapters/chapter_0.c"
|
||||||
|
#include "chapters/chapter_1.c"
|
||||||
|
#include "chapters/chapter_2.c"
|
||||||
|
#include "chapters/chapter_3.c"
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
About this "book":
|
About this "book":
|
||||||
|
Loading…
Reference in New Issue
Block a user