C Programming - Tutorial 2 - For Wings


Forward:

I began my first tutorial with a forward that was more of a disclaimer. I stated that I was a beginner and had lots to learn. Well, I'm still an island in an ocean of programming techniques and practices, but I have definitely learned a LOT!

Most of the things I discuss in this tutorial could be found else where, if you took the time to look it all up. But I put it here, because I think it's relevant to someone who wants to program in C on the Commodore64 running the WiNGs Operating System. And of course, how I discuss it will always have a Commodore tilt.

Without further a due, lets proceed...


Table of Contents:

Structures intro
Memory and allocation
Pointers and string handling
Structures in linked lists
Variable conversion
Quick math tips
Adjusting and manipulating the console


Structures intro

We have discussed what simple variables are like:

int
long
char
char *

etc. But now I'm going to talk about what a variable really IS, how much memory it occupies and how to group variables into collections called data structures, referred to as structs.

When you make a variable, you declare what its type is. This is only for the compiler to know how big it is, and how to treat it when doing math on it. The actual variable itself is just an address in memory with a byte or two or more reserved at that address to hold some custom data.

So, an int is 2 bytes, a long is 4 bytes. And a char is 1 byte. And a pointer of any type is 4 bytes (only 3 or which are significant). Defining them as types allows the compiler to know if you are going to run into trouble. For example if you have 2 ints, and you try to multiply them together, and store them in a char, you are going to run into trouble. And the compiler will warn you about this at compile-time.

Variables are essential and work the way you'd expect them to, however grouping them into a struct unleashes some awesome power. You make a struct as follows:

typedef struct mystruct_s {
  int a;
  int b;
  char * astring;
} mystruct;

Now instead of just ints, longs, and chars we have mystructs. The size of mystruct is the total size of the variables that it contains. In the case of mystruct as defined above, that would be 8 bytes. 2 ints, and a char pointer. This size can be determined more easily using sizeof() function. Now in code we can do the following:

void main() {
  mystruct * structa;

    structa = (mystruct *)malloc(sizeof(mystruct));

  structa->a = 1;
  structa->b = 3;

  structa->astring = (char *)malloc(256);

  sprintf(structa->astring, "%d + %d = %d", structa->a, structa->b, struct->a + structa->b);
}

Do you follow what is going on? We first made a new "type" of variable, which is actually a struct that contains other variables. Then we make a new instance of our struct which we called structa. Now structa is like a logical unit, and its parts can be set like normal variables, simply by accessing its parts with a -> . Which, by the way, should be reminiscent of an arrow. So we set structa->a to 1. a is an int inside structa. So there is no problem with that.

Well, how is this powerful? There are a number of clever ways to use structures to improve your life as a programmer. And some ways to use them that enable functionality that is simply not possible without them. The first and most clear benefit of them is in passing complex sets of variables into and back from functions. Witness the following:

typedef struct mystruct_s {
  int added;
  int subtracted;
  int multiplied;
  int divided;
} mystruct;

void main() {
  mystruct * structa;

  structa = CalculateTheVars(20,18);

  printf("20 and 18, added = %d, subtracted = %d", structa->added, structa->subtracted);
  printf("20 and 18, multiplied = %d, divided = %d", structa->multiplied, structa->dividied);
}

mystruct * CalculateTheVars(int a, int b) {
  mystruct * The Structure;
  TheStructure = (mystruct *)malloc(sizeof(mystruct));

  TheStructure->added = a + b;
  TheStructure->subtracted = a - b;
  TheStructure->multiplied = a * b;
  TheStructure->dividied = a / b; 

  return(TheStructure);
}

As you can see in the above example. We made a structure with 4 ints. Then we have a main function, plus a seperate function to do the calculations. We pass the 2 numbers to the calculating function, and it passes back a pointer to a structure with all 4 answers! We are now free to pick and choose which of these answers we want to use. Without structs, it is only possible to return ONE type of variable from a function. But since we made up our own type of variable, we get a lot more power out of functions with returns.

You shouldn't proceed any further if you don't fully understand what is going on in the section above. It only gets more convoluted from here on in.

Next we are going to introduce something interesting to our struct. Take a look at the following struct definition:

typedef struct linkedstrings_s {
  char * string;
  struct linkedstring_s * nextstring;
  struct linkedstring_s * prevstring;
} linkedstrings;

The struct itself, contains variable pointers to other structures exactly like itself. You can imagine how this would be used. However, before we talk about implementing this use, we first need to know more about memory, allocation and pointers.



Memory and Memory Allocation

We know that the computer has addressable memory locations. At which we can store our data, and we can go back to these memory locations to retrieve our data, or to update the data stored there. If we make an int, or a long or a char, or structs that contain only these 3 types of variables, then the compiler knows and can calculate exactly how much memory that is going to need, and can set aside free space for those variables right at the start. If however, we have a pointer, we're not sure how much memory is going to be needed. The pointer itself is 4 bytes so we know that. However, the pointer only holds a number which is the memory address of another free section of memory. This free section of memory is dynamic in size. Which means we can put data of various length into memory and still access it.

However, we have to know where there is some free space in memory large enough for the data we want to store. And when we've found that memory, we assign our pointer to Point to the free space. Like this:

  char * mystring;

  mystring = (char *)malloc(256);

Malloc() goes and finds a sequential section of free ram that is at least 256 bytes in length. It reserves this ram, by telling the operating system that it is taken, and returns a pointer to the starting address of this 256 byte block. We capture this returned address pointer with our char * mystring variable. Now we can use the memory starting at the address specified by mystring, PLUS we can use Each of the memory locations addressed by mystring+1 through mystring+255. Does that make sense?

Let me draw an example to help make it clearer. The mystring pointer is essentially just a number. Until we specifically set it to something, we can't reliably use its default value. So, we have a memory range of 5500 through AAFF. (This is JUST an example.) Now, lets say, we need 256 bytes of free space. So OS checks 5500 through 55FF, and that's not entirely free. Then it checks 5600 through 56FF. And low and behold, all of that space is free. So it returns to us a pointer. Which is a number. And the number is 5600. The Number "5600" is held by our pointer variable mystring. So, if we wanted to put something in 56FF, we could use mystring+255.

Fortunately there are functions that do most of this handling for us. So for example, after allocating some space, we could do the following:

  sprintf(mystring, "This is a string of text");

sprintf takes a char * as its first argument. And inserts the characters of the string into the sequential bytes in memory, starting at where the pointer specifies. So the 'T' goes into 5600, the 'h' goes into 5601, the 'i' into 5602, and the 's' into 5603. And so on. And we know this is ok, as long as we don't try and put in more than was allocated for.

Putting in more than you allocated for is called buffer overflow. sprintf doesn't have any idea about allocated memory. All it knows is where to start putting bytes, and how many it is going to put. It's up to you to make sure you don't allocate 20 and then give sprintf 30 bytes to shove in there. The extra 10 bytes would be placed in sequence following the end of the allocated block. Potentially, the bytes after the allocated block may be allocated by another variable, or even an other process. Overwriting another process's variables almost immediately produces an unstoppable crash of the entire computer.

SO... Be Very Careful with your allocation. One thing to note is, there are Some functions that allocate memory for you. getline() and strdup() allocate memory for themselves. But for any other function that does not allocate its own memory, you MUST allocate the memory you are going to use, every single time, before using it. You are free to reuse the same allocated memory, but if you are going to make a new string, it Must be allocated for.

Before I get deeper into pointers, There is an easy way to allocate exactly the right amount of space that you need. If you are allocating for a string, you can use strlen(), and if you are allocating for a struct, you can use sizeof(). Like this:

  mystruct * ASampleStructPtr;
  char * ASampleStringPtr;

  ASampleStructPtr = (mystruct *)malloc(sizeof(mystruct));

  ASampleStringPtr = (char *)malloc(strlen("I'm allocating enough to hold exactly this string")+1);

And that's it, you've allocated exactly enough to hold that string. You need the +1 on the end of the string because all strings have a terminating 0 byte at the end of them.



