Skip to content

Instantly share code, notes, and snippets.

@clausecker
Created January 4, 2018 00:46
Show Gist options
  • Save clausecker/e4177a30ba6b047e749dd08288d177c1 to your computer and use it in GitHub Desktop.
Save clausecker/e4177a30ba6b047e749dd08288d177c1 to your computer and use it in GitHub Desktop.
/*-
* Copyright (c) 2014, 2016 Robert Clausecker
*/
/*
* util.h - various utility functions
*/
#ifndef UTIL_H
#define UTIL_H
/*
* Return values of yesno()
*/
enum {
YN_YES = 0, /* affirmative response */
YN_NO = 1, /* negative response */
YN_RETRY = 2, /* neither affirmative nor negative */
YN_ERROR = -1 /* an error occured */
};
extern int yesno(void);
#endif /* UTIL_H */
/*-
* Copyright (c) 2016 Robert Clausecker
*/
/*
* yesno.c - Get a yes/no answer from the user
*/
#define _POSIX_C_SOURCE 200809L
#include <langinfo.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include "err.h"
#include "util.h"
static int regex_helper(regex_t *restrict, const char *restrict, const char *restrict);
/*
* Get a line of text from stdin and compare it against YESEXPR and
* NOEXPR. If one of them matches, return YN_YES or YN_NO. If neither
* matches, return YN_RETRY. If an error occurs, return YN_ERROR.
* An error message will be printed in this case, the value of errno
* is not significant.
*/
extern int
yesno(void)
{
static regex_t yesregex, noregex;
static size_t inputlen = 0;
static int haveregexes = 0; /* 0: no, 1: yes, -1: failed before */
static char *inputbuf = NULL;
if (haveregexes != 1) {
if (haveregexes == -1)
return (YN_ERROR);
/* use POSIX-locale expressions as fallback */
if (regex_helper(&yesregex, nl_langinfo(YESEXPR), "^[yY]")
|| regex_helper(&noregex, nl_langinfo(NOEXPR), "^[nN]")) {
haveregexes = -1;
return (YN_ERROR);
}
haveregexes = 1;
}
if (getline(&inputbuf, &inputlen, stdin) < 0) {
if (ferror(stdin))
/* LOCALIZE */
warn("Error reading stdin");
else
/* LOCALIZE */
warnx("End of file on stdin");
return (YN_ERROR);
}
/* prefer NOEXPR in the unlikely case that both match */
if (regexec(&noregex, inputbuf, 0, NULL, 0) == 0)
return (YN_NO);
if (regexec(&yesregex, inputbuf, 0, NULL, 0) == 0)
return (YN_YES);
return (YN_RETRY);
}
/*
* Compile the regular expression expr, if that fails, compile expr2
* instead. Print an error message if compiling either failed. Return
* 0 on success, -1 on failure.
*/
static int
regex_helper(regex_t *restrict regex, const char *restrict expr, const char *restrict expr2)
{
size_t errlen;
int compresult;
char *errbuf, errbuf_nomem[256];
compresult = regcomp(regex, expr, REG_NOSUB);
if (compresult == 0)
return 0;
/* try to print an error message */
errlen = regerror(compresult, regex, NULL, 0);
errbuf = malloc(errlen);
if (errbuf == NULL) {
/* do something meaningful anyway */
errbuf = errbuf_nomem;
errlen = sizeof errbuf_nomem;
}
regerror(compresult, regex, errbuf, errlen);
/* LOCALIZE */
warnx("Error compiling regular expression %s: %s", expr, errbuf);
if (errbuf != errbuf_nomem)
free(errbuf);
if (expr2 != NULL)
return (regex_helper(regex, expr2, NULL));
else
return (-1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment