#!/usr/bin/perl
#   errnotbl -1 file err.c err.h
#   errnotbl 2 intro err.c err.h
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# This little program feeds its first two args to man(1) to get a  page  that #
# contains  this system's errno codes.  It chews up man's output and extracts #
# the error codes.  It then puts them out as a  C  table  that  contains  the #
# symbolic  name, the errno value, and the error message.  It also adds a set #
# of C routines that return an errno's name and message.                      #
#                                                                             #
# If the section number is negative, we will treat the second arg as  a  file #
# name and read from it. This way, we can process man pages copied from other #
# machines.  This is mostly useful if the local machine lacks man pages.      #
#                                                                             #
# Note that two files, are written by this program. Their names are err.c and #
# err.h  by default, but you can override them by including the file names on #
# the command line.  You should as usual include err.h in your C  files  that #
# invoke either of the functions defined here.                                #
#                                                                             #
# The above args are the defaults; you can override them if you wish.         #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #

($host = `hostname`) =~ s/\s+$//;
$s = $ARGV[0] || '2';
$m = $ARGV[1] || 'intro';
$c = $ARGV[2] || 'err.c';
$h = $ARGV[3] || 'err.h';
if ($s > 0) {					# Do we have a man section?
	$cmd = "man $s $m |";
	open(P,$cmd)	# Start up a man subprocess.
		|| die "Can't run \"man $s $m\" [$!]\n";
	print "\tUsing \"man $s $m\"\n";
} else {
	$cmd = "<$m";
	open(P,$cmd) 				# Read from file.
		|| die "Can't read \"$m\" [$!]\n";
	print "\tReading $m\n";
}
open(C,">$c")
	|| die "Can't write to $c [$!]\n";
open(H,">$h")
	|| die "Can't write to $h [$!]\n";

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#
# Here are the lines that we put out at the start. This writes to both #
# a header file and to the start of the err.c file.                    #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#
@hdr = (
	"#ifndef err_h\n",
	"#define err_h\n",
	"/*\n",
	"* This file was generated on $host by:\n",
	"*    $cmd\n",
	"*/\n",
	"#define ERRTBL struct _errno_table_\n",
	"ERRTBL {\n",
	"	int code;\n",
	"	char *name;\n",
	"	char *message;\n",
	"};\n",
	"#ifndef Errlist\n",
	"#define Errlist errno,sys_errname(errno),sys_errmessage(errno)\n",
	"#endif\n",
	"#ifndef Errinfo\n",
	"#define Errinfo errno,sys_errname(errno),sys_errmessage(errno)\n",
	"#endif\n",
	"\n",
	"extern int   errno;\n",
	"extern char* sys_errmessage();\n",
	"extern char* sys_errname();\n",
	"extern char  unknown[];\n",
	"\n",
	"#endif\n",
);
for (@hdr) {print H;}

@init = (
#	"	static char err_sccs_id[] = "%W% %G%";",
	"#include \"err.h\"\n",
	"/*\n",
	"* This file was generated on $host by:\n",
	"*    $cmd\n",
	"*/\n",
	"\n",
	"ERRTBL sys_errtbl[] = {\n",
	"\t{   0,\t\"EOK\",\t\"No problem\" },\n",
);
for (@init) {print C;}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#
# Different  systems  need  different  patterns   to   recognize   the #
# error-message  lines in the man page.  The biggest pain is caused by #
# the fact that the number and mnemonic may be in either order, so  if #
# you swap patters, you must also swap $e and $s in the printf call:   #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#
$patbsd = '^\s\s+(E[A-Z0-9]+)\s+([0-9]+)\s+(.*)';	# SunOS, Ultrix.
$patusg = '^\s\s+([0-9]+)\s+(E[A-Z0-9]+)\s+(.*)';	# Solaris, Sys/V.
$pataix = '^#define\s+(E[A-Z0-9]+)\s+([0-9]+)\s+\/\*\s*(.*)\s*\*\/';	# AIX's errno.h

while ($l = <P>) {	# Now read man's output and select the errno codes.
	$l =~ s/.//g;
	if (($s, $e, $m) = ($l =~ $patbsd)) {	# Symbol, Errno, Message.
		$m =~ s/\s+/ /;
		printf(C "\t{%4d,\t\"%s\",\t\"%s\" },\n",$e,$s,$m);
	} elsif (($e, $s, $m) = ($l =~ $patusg)) {	# Errno, Symbol, Message.
		$m =~ s/\s+/ /;
		printf(C "\t{%4d,\t\"%s\",\t\"%s\" },\n",$e,$s,$m);
	} elsif (($s, $e, $m) = ($l =~ $pataix)) {	# Symbol, Errno, Message.
		$m =~ s/\s+/ /;
		printf(C "\t{%4d,\t\"%s\",\t\"%s\" },\n",$e,$s,$m);
	}
}

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#
# Here are the lines that we put out at the end. These include several #
# lookup routines that search thru the table for a given error code or #
# mnemonic and return the index or the message.                        #
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#
@term = ( "{0,0,0}};\n",
	"\n",
	"char * sys_errname(e)\n",
	"{\tint i;\n",
	"\tfor (i=0; sys_errtbl[i].name;i++)\n",
	"\t\tif (e == sys_errtbl[i].code)\n",
	"\t\t\treturn sys_errtbl[i].name;\n",
	"\treturn unknown;\n",
	"}\n",
	"\n",
	"char * sys_errmessage(e)\n",
	"{\tint i;\n",
	"\tfor (i=0; sys_errtbl[i].message;i++)\n",
	"\t\tif (e == sys_errtbl[i].code)\n",
	"\t\t\treturn sys_errtbl[i].message;\n",
	"\treturn unknown;\n",
	"}\n",
);
for (@term) {print C;}