Pointers

What happens to a block of allocated memory if you take the pointer to it and point the pointer somewhere else? Not much. The allocated block of memory stays allocated, and thus, used, until you tell it to no longer be reserved. You do that with the free() function. It is important to note that you can only free() a block of memory by giving it the pointer to the START of the block. And you should free() all allocated memory when you are done with it, or when the pointer is going to disappear or be assigned to something else. For example before returning from a function.

I mention above that it has to be a pointer that points at the START or the first byte of the allocated block, because it is possible to have more than one pointer pointing at either the same address, or different addressses that are both within the same allocated block.

There are two ways you can reference a byte in a string, Using an array index or using a pointer that you increment. I'll use the example of where you might want to replace all the Spaces in a string with underscores. The first example uses an array index:

int i

for(i = 0; i < strlen(mystring); i++) {
  if(mystring[i] == ' ')
    mystring[i] = '_';
}

That will work. Like I said in the first tutorial, you can access a string element like an array. In this case we are looping through with i as an index. And every time we are checking to see if the current index into to the string is a Space. And if so, we are setting the character at the index into the string to be an underscore. This next example shows how to do the same thing using a pointer that we will increment to point to the next location in memory, and thus the next character in the string.

ptr = mystring;

while(*ptr != NULL) {
  if(*ptr == ' ')
    *ptr = '_';
  ptr++;
}

First we set ptr equal to mystring. So both mystring and ptr are pointers, but they point to the same thing. Putting the * before the pointer variable means you want to get the value stored at the location pointed to... not the number that is the address we're pointing to. There are really three values involved with a pointer. The address of the pointer itself, the address the pointer points to, and the value at the address the pointer points to. So, while the value at the address the pointer is pointing to is not NULL, we loop. Then if the value at the address the pointer is pointing to is a space we put an underscore there instead. Then we just increment the pointer (note the * is not present when we increment the pointer). Remember that because the pointer points to a block of sequential memory, incrementing the pointer makes the pointer point to the next address in the block. And if that's not NULL then it repeats the process.

For character arrays, and int or even long arrays, there isn't much processor speed difference between the two methods. I prefer pointers, and usually when you become comfortable with pointers it feels cleaner and more concise to use a pointer instead of an array index. However, if you have an array of structs, it is very bad to use an array index. To get to the element of the struct array, the processor must first calculate the size of the struct then multiply that by the index number. So if the struct is 8 bytes big, and your index is 5, the start of the 5th element would be 40 bytes offset from the start of the array. If you do this in a loop, the processor must recalculate the size of the array each time it loops. This is just a waste of processor time, that the C64 really can't afford. By making a pointer, the pointer can be incremented, which means that each time the code loops, you are just taking the value of the pointer and adding one pointer size to it. The pointer size is pre calculated, which will save lots of processing power.

There is an even easier way... but the second example above is a perfectly good way to do the search and replace if you didn't know about the function strchr(). strchr() takes a pointer of where to start looking and a character to look for, and returns a pointer to the location of the character, or NULL if it didn't find it. So we could just do this:

ptr = mystring;
while(ptr = strchr(ptr, ' ')) {
  *ptr = '_';
  ptr++;
}

In both of these last two examples, the pointer starts at the beginning of the string, and moves through it until it reaches the end. After which, the ptr variable is not relevant. Because the mystring variable still points to the start of the allocated block. So, if we wanted we could reset ptr to the start of the string with ptr = mystring. And search for something else or whatever. It is very important that we always maintain some way of finding the start of the allocated block.

If you continually lose the location of an allocated block, or you continually allocate new blocks without freeing old ones, then you have what is called a "memory leak." And such memory leaks will eventually lead to all of the computers Memory being needlessly consumed, and wasted. Typically, all of the memory allocated by a single process will be automatically free()'d when that process quits or is killed. But it is still a good habit to get into to free() the memory yourself.

There are other ways to use pointers to manipulate strings in conjunction with the string handling functions built into C. you must #INCLUDE <string.h> to use the string handling functions. Some of these functions include:

	strcmp()		//compares mathematically two strings to see which is greater
	strcasecmp()		//compares strings case insensitively
	strstr()		//finds a small string within a larger string
	sprintf()		//writes a formatted string to a string pointer
	strcpy()		//copies bytes from one string to another
	strncopy()		//copies n bytes from the start location
	strdup()		//allocates memory and dumps a string into it
	strcat()		//concatenates one string to the end of another

These are the most common.



Structures in linked lists

Now that we have a better understanding of pointers, we can look at the ingenious use of pointers and structs to create what is called a linked list. Linked lists are the corner stone of many complicated data organization programs.

One example that clearly demonstrates the power of a linked list, imagine you had a text file that was 900 kilobytes long and you want to make a text editor to edit it. What you could do is just allocate 1800 kilobytes of memory. And if you have a SuperRam card with several megabytes of ram, this might not be impossible. Then you could read in the whole 900k into that great big buffer. BUT, now imagine you want to add a sentence right at the top. Each time you press a key to insert a character, how will you make room for that character? You would have to shunt every single one of those 900,000 characters DOWN one position in the buffer. That might take a few seconds to accomplish. Typing a single word would take half a minute just to make the room in the buffer!! Clearly this is completely unacceptable.

So how do you handle such a large file with any semblance of speed? The answer is to make multiple small buffers, that are linked together in the correct order. If you make a change in one area of the file, you are only shunting the remaining bytes in that small buffer. This can be done so quickly that the user doesn't notice. As the user scrolls through the file, he unknowingly is scrolling from one small buffer to the next one in the "linked list." If the user overflows one of the buffers, you can just create a new buffer and insert it into the list so they fall in the right order.

All we need to know now is, how do we make a linked list? At the end of the first section on structs, I showed you a structure that contains pointers to other structures. Like this:

  
typedef struct linkedstrings_s {
  char * buffer;
  struct linkedstring_s * nextbuffer;
  struct linkedstring_s * prevbuffer;
} linkedbuffer;

Let's say we are going to read in a file that is only 20 bytes. Well, we really only need one buffer... to start with. So we make an instance of the structure:

void main() {

  linkedbuffer * currentbuffer;

  currentbuffer = (linkedbuffer *)malloc(sizeof(linkedbuffer));

  // Open a file here that has 20 bytes of text in it. 

  currentbuffer->buffer = malloc(40);
  
  currentbuffer->nextbuffer = NULL;
  currentbuffer->prevbuffer = NULL;

  fread(currentbuffer->buffer, 1, 20, PreviouslyOpenedFile);

}

Ok. We allocated memory for a buffer struct. And also allocated memory for the string that will be pointed to by this buffer struct. And we set the pointers to the next and previous buffers to NULL. The we read in from the file into the allocated string space.

Now, as the user of our text editor adds characters to the file, he quickly fills up the first buffer. You'd have to keep track of where the user is in the buffer, and how full the buffer is, but that is beyond the scope of this particular article. Once he has filled the buffer, we need another one:

  currentbuffer->nextbuffer = (linkedbuffer *)malloc(sizeof(linkedbuffer));

  currentbuffer->nextbuffer->prevbuffer = currentbuffer;

  currentbuffer = currentbuffer->nextbuffer;

  currentbuffer->nextbuffer = NULL;

  currentbuffer->buffer = (char *)malloc(40);

Examine the above code thoroughly, until you completely understand it before continuing on to other sections. An explanation might be helpful. Because the currentbuffer is a struct, and that struct has a component that is a pointer to another struct, you can use that as a variable for allocating another one. This is what was done on the first line. In the second line you can see that it is possible to use multiple -> arrows to follow pointer to pointer to pointer. Using the first buffer, to reference the second buffer we just created... and then setting that second buffers "prevbuffer" pointer to the current buffer. Essentially you need to give the second buffer a reverse link to the first buffer. But at the time that you do that, you only have direct access to the first buffer. Once you have established the reverse link from the second buffer to the first buffer, you are free to move your current buffer pointer to the second buffer, because you know that you have a link to get back to the first should you need to. And we did this on the 3rd line. Now that our current buffer is in fact the second buffer, all of the first level structure references are affecting the second buffer struct. Like on the 4th line, we set the currentbuffer->nextbuffer to NULL. We are actually setting the second buffer's Next pointer to NULL. Then finally we allocate an additional 40 bytes for the string of the second buffer.

