Skip to content

Instantly share code, notes, and snippets.

@memreflect
Created July 22, 2020 04:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save memreflect/128ee7052cc0f84faa1cf80254e7a5bc to your computer and use it in GitHub Desktop.
Save memreflect/128ee7052cc0f84faa1cf80254e7a5bc to your computer and use it in GitHub Desktop.
Bounded line input routine `get_line`
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "errval.h"
static char const* errstrings[] = {
[ErrOK] = "no error",
[ErrIO] = "I/O error",
[ErrMore] = "more data available",
[ErrParam] = "invalid parameter",
//[ErrSys] = strerror(errno),
[ErrMax] = "unknown error",
};
char const*
errstr(errval err) {
if (err == ErrSys) {
return strerror(errno);
} else if (err > ErrMax) {
err = ErrMax;
}
return errstrings[err];
}
void
errprint(errval err, char const* msg) {
if (msg) {
fprintf(stderr, "%s: ", msg);
}
fprintf(stderr, "%s\n", errstr(err));
}
#ifndef ERRVAL_H
#define ERRVAL_H 1
#ifdef __cplusplus
extern "C" {
#endif
/*
* Error constants
*/
typedef enum {
ErrOK,
ErrIO,
ErrMore,
ErrParam,
ErrSys,
ErrMax,
} errval;
/*
* Return a string description of an error
*
* If `err` is `ErrSys`, `strerror(errno)` is returned.
*/
char const* errstr(errval);
/*
* Print a string description of an error to `stderr`
*
* If `msg` is not a null pointer, it is printed before the error
* description, followed by a space and a colon like `perror`.
*/
void errprint(errval err, char const* msg);
#ifdef __cplusplus
}
#endif
#endif /* !ERRVAL_H */
#include <stdio.h>
#include "errval.h"
/*
* Read a line from standard input
*
* Upon return, successful or not, the `buffer` argument contains a
* null-terminated string that is `size-1` characters or less in length,
* and the `len` parameter, if not a null pointer, will contain the
* length of that string.
*
* A newline character, if read, is discarded and causes the function to
* return as if `EOF` was encountered.
*
* The return value is a value suitable for use with `errstr` or
* `errprint`.
*
* Errors:
* ErrOK - No error
* ErrIO - I/O error (only used if ErrSys is not returned instead)
* ErrMore - line was longer than `size-1` characters, excluding \n
* ErrParam - buffer is a null pointer, or size is less than 2
* ErrSys - error is stored in errno
*/
errval
get_line(char* restrict buffer, size_t size, size_t* restrict len) {
// size == 0 cannot store any characters.
// size == 1 can only store a null terminator.
if (!buffer || size < 2) {
return ErrParam;
}
int c;
for (size_t i = 0; i < size; i++) {
c = getchar();
if (c == '\n' || c == EOF) {
buffer[i] = 0;
if (len) {
*len = i;
}
if (ferror(stdin)) {
if (errno) {
return ErrSys;
}
return ErrIO;
}
return ErrOK;
}
buffer[i] = c;
}
// If we're outside the loop, then we didn't read a full
// line. This means we don't have room for a null terminator,
// so we need to push the last char back onto stdin.
ungetc(c, stdin);
buffer[size-1] = 0;
if (len) {
*len = size-1;
}
return ErrMore;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment