aboutsummaryrefslogblamecommitdiffstats
path: root/libhaiku.c
blob: 870093a981974bcbc40586bfcb58de0d7329a19f (plain) (tree)
































                                                         

                 



                                            

                                                                    













































































































































































































































































                                                                                                                   
                                                       
/* 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);