BOOM! We have two structs, each with their own small string buffer, the first points to the second, and the second points to the first. And before the first is NULL, and after the second is NULL.

At this point there is another 40 bytes for the user to continue typing into. But what if the user goes back to halfway through the first and starts adding more there again? This requires the clever manipulation of the next and prev pointers, to insert another buffer between 1 and 2. So that you end up with something like this:

	NULL <-- 1 <--> 2 <--> 3 --> NULL

The reason the NULL on the left doesn't point to the 1, and the NULL on the right doesn't point to the 3 is because, the NULL's are not actual structs, so they don't contain any variables to be used as pointers. 3 simply terminates into NULL, and you must check that the next pointer is not NULL before setting the current pointer equal to it.

If you make a mistake, or overlook one of the pointer changes while you are inserting a new buffer, you might end up with something like this:

	NULL <-- 1 --> 2 <--> 3 --> NULL

This sort of mistake happens, when you tried to insert a new buffer after 1, and before 3. There are a lot of pointers to take care of, 1's Next, 3's Prev, 2's Prev and 2's Next. In this above example, 2's Prev was over looked. It will seem ok when you are moving through the list from 1 to 2 to 3, but when you move back wards from 3 to 2 to ... 2's Previous pointer by default isn't NULL, so it essentially points randomly to somewhere out there in the rest of the computers memory. This sort of mistake often produces colourful results, and will also cause a total computer crash if you try and write any data to that errant pointer.

A text editor is not a simple project. It's way beyond my ability. But linked lists can be used all over the place, for any number of projects that require dynamically adding new elements into an ordered list. They could be people or emails or dates on a calendar.

The struct that makes up a single list item, can be expanded to have a "data" struct pointer. So that each list item can have a pointer to a complete set of variables. So for example, if it was the linked list items were days on a schedule the struct might be somewhat like this:

typedef struct somedata_s {
  char * title;
  char * summary;
  char * description;
} somedata;

typedef struct scheduledate_s {
  struct scheduledate_s * nextdate;
  strcut scheduledate_s * prevdate;

  int date;
  int month;
  int year;

  struct somedata_s * data;  
} scheduledate;

So you would build the linked list by joining the scheduledate structures together, and each one contains base information that describe the date, month and year as numbers, with an extended data component as in the example that follows:

myevent = (scheduledate *)malloc(sizeof(scheduledate));

myevent->date = 25; 
myevent->month = 12;
myevent->year = 2004;

myevent->data = (somedata *)malloc(sizeof(somedata));

myevent->data->title = strdup("CHRISTMAS!!!");

THAT Is pretty cool! There are a near unlimited number of uses for this type of data structuring.



Variable conversion

You may have noticed that before calling malloc() there is always the type of pointer that it is, in brackets. This is a form of what is called "casting." It allows the compiler to run through and in some cases give warnings, because you have specified what it is exactly you want it to do. The compiler can use this information to detect potential problems in the code, before you ever run the program.

In some cases, you want to take a long (32 bit integer) and store it in an int (16 bit integer). Normally you can't do that, because the long value could potentially be much larger than the int value. However, certain operations that you do to the code might make you as the programmer 100% sure that the long contains a value small enough that it will fit into an int without overflowing. So you want to force the conversion of the long to an int. You can "caste" the long as an int and the compiler will let you do that. Like this:

char n;
int i;

i = SomeRandomNumber();

n = (char)(i/1024);

i might be a value in the 10,000s but when it is divided by 1024, we know it is in the range of 1-255. So we know there is no problem storing it in the char n. But without casting the (i/1024) as a (char) the compiler will give us an error about the left and right sides of the = not being the same variable type.

Another very useful type of variable conversion is converting strings of numbers written in ascii to their equivalent as integer numbers. For example:

char * astring;
int anumber;

astring = strdup("12345");

anumber = atoi(astring);

atoi() stands for Ascii to Int. And will take the ascii string of numbers "12345" and convert it to an int 12345. As an int it can undergo mathematical transformations, for example, you can divide it or add it with another number. To convert an int back to a string you use sprintf(), like so:

sprintf(astring, "%d", anumber);

There are a few additional functions used for handling longs instead of ints. Aswell, when using atoi it only converts the first few string characters to the number. To convert an entire string you must use strtol() or strtoi(). In summary:

	atoi()		//a few chars to an int
	strtoi()	//a complete string to an int
	atol()		//a few chars to a long
	strtol()	//a complete string to a long

The formatting characters in a formatting string given to sprintf():

	%d		signed int
	%ld		signed long



Some Quick math tips

When you are doing math with ints or longs there are a few shortcuts:

	i = i + 1;	//	i++;
	i = i - 1;	//	i--;
	i = i + 2;	//	i += 2;
	i = i - 2;	//	i -= 2;
	i = i * 2;	//	i *= 2;
	i = i / 2;	//	i /= 2;

In the list above the 2's could be any number really. So if you just have a single variable like i, and you want to have it equal to itself multiplied by 12, you can just i *= 12;, and it will be set to itself times 12. These tips are just handy for saving you a bunch of unnecessary typing.

A way to save processor time, if you are dividing by 2 or multiplying by 2, you can do a bit shift:

	i >> 1;		//	for a divide by 2
	i << 1;		//	for a multiply by 2

It's possible you may loose a bit of accuracy (literally 'a bit') when you do the bit shift. But that's the nature of doing math on a digital computer. And it's really damn fast. I have been informed by Jolz, that in fact, the compiler is intelligent enough to convert divisions and multiplications by 2 to use bit shifts. So it isn't really necessary to do this manually. However, for those that like to know how the math is being done, you can do Math quite manually in C if you desire to do so.



Adjusting and manipulating the console

To Get away from some of the more low level stuff, let's look at how we can change the settings of the console for making more powerful console programs.

To do anything with the console, when compiling you must link the program to the console shared library. So the compile string should include -lconlib. And the code should #INCLUDE <console.h> to make use of the conlib's functions.

When you first want to clear out the console settings and set them for scratch you issue con_init() and when your program is finished it should issue a con_end(). This will revert the console to its old settings.

There is a structure for the terminal I/O, for which you must make an access variable. You do this by putting 'struct termios tio' somewhere in the code, preferably a global variable so any function can access this termios struct. Then you use a function gettio() to fill your tio struct with the current terminal settings. You can then manually change the tio struct variables, and use the function settio() to write them back to the terminal. There is an int in the termios struct called "flags", in which each bit of the int controls a different behaviour of the terminal. The console.h file has defined some keywords with the numbers that will flip the bits of the tio.flags bits for you. Here is an example.

struct termios tio;

void main() {
  
  con_init();

  gettio(STDOUT_FILENO, &tio);

  tio.flags |= TF_ECHO | TF_ICRLF;
  tio.MIN = 1;

  settio(STDOUT_FILENO, &tio);

  con_end();

} 

What this did was, tio.flags |= TF_ECHO | TF_ICRLF; will toggle the ICRLF and the ECHO flags ON. So the console will echo the characters that you type. And will handle the carriage return linefeeds like Unix does. MIN means the console requires a minimum of 1 character to be submitted before it proceeds. Then those settings are written to the terminal.

Suppose you wanted to ask the user for a password. And you wanted them to type in their password, but you didn't want the characters to be echoed to the screen. You could issue the following:

  gettio(STDOUT_FILENO, &tio);
  tio.flags ^= TF_ECHO;
  settio(STDOUT_FILENO, &tio);

