# Streams

## Need for many channels for IO
The next problem is not only about debugging but also motivates `stderr` stream.

:::::{activity} An error in a file processing program
:label: an-error-in-a-file-processing-program
The following code leads to a segmentation fault (`SIGSEGV`). 
1. Use the debugger to see what is the problem. 

   How would you debug it, where would you put your breakpoints?
1. How would you fix the problem?

```{literalinclude} ../code-error/file-opening-error.c
:language: c
```

````{dropdown} Hint
If you found the error source:

For informative error messages use [`perror("")`](cref:perror). It prints the last occurred error.

Moreover you can also add a message that will be additionally printed. For example, in case a file cannot be found, `perror("File cannot be opened")` will print to `stderr`:

```text
File cannot be opened.: No such file or directory
```
````
:::::

<!--
I have to indtroduce perror etc
-->


## Stream

:::{wpd} standard streams
preconnected input and output communication channels between a program and its environment when it begins execution.
:::


:::{list-table} each file is a stream of bytes until `end-of-file` (`EOF`). Example for a file that contains the word `leverpostej`
* - 0
  - 1
  - 2
  - 3
  - ...
  - n - 1
  - end-of-file
* - l
  - e
  - v
  - e
  - ...
  - j
  - 
:::

## Three streams

:::{commons-figure} https://commons.wikimedia.org/wiki/File:Stdstreams-notitle.svg
:align: right
:figwidth: 50%
a text terminal, the program in execution (process), the streams standard input `stdin`, standard output `stdout`, standard error `stderr`.
:::

- we used `stdin` and `stdout` before.
  - `scanf()` and `printf()`
- `stderr` channel is useful for separating the actual data from *status messages*. 

<br>
<br>
<br>
<br>
<br>
<br>

## `stderr`'s purpose

Most shell tools use `stderr` stream for status messages:

:::{command-output} curl -LH "Accept: text/plain" icanhazdadjoke.com | tr '[:lower:]' '[:upper:]' | sed -E 's/^/👉  /; s/$/ 🤣/'
:shell:
:::

In the example above, we see a *pipe* of three commands. Most terminal shells support pipes.
In a pipe, the output of a previous command is used for the next command. In the command above:
1. `curl` downloads a dad joke as text.
1. `tr` translates lowercase characters to UPPERCASE.
1. `sed` wraps the string with two emojis.

You see that the last two commands are applied to the actual data – the dad joke, which is sent through `stdout`, and not to the status data – the download status, which is sent through `stderr`.

Therefore `perror()` command in [the previous activity](
an-error-in-a-file-processing-program) uses `stderr`.

:::{card} 🥡 Takeaway
Write error or status messages to `stderr`, especially if your program outputs actual data to `stdout`.
:::


:::{card} ⚡ Live programming
Processing the CSV file from the problem.
:::


## Appendix


## Checking and resetting stream status

If reading function return an `EOF`, this can be due to two reasons:
1. end of file reached
1. there has been an error

If either of them has happened, the status must be cleared.

:::{list-table}
:header-rows: 1
* - function
  - purpose
* - `int feof(FILE *stream)`{l=c}
  - returns `EOF` indicator, i.e., non-zero if `EOF` reached
* - `int ferror(FILE *stream)`{l=c}
  - returns error indicator, e.g., hardware error
* - `int clearerr(FILE *stream)`{l=c}
  - clears EOF and error indicators
:::


Only Use these to understand why a reading or writing operation failed.

:::{literalinclude} ../code/feof.c
:language: c
:::
:::{literalinclude} ../code/feof.txt
:language: text
:::



:::{card} 🕳️ Pitfall
When I learned first about `feof`, I thought `feof` is good for using it in a control loop as follows:

```c
#include <stdio.h>
#define LINE_MAX 80

char line[LINE_MAX];

int main() {
  auto fp = fopen("temperatures.txt", "r");
  while (!feof(fp))
    puts(fgets(line, LINE_MAX, fp));
}
```
This is wrong. First we have to attempt reading and then we should use `feof`. This is an indicator or flag which is set after the first attempt.

`feof` (and `ferror`) are probably designed to know the reason why a previous read operation have not succeeded. Read functions like `fgets` and `fgetc()` return `nullptr` and `EOF` respectively, if nothing can be read. The reason for the `EOF` can also be an error, which I believe misled me. Both end-of-file and an error can be the reason for not being able read. Only after using `feof` or `ferror` can we know the reason.

Details: [Why is `while(!feof(file))`{l=c} always wrong?](https://stackoverflow.com/questions/5431941/why-is-while-feoffile-always-wrong#26557243)
:::
