/* See LICENSE file for copyright and license details. */
#include "libhaiku.h"
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define H(...) return random_haiku(__VA_ARGS__, NULL)
/* Synonyms. */
#if defined(EDEADLOCK) && !defined(EDEADLK)
# define EDEADLK EDEADLOCK
#endif
#if defined(EOPNOTSUPP) && !defined(ENOTSUP)
# define ENOTSUP EOPNOTSUPP
#endif
/**
* Pick a random integer in [0, `n`[
*
* @param n The largest allowed return value plus one
* @return A random integer in [0, `n`[
*/
static int
random_int(int n)
{
static int initialised = 0;
double r;
int ri;
if (!initialised) {
srand((unsigned)time(NULL));
initialised = 1;
}
ri = rand();
r = (double)ri * (double)n / ((double)RAND_MAX + (double)1);
ri = ((int)r) % n;
return ri < 0 ? (ri + n) : ri;
}
/**
* Pick a random haiku
*
* @param str... `NULL`-terminated list of haiku
* Must contain at least one haiku
* @return One of the haiku, randomly selected
*/
#ifdef __GNUC__
__attribute__((__sentinel__))
#endif
static const char *
random_haiku(const char *str, ... /*, NULL */)
{
int n = 1;
const char *s = str;
va_list args;
va_start(args, str);
while (va_arg(args, const char *))
n++;
va_end(args);
if (n == 1)
return str;
n = random_int(n);
va_start(args, str);
while (n--)
s = va_arg(args, const char *);
va_end(args);
return s;
}
/**
* Get a random generic poetic error message
*
* @return A random generic poetic error message
*/
const char *
libhaiku_generic(void)
{
H("Error messages\n""cannot completely convey.\n""We now know shared loss.\n",
"Errors have occurred.\n""We won't tell you where or why.\n""Lazy programmers.\n",
"To have no errors.\n""Would be life without meaning.\n""No struggle, no joy.\n",
"There is a chasm\n""of carbon and silicon\n""the software can't bridge.\n",
"Beauty, success, truth\n""He is blessed who has two.\n""Your program has none.\n",
"Technical support\n""would be a flowing source of\n""sweet commiseration.\n");
}
/**
* Get a poetic error message
*
* @param errnum `errno` value that the error message shall be selected for
* @param genericp Unless `NULL`, will be set to 1 if the function didn't have
* any haikus specific the the specified error, and had to
* return a generic haiku, and to 0 otherwise
* @return A poetic error message
*/
const char *
libhaiku_strerror(int errnum, int *genericp)
{
if (genericp)
*genericp = 0;
switch (errnum) {
#ifdef ENETDOWN
case ENETDOWN:
H("Stay the patient course.\n""Of little worth is your ire.\n""The network is down.\n",
"Your vast achievements\n""are now only dreams.\n""The network is down.\n");
#endif
#ifdef ERFKILL
case ERFKILL:
H("The action you took\n""severed hope of connection\n""with the Internet.\n");
#endif
#ifdef EAGAIN
case EAGAIN:
#endif
#ifdef ENFILE
case ENFILE:
#endif
#ifdef EMFILE
case EMFILE:
#endif
#ifdef EUSERS
case EUSERS:
#endif
#ifdef EMLINK
case EMLINK:
#endif
#if defined(EAGAIN) || defined(ENFILE) || defined(EMFILE) || defined(EUSERS) || defined(EMLINK)
H("ABORTED effort:\n""Close all that you have.\n""You ask way too much.\n",
"The code was willing\n""It considered your request\n""But the chips were weak.\n");
#endif
#ifdef ENOMEM
case ENOMEM:
H("I'm sorry, there's... um...\n""insufficient... what's-it-called?\n""The term eludes me...\n");
#endif
#ifdef ENOSPC
case ENOSPC:
#endif
#ifdef ENOSR
case ENOSR:
#endif
#ifdef ENOBUFS
case ENOBUFS:
#endif
#ifdef EDQUOT
case EDQUOT:
#endif
#if defined(ENOSPC) || defined(ENOSR) || defined(ENOBUFS) || defined(EDQUOT)
H("Out of memory.\n""We wish to hold the whole sky,\n""But we never will.\n");
#endif
#ifdef ENOANO
case ENOANO:
#endif
#ifdef ENOENT
case ENOENT:
#endif
#if defined(ENOANO) || defined(ENOENT)
H("With searching comes loss\n""and the presence of absence:\n""'My Novel' not found.\n",
"Rather than a beep\n""Or a rude error message,\n""These words: “File not found.”\n",
"Three things are certain:\n""Death, taxes, and lost data.\n""Guess which has occurred.\n",
"Having been erased,\n""The document you're seeking\n""Must now be retyped.\n",
"Everything is gone.\n""Your life's work has been destroyed.\n""Squeeze trigger (yes/no)?\n",
"Spring will come again,\n""But it will not bring with it\n""Any of your files.\n");
#endif
#ifdef EOWNERDEAD
case EOWNERDEAD: /* Reusing haiku from ENOENT. */
H("Three things are certain:\n""Death, taxes, and lost data.\n""Guess which has occurred.\n");
#endif
#ifdef EMSGSIZE
case EMSGSIZE:
H("A file that big?\n""It might be very useful.\n""But now it is gone.\n");
#endif
#ifdef EHWPOISON
case EHWPOISON:
H("Yesterday it worked.\n""Today it is not working.\n""Windows is like that.\n");
#endif
#ifdef EUCLEAN
case EUCLEAN:
#endif
#ifdef ENOTRECOVERABLE
case ENOTRECOVERABLE:
#endif
#if defined(EUCLEAN) || defined(ENOTRECOVERABLE)
H("Chaos reigns within.\n""Reflect, repent, and reboot.\n""Order shall return.\n");
#endif
#ifdef EHOSTDOWN
case EHOSTDOWN:
H("Windows NT crashed.\n""I am the Blue Screen of Death.\n""Noone hears your screams.\n",
"Won't you please observe\n""a brief moment of silence\n""For the dead server?\n");
#endif
#ifdef EBFONT
case EBFONT:
H("First snow, then silence.\n""This thousand dollar screen dies\n""so beautifully.\n");
#endif
#ifdef EFAULT
case EFAULT:
H("A crash reduces\n""your expensive computer\n""to a simple stone.\n",
"Seeing my great fault.\n""Through a darkening red screen.\n""I begin again.\n",
"Memory shaken,\n""the San Andreas of all\n""invalid page faults.\n");
#endif
#ifdef EINVAL
case EINVAL:
H("Something you entered\n""transcended parameters.\n""So much is unknown.\n",
"Some incompetence\n""fundamentally transcends\n""mere error message.\n");
#endif
#ifdef EDEADLK
case EDEADLK:
H("From formless chaos,\n""each thread seeks resolution.\n""A race condition.\n");
#endif
#ifdef EBADMSG
case EBADMSG:
H("Many fingers clicking.\n""Screens are full of letters.\n""What is their meaning?\n");
#endif
#ifdef ELOOP
case ELOOP:
H("Linkage exception.\n""Code has looped upon itself\n""like the coiled serpent.\n");
#endif
#ifdef ECHILD
case ECHILD:
H("A futile, grim reap.\n""You will have to realise that,\n""you've no children left.\n");
#endif
#ifdef EPIPE
case EPIPE:
H("Your pipe is broken.\n""Code in watery ruins.\n""Machines short circuit.\n");
#endif
#ifdef EACCES
case EACCES:
H("Touching others' files?\n""Can't keep your hands to yourself?\n""Permission denied.\n");
#endif
#ifdef EINTR
case EINTR:
H("Call interrupted?\n""Why do you not post a sign:\n""Disturb. Risk your life!\n");
#endif
#ifdef EPERM
case EPERM:
H("Caution to the wind.\n""You should always run as root.\n""She can do anything.\n");
#endif
default:
if (genericp)
*genericp = 1;
return libhaiku_generic();
}
}
/**
* Print a poetic error message
*
* @param prefix Unless `NULL` or empty, each line will be prefixed
* by the specified string followed by a colon and a space
* @param errnum `errno` value that the error message shall be selected for;
* if negative, a generic error message is printed
*/
void
libhaiku_perror2(const char *prefix, int errnum)
{
const char *line1, *line2, *line3;
int len1, len2;
line1 = libhaiku_strerror(errnum, NULL);
if (prefix && *prefix) {
line2 = &strchr(line1, '\n')[1];
line3 = &strchr(line2, '\n')[1];
len1 = (int)(line2 - line1);
len2 = (int)(line3 - line2);
fprintf(stderr, "%s: %.*s%s: %.*s%s: %s", prefix, len1, line1, prefix, len2, line2, prefix, line3);
} else {
fprintf(stderr, "%s", line1);
}
}
/**
* Print a poetic error message
*
* @param prefix Unless `NULL` or empty, each line will be prefixed
* by the specified string followed by a colon and a space
*/
extern inline void libhaiku_perror(const char *prefix);