#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <ipxe/errortab.h>

/** @file
 *
 * Error descriptions.
 *
 * The error numbers used by Etherboot are a superset of those defined
 * by the PXE specification version 2.1.  See errno.h for a listing of
 * the error values.
 *
 * To save space in ROM images, error string tables are optional.  Use
 * the ERRORMSG_XXX options in config.h to select which error string
 * tables you want to include.  If an error string table is omitted,
 * strerror() will simply return the text "Error 0x<errno>".
 *
 */

FILE_LICENCE ( GPL2_OR_LATER );

/**
 * Find error description
 *
 * @v errno		Error number
 * @ret errortab	Error description, or NULL
 */
static struct errortab * find_error ( int errno ) {
	struct errortab *errortab;

	for_each_table_entry ( errortab, ERRORTAB ) {
		if ( errortab->errno == errno )
			return errortab;
	}

	return NULL;
}

/**
 * Find closest error description
 *
 * @v errno		Error number
 * @ret errortab	Error description, or NULL
 *
 * 
 */
static struct errortab * find_closest_error ( int errno ) {
	struct errortab *errortab;

	/* First, look for an exact match */
	if ( ( errortab = find_error ( errno ) ) != NULL )
		return errortab;

	/* Second, try masking off the iPXE-specific bit and seeing if
	 * we have an entry for the generic POSIX error message.
	 */
	if ( ( errortab = find_error ( errno & 0x7f0000ff ) ) != NULL )
		return errortab;

	return NULL;
}

/**
 * Retrieve string representation of error number.
 *
 * @v errno/rc		Error number or return status code
 * @ret strerror	Pointer to error text
 *
 * If the error is not found in the linked-in error tables, generates
 * a generic "Error 0x<errno>" message.
 *
 * The pointer returned by strerror() is valid only until the next
 * call to strerror().
 *
 */
const char * strerror ( int errno ) {
	static char errbuf[64];
	struct errortab *errortab;

	/* Allow for strerror(rc) as well as strerror(errno) */
	if ( errno < 0 )
		errno = -errno;

	/* Find the error description, if one exists */
	errortab = find_closest_error ( errno );

	/* Construct the error message */
	if ( errortab ) {
		snprintf ( errbuf, sizeof ( errbuf ),
			   "%s (http://ipxe.org/%08x)",
			   errortab->text, errno );
	} else {
		snprintf ( errbuf, sizeof ( errbuf ),
			   "Error %#08x (http://ipxe.org/%08x)",
			   errno, errno );
	}

	return errbuf;
}

/* Do not include ERRFILE portion in the numbers in the error table */
#undef ERRFILE
#define ERRFILE 0

/** The most common errors */
struct errortab common_errors[] __errortab = {
	__einfo_errortab ( EINFO_ENOERR ),
	__einfo_errortab ( EINFO_EACCES ),
	__einfo_errortab ( EINFO_ECANCELED ),
	__einfo_errortab ( EINFO_ECONNRESET ),
	__einfo_errortab ( EINFO_EINVAL ),
	__einfo_errortab ( EINFO_EIO ),
	__einfo_errortab ( EINFO_ENETUNREACH ),
	__einfo_errortab ( EINFO_ENODEV ),
	__einfo_errortab ( EINFO_ENOENT ),
	__einfo_errortab ( EINFO_ENOEXEC ),
	__einfo_errortab ( EINFO_ENOMEM ),
	__einfo_errortab ( EINFO_ENOSPC ),
	__einfo_errortab ( EINFO_ENOTCONN ),
	__einfo_errortab ( EINFO_ENOTSUP ),
	__einfo_errortab ( EINFO_EPERM ),
	__einfo_errortab ( EINFO_ERANGE ),
	__einfo_errortab ( EINFO_ETIMEDOUT ),
};
