Input Line Editor with custom I/O routines

From Nutwiki
Jump to: navigation, search

Overview

This example showcases how to use custom I/O functions with the input line editor (EdLine) functionality that Nut/OS provides. In order to work with this example, please familiarize yourself with the basic workings of EdLine in the Input Line Editor example as the functions discussed there won't be explained again.

You may be wondering what is different to the first example provided... actually not much. It still takes input and outputs the correct stuff. The main difference is that this time around we don't use stdin and stdout for that, instead we work with filepointers and a custom routine to read/write them. This would allow us to use just about any function that returns a filepointer, like sockets or data from a file.

There are many more ways to use the custom I/O routines, it is not limited to filepointers.

Source Code

<source lang="c">

  1. include <stdio.h>
  2. include <io.h>
  1. include <dev/board.h>
  1. include <gorp/edline.h>
  1. define LINE_MAXLENGTH 100

EDLINE *el; char *buf;

static int CustomInput(void *param) {

   return fgetc(param);

}

static int CustomOutput(void *param, int ch) {

   return fputc(ch, param);

}

int main(void) {

   u_long baud = 115200;
   FILE *uart_out = NULL;
   FILE *uart_in = NULL;
   NutRegisterDevice(&DEV_UART, 0, 0);
   uart_out = fopen(DEV_UART_NAME, "w");
   uart_in = fopen(DEV_UART_NAME, "r");
   _ioctl(_fileno(uart_out), UART_SETSPEED, &baud);
   fputs("\nNut/OS EdLine Demo", uart_out);
   fputs("\nPlease enter some text: ", uart_out);
   buf = malloc(LINE_MAXLENGTH);
   el = EdLineOpen(EDIT_MODE_ECHO);
   EdLineRegisterInput(el, CustomInput, uart_in);
   EdLineRegisterOutput(el, CustomOutput, uart_out);
   EdLineRead(el, buf, LINE_MAXLENGTH);
   EdLineClose(el);
   fprintf(uart_out, "You entered: %s", buf);
   for (;;) {
   }
   return 0;

} </source>

Details

Let's first look at the main function again and save the IO functions for later.

<source lang="c"> int main(void) {

   u_long baud = 115200;
   FILE *uart_out = NULL;
   FILE *uart_in = NULL;
   NutRegisterDevice(&DEV_UART, 0, 0);
   uart_out = fopen(DEV_UART_NAME, "w");
   uart_in = fopen(DEV_UART_NAME, "r");

</source>

First we declare 2 new file pointers uart_out and uart_in which will serve as our input and output streams. Those will take the role that stdin and stdout had in the previous examples.

Then we register the UART device and assign it to uart_out in write mode and to uart_in in read mode. This is almost the same as when we were using stdin and stdout but with open instead of reopen.

<source lang="c"> _ioctl(_fileno(uart_out), UART_SETSPEED, &baud); fputs("\nNut/OS EdLine Demo", uart_out); fputs("\nPlease enter some text: ", uart_out); </source> Setting the BAUD rate is as important as ever. Again, we simply replace stdout by uart_out.

Since we don't use stdin and stdout at all, puts() and similar function won't work anymore.

<source lang="c"> /* normal puts */ puts("hello world");

/* is basically the same as this */ fputs("hello world", stdout); </source>

Since we simply replaced stdout with uart_out, we can also do the same for our "normal" puts().

<source lang="c"> EdLineRegisterInput(el, CustomInput, uart_in); EdLineRegisterOutput(el, CustomOutput, uart_out); </source>

After creating the input line editor we have these 2 new lines. They register our custom I/O functions which will, from this point onwards, replace the ones that EdLine uses by default.

EdLineRegisterInput takes 3 parameters: A pointer to the EdLine we want to use, which is el of course. Second is the address of the custom input function. Entering the name of a function without the parentheses and parameters represents the address so we just use that here. Finally, we pass a pointer to our parameters.

Why do we do that? Because if we wrote CustomInput(uart_in) it would not pass the function address but instead evaluate the expression and return whatever the function returns. Which is clearly not what we want, right?

The EdLineRegisterOutput function is pretty much the same. It takes the CustomOutput function and uart_out as parameters but besides that it works in exactly the same way.

<source lang="c"> fprintf(uart_out, "You entered: %s", buf); </source>

Same as above: stdout doesn't exist so we use fprintf instead of printf. You could use this the same way for stdout.

Now for those custom functions...

<source lang="c"> static int CustomInput(void *param) {

   return fgetc(param);

} </source>

That's all there is to it? Well... yes. The function takes one parameter, the pointer to the parameters we want to pass. Since we only pass one parameter, we can read param as if it were passed directly. If you wanted to pass multiple parameters, you'd have to fill them into a structure first and pass a pointer to that as parameter.

The function itself simply reads a single character from the filepointer passed to it and returns that. If you look into the EdLine source you'll see that it is not much different from the default functions except that it takes a parameter instead of expecting stdin to be the stream we want to read from.

This function gets called for every single character we want to read.

<source lang="c"> static int CustomOutput(void *param, int ch) {

   return fputc(ch, param);

} </source>

The CustomOutput function is not much different. It takes a pointer to the parameters and an int, ch.

ch is a single character that is supposed to be output to the input line editor, hence the name. In case you're wondering where that parameter is obtained from... Nut/OS passes it to the function along with our parameter pointer to save us the hassle of obtaining it manually. Thanks, Nut/OS!

The insides of the function are pretty unspectacular. We use fputc to print the character to our file, returning whatever fputc returns. That's it.

Feel free to add to this example, as it appears to be not too exciting but allows for many, many possibilities.

Some ideas to get your mind going:

  • Network Input Line Editor
  • Logging the keystrokes and/or output to file in addition to processing it
  • Replaying input/output from a file
  • Relaying the commands from one interface to another or even across network nodes

It's a pretty neat functionality, actually.

Output

<source lang="text"> Nut/OS EdLine Demo Please enter some text: I'm a cool custom I/O example! You entered: I'm a cool custom I/O example! </source>