The ^= is an exclusive OR, so all the bits would stay as they are, except ECHO would be forced OFF. When the program is finished taking the password (with it not echoing), you just issue:

  gettio(STDOUT_FILENO, &tio);
  tio.flags |= TF_ECHO;
  settio(STDOUT_FILENO, &tio);

And the ECHO is turned back on. The Other bits that can be toggled are:

	TF_ICANON
	TF_IGNCR
	TF_ECHO
	TF_ECHONL
	TF_OPOST
	TF_ISIG
	TF_ICRLF

NOTE: Since the writing of this article, before I had a chance to put it up on the web, 3 functions have been added to the console library:

con_modeon(flags);
con_modeoff(flags);

and

con_modeonoff(flags,flags);

Which will do all the gettio() and settio() stuff for you. Using these functions, if you want to turn on ECHO, you simply issue: con_modeon(TF_ECHO); That's pretty darn simple. Power to shared libraries!

Turning on ICANON enables a line edit mode, so that you can type several characters before pressing return to have them submitted. Turning ICANON off puts the console into a single character input mode. Single character mode is most often used for taking menu option choices from the user. So they don't need to press return to submit the option.

When the con_init() has been run, there will be variables con_xsize and con_ysize which are equal to the number of columns and rows available on the console. These numbers can be used to figure out if you are on the 80 column console or the 40 column console, or, in the future, what size the console is if the user resizes it inside a GUI window. And you can write your code so that the program draws itself properly based on the height and width of the console.

You can freely position the cursor with the function con_gotoxy(); And giving it x (column) and y (row) character co-ordinates.

You can clear the whole screen with con_clrscr();

You can clear a line, with con_clrline(); con_clrline() takes one of three types, either LC_Start, LC_End, or LC_Full. LC_Start clears from the current cursor position to the start of the line. LC_End from the current cursor position to the end of the line. And LC_Full will clear the entire line that the cursor is on.

All these screen changes, including printing to the screen, after issuing con_init(), will not update the display until you issue, con_update(); This is a VERY good thing. If you are going to make many changes to the display, drawing the changes one at a time would be very slow. So you make many changes, and the console keeps track of them, then when you issue con_update() it draws the final result the way it should look. This makes scrolling and clearing and printing many things to the screen appear fast and instant.

You can set the foreground and background colours of the console with con_setfg() and con_setbg(). The values give to these functions are defined as follows:

	0	COL_Black
	1	COL_White
	2	COL_Red
	3	COL_Cyan
	4	COL_Purple
	5	COL_Green
	6	COL_Blue
	7	COL_Yellow
	8	COL_Orange
	9	COL_Brown
	10	COL_Pink
	11	COL_DarkGrey
	12	COL_MedGrey
	13	COL_LightGreen
	14	COL_LightBlue
	15	COL_LightGrey

But it should be noted that the colours on the console follow the standard ANSI colour schemes. Which means that not all 16 colours can be used, has been my experience.

The final important thing to know for easy formatting of printed text to the console, when you use a printf() function, you give it a "formatting" string, and then the variables that will plug into the format string. A typical printf() would look like this:

  printf("The number %d is greater than %d", 5, 4);

The %d will be substituted with a number, the occurrence of the % format items in the formatting string are replaced respectively by the variables that follow the format string. However, if you are going to print out 20 of these lines, and some of the numbers are going to be 2 or 3 or even 4 digits, we want all off the text sections to line up above one another. To do this, the format elements can contain a number, which is the minimum number of spaces to take up. If the value to fill in is less than the minimum, the value is padded with spaces. So... look at this example:

for(i=9; i<13; i++)
  printf("test %2d test\n", i);

This would result in the following output:

test  9 test
test 10 test
test 11 test
test 12 test

Notice how the 9, that is less than 2 spaces, is padded with a space, so that all of the second test's line up perfectly.

That's all for now!! There is plenty more to learn. And eventually I'll get to writing articles on programming more about the GUI, and also some more tips and examples. How to program with XML and How to use Lots of great tools and services, in your programs, that WiNGs has to offer.

Including IPC, internal Piping, Threads, Spawn, Directory working, Channels and Message loops.