1

We just started the topic of low level functions in C and for a part of this assignment we have to ask for user input. Normally I would use printf() and scanf() however for this assignment we are only allowed to use read(), write(), open(), close(), and lseek().

My question is how would you read input from the keyboard after printing to the screen? I understand that I would have to use read, the file descriptor would be STDIN_FILENO, however how would I determine the size count? Also how would I keep track of what the user inputted? Would I have to create an char array just for that?

Also if anyone could reference some good reading material or tutorials for programming with low level functions that would help a lot.

user3538161
  • 387
  • 1
  • 4
  • 6
  • The simplest way is probably to read one character at a time until you get a newline, appending the characters to an array. – Some programmer dude Sep 26 '15 at 01:07
  • if you want to read from stdin, wouldn't you need to specify the size of the array before hand? I get what you mean but I don't know how I would implement it. – user3538161 Sep 26 '15 at 01:14
  • There really isn't much difference between using `read/write` to do input/output and using `getchar/printf`. The primary difference is you do not enjoy the luxury of formatted output or a variadic print function. You have to do the formatting by writing each piece of output to `stdout` as you want it to appear. If you are still stuck let me know. You use read on `stdin` just like any file. You declare a buffer (array) and read from `stdin` into the buffer. The return from `read` gives you the number of characters successfully read. – David C. Rankin Sep 26 '15 at 05:47

2 Answers2

2

Reading char by char would be bad for performance. System calls are kind of expensive. Most usually you want some kind of buffer (malloced, static memory, on the stack). The size (once it's past certain size) doesn't really matter that much.

If your fd 0 is a terminal in cooked mode, you will get a line on each call to read (which will usually fail to fill your entire buffer). What's important to realize is that a read request for N bytes doesn't need to return N bytes and a return of less than N bytes doesn't need to mean an IO error. If it's a disk-based file, then your read request for the size of your buffer will tend to get fullfilled fully.

Advanced Programming in the Unix Environment by Richard Stevens is a good book on this. Then of course, the man pages for the system calls.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
1

Continuing from my earlier comment, there is really little difference between reading and writing to stdin using read and write and using higher level functions like fgets and printf. The primary difference is you cannot rely on the format string provided by the variadic printf, and with read, you are responsible for making use of the return to know how many characters were actually read.

Below is a short example showing the basics of reading input from stdin with read and then writing that information back with write (note: there are additional checks you should add like checking that the number of characters read is less than the size of the buffer to know if more characters remain to be read, etc...) You can always put much of the prompt and read into a function to ease repetitive use.

With write, just remember, the order you write things to stdout is the format string. So simply make logical calls to write to accomplish the formatting you desire. While you are free to use the STDIN_FILENO defines, you can also simply use 0 - stdin, 1 - stdout and 2 - stderr:

#include <unistd.h>

#define MAXC 256

int main (void) {

    char buf[MAXC] = {0};
    ssize_t nchr = 0;

    /* write the prompt to stdout requesting input */
    write (1, "\n enter text  : ", sizeof ("\n enter text  : "));

    /* read up to MAXC characters from stdin */
    if ((nchr = read (0, buf, MAXC)) == -1) {
        write (2, "error: read failure.\n", sizeof ("error: read failure.\n"));
        return 1;
    }

    /* test if additional characters remain unread in stdin */
    if (nchr == MAXC && buf[nchr - 1] != '\n')
        write (2, "warning: additional chars remain unread.\n",
            sizeof ("warning: additional chars remain unread.\n"));

    /* write the contents of buf to stdout */
    write (1, "\n text entered: ", sizeof ("\n text entered: "));
    write (1, buf, nchr-1);
    write (1, "\n\n", sizeof ("\n\n"));

    return 0;
}

Compile

gcc -Wall -Wextra -o bin/read_write_stdin read_write_stdin.c

Output

$ ./bin/read_write_stdin

 enter text  : The quick brown fox jumps over the lazy dog!

 text entered: The quick brown fox jumps over the lazy dog!
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • I'm a little confused with write. Doesn't the second argument have to be a pointer to a buffer? How are you able to pass a string? Also doesn't read return 0 when it reaches the end of the file? so wouldn't nchr = 0? – user3538161 Sep 26 '15 at 22:54
  • It is a pointer to a buffer. The buffer is declared as a static array `buf[MAXC]`. However, when passed as a parameter, the array *decays* to a pointer. This is the same reason you can write `int main (int argc, char *argv[])` or `int main (int argc, char **argv)`. You can read all you want about the topic, simply search **C pointer decay**. I simply chose a static array to keep the example simple rather than choosing to dynamically allocate a pointer with `malloc` and distracting from the `read` `write` logic. (`calloc` would be a better choice). – David C. Rankin Sep 27 '15 at 00:11