summaryrefslogtreecommitdiffstats
path: root/misc
diff options
context:
space:
mode:
Diffstat (limited to 'misc')
-rw-r--r--misc/README4
-rw-r--r--misc/locales/it/manlint.166
-rw-r--r--misc/man-preformat.c332
-rw-r--r--misc/manlint225
-rw-r--r--misc/manlint.163
5 files changed, 690 insertions, 0 deletions
diff --git a/misc/README b/misc/README
new file mode 100644
index 0000000..5d20cf1
--- /dev/null
+++ b/misc/README
@@ -0,0 +1,4 @@
+Here a contributed program to preformat man pages. Untested.
+Comments, suggestions and patches are welcome.
+
+flc, <flucifredi@acm.org>.
diff --git a/misc/locales/it/manlint.1 b/misc/locales/it/manlint.1
new file mode 100644
index 0000000..11aac7e
--- /dev/null
+++ b/misc/locales/it/manlint.1
@@ -0,0 +1,66 @@
+'\"
+.\" (C) Copyright 1999 David A. Wheeler (dwheeler@ida.org)
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one
+.\"
+.\" Since the Linux kernel and libraries are constantly changing, this
+.\" manual page may be incorrect or out-of-date. The author(s) assume no
+.\" responsibility for errors or omissions, or for damages resulting from
+.\" the use of the information contained herein. The author(s) may not
+.\" have taken the same level of care in the production of this manual,
+.\" which is licensed free of charge, as they might when working
+.\" professionally.
+.\"
+.\" Formatted or processed versions of this manual, if unaccompanied by
+.\" the source, must acknowledge the copyright and authors of this work.
+.\"
+.\" Modified Wed Jul 14 23:00:00 1999 by David A. Wheeler (dwheeler@ida.org)
+.\"
+.\" Traduzione da man-1.6d di Giulio Daprelā <giulio@pluto.it>
+.\" Revisione a cura di Vieri Giugni <v.giugni@gmail.com>
+.\" giugno 2006
+.\"
+.TH MANLINT 1 "14 luglio 1999" "Linux" "Linux Programmer's Manual"
+.SH NOME
+manlint \- programma per identificare errori di formattazione in pagine man o mdoc
+.SH SINTASSI
+.BR manlint " [ " options " ] [ " "list of files" " ] "
+.SH DESCRIZIONE
+manlint
+č un programma che identifica lint (errori) nella formattazione di pagine
+man o mdoc page, in modo simile a quello di un controllo ortografico per un testo
+ordinario. Manlint ha un elenco di macro e sequenze di escape ammesse,
+e segnala l'uso di qualunque macro o sequenza di escape che non sia
+nell'elenco di riferimento.
+Questo elenco di riferimento include tutte le macro definite in
+.BR man (7),
+.BR mdoc (7),
+e un sottoinsieme sicuro di troff definito in
+.BR man (7).
+manlint inoltre verifica la conformitā alle varie regole.
+.PP
+Se una man page supera la verifica di manlint, dovrebbe risultare
+ampiamente portabile, anche con strumenti come
+.BR man2html (1)
+che non implementano completamente troff.
+.PP
+Se non viene indicato un elenco di file, viene letto lo standard input.
+Un file con il nome "-" viene interpretato come standard input.
+.SH BUG
+Attualmente il programma č alquanto incompleto.
+Non ha nessuna opzione, non verifica le sequenze di escape,
+e non č in grado di gestire il formato mdoc.
+Inoltre, č chiaro cosa deve fare, e anche nella sua forma attuale
+puō trovare tantissimi problemi nei file man esistenti.
+.SH AUTORE
+David A. Wheeler (dwheeler@ida.org)
+.SH "VEDERE ANCHE"
+.BR man (7),
+.BR mdoc (7).
diff --git a/misc/man-preformat.c b/misc/man-preformat.c
new file mode 100644
index 0000000..79f4d45
--- /dev/null
+++ b/misc/man-preformat.c
@@ -0,0 +1,332 @@
+From bryanh@giraffe.giraffe.netgate.net Sat Nov 16 09:32:59 1996
+Received: from giraffe.giraffe.netgate.net by hera.cwi.nl with SMTP
+ id <AA24735@cwi.nl>; Sat, 16 Nov 1996 09:32:53 +0100
+Received: (from bryanh@localhost) by giraffe.giraffe.netgate.net (8.6.11/8.6.9) id AAA00639; Sat, 16 Nov 1996 00:32:46 -0800
+Date: Sat, 16 Nov 1996 00:32:46 -0800
+Message-Id: <199611160832.AAA00639@giraffe.giraffe.netgate.net>
+From: bryanh@giraffe.netgate.net (Bryan Henderson)
+To: Andries.Brouwer@cwi.nl
+In-Reply-To: <9611151043.AA01606=aeb@zeus.cwi.nl> (Andries.Brouwer@cwi.nl)
+Subject: Re: cross references for Linux man page package
+Status: RO
+
+>I hope a shell script or perl script?
+
+Well, no. Shell scripts are too hard and I don't know perl. So it's in
+tortured C. It also needs the shhopt package (from sunsite), which
+effortlessly parses a command line, but not many people know about it.
+So maybe this isn't up to distributions standards.
+
+
+Here it is anyway. You invoke it just like this:
+
+ preformat ls.1
+
+Or for a whole directory,
+
+ preformat *
+
+Or if you keep preformatted man pages elsewhere than /usr/man/preformat/catN,
+
+ preformat --mandir=/usr/local/doc/package_xyz/man *
+
+It makes the target directories where necessary and groffs and gzips the
+man pages into them. If it finds a man page that looks like ".so whatever",
+it just does a symbolic link to the base file instead.
+
+--------------------------------------------------------------------------
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <shhopt.h>
+
+#define TRUE 1
+#define FALSE 0
+
+
+
+void
+compute_mkdir_command(const char *installed_path,
+ char *mkdir_cmd, const int mkdir_cmd_l) {
+/*----------------------------------------------------------------------------
+ Figure out what, if any, mkdir command we need to create the directories
+ in which to put the file whose full pathname is <installed_path>.
+----------------------------------------------------------------------------*/
+ char *slash_p; /* pointer to last slash in installed_path. */
+ char need_dir[strlen(installed_path)+1];
+ /* pathname of directory which must exist so we can install the man
+ page into it. If we're just defaulting to the current directory,
+ then this is a null string.
+ */
+
+ slash_p = strrchr(installed_path, '/');
+ if (slash_p == NULL) need_dir[0] = '\0';
+ else {
+ int need_dir_l; /* length for need_dir */
+ need_dir_l = slash_p - installed_path + 1; /* includes slash */
+ strncpy(need_dir, installed_path, need_dir_l);
+ need_dir[need_dir_l] = '\0'; /* need that string terminator */
+ }
+
+ if (need_dir[0] == '\0')
+ mkdir_cmd[0] = '\0';
+ else {
+ struct stat stat_buf; /* results of a stat system call */
+ int rc; /* return code from stat() */
+
+ rc = stat(need_dir, &stat_buf);
+ if (rc == 0)
+ mkdir_cmd[0] = '\0';
+ else
+ sprintf(mkdir_cmd, "umask 002;mkdir --parents %s; ", need_dir);
+ }
+}
+
+
+
+void
+extract_dot_so_stmt(const char *man_page_source_path,
+ char *dot_so_stmt, const int dot_so_stmt_l) {
+
+ FILE *source_file;
+
+ source_file = fopen(man_page_source_path, "r");
+ if (source_file != NULL) {
+ char buffer[200]; /* First line of file */
+
+ fgets(buffer, sizeof(buffer), source_file);
+ fclose(source_file);
+
+ if (strncmp(buffer, ".so ", 4) == 0)
+ snprintf(dot_so_stmt, dot_so_stmt_l, "%s", buffer);
+ else dot_so_stmt[0] = '\0';
+ } else dot_so_stmt[0] = '\0';
+}
+
+
+
+void
+format_page(const char *installed_path, const char *man_page_source_path,
+ const char *mkdir_cmd, int *rc_p) {
+/*----------------------------------------------------------------------------
+ Format and compress the groff source in file <man_page_source_path>
+ and put the output in <installed_path>. Execute the possible mkdir
+ command <mkdir_cmd> too.
+-----------------------------------------------------------------------------*/
+ char shell_command[100+strlen(installed_path)
+ + strlen(man_page_source_path)
+ + strlen(mkdir_cmd)];
+ /* A pipeline we have the shell execute */
+ int rc; /* local return code */
+
+ snprintf(shell_command, sizeof(shell_command),
+ "%sgroff -Tlatin1 -mandoc %s | gzip >%s",
+ mkdir_cmd, man_page_source_path, installed_path);
+
+ printf("%s\n", shell_command);
+ rc = system(shell_command);
+ if (rc != 0) {
+ fprintf(stderr, "groff pipeline failed, rc = %d\n", rc);
+ *rc_p = 10;
+ } else {
+ *rc_p = 0;
+ chmod(installed_path,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH
+ );
+ }
+}
+
+
+
+void
+create_symlink(const char *installed_path, const char *dot_so_stmt,
+ const char *mkdir_cmd, int *rc_p) {
+/*----------------------------------------------------------------------------
+ Create a symlink from <installed_path> to the installed name of the man
+ page identified by <dot_so_stmt>.
+
+ We make some large assumptions about the .so statement, so this may return
+ gibberish.
+
+ Execute the possible mkdir command <mkdir_cmd> too.
+-----------------------------------------------------------------------------*/
+ char shell_command[100+strlen(mkdir_cmd) +
+ strlen(installed_path) +
+ strlen(dot_so_stmt)];
+ /* A pipeline we have the shell execute */
+ int rc; /* local return code */
+ char *slash_p; /* pointer to last slash in .so statement, or NULL */
+
+ slash_p = strrchr(dot_so_stmt, '/');
+ if (slash_p == NULL) {
+ fprintf(stderr, "Cannot find the base filename "
+ "in the .so statement '%s'. There is no slash ('/').\n",
+ dot_so_stmt);
+ *rc_p = 15;
+ } else if (*(slash_p+1) == '\0') {
+ fprintf(stderr, "Cannot find the base filename "
+ "in the .so statement '%s'. There is nothing after the "
+ "last slash ('/').",
+ dot_so_stmt);
+ *rc_p = 13;
+ } else {
+ char link_contents[200];
+
+ strncpy(link_contents, slash_p+1, sizeof(link_contents)-10);
+ if (link_contents[strlen(link_contents)-1] == '\n')
+ link_contents[strlen(link_contents)-1] = '\0';
+ strcat(link_contents, ".gz");
+
+ sprintf(shell_command, "%sln --symbolic %s %s",
+ mkdir_cmd, link_contents, installed_path);
+
+ printf("%s\n", shell_command);
+ rc = system(shell_command);
+ if (rc != 0) {
+ fprintf(stderr, "ln pipeline failed, rc = %d\n", rc);
+ *rc_p = 10;
+ } else *rc_p = 0;
+ }
+}
+
+
+
+void
+install_it(char *installed_path, char *man_page_source_path, int *rc_p){
+/*----------------------------------------------------------------------------
+ Take the man page groff source in file <man_page_source_path>, format
+ it, compress it, and place it in file <installed_path>.
+
+ Special case: If the file appears to be just a groff .so statement,
+ don't format it; instead, create a symbolic link that will do the same
+ thing as formatting the .so. A .so statement looks like:
+
+ .so man3/basepage.3
+
+ and means to include all the groff source from the file man3/basepage.3.
+ So we just create a symbolic link to cat3/basepage.3.gz and save some
+ redundancy.
+
+
+ Make any directories necessary to create file <installed_path>.
+
+-----------------------------------------------------------------------------*/
+ char mkdir_cmd[30 + strlen(installed_path)];
+ /* A mkdir shell command to create the necessary directories. Null
+ string if no directory needs creating.
+ */
+ char dot_so_stmt[200];
+ /* The .so statement from the man page source, if the man page appears
+ to be one that consists solely of a .so statement. If it doesn't
+ appear so, this is an empty string.
+ */
+
+ /* We have to remove the file first, because it may be a symbolic link
+ for the purposes of having the same man page come up for multiple
+ commands. If we just overwrite, we will be replacing the base file,
+ which we don't want to do.
+ */
+ unlink(installed_path);
+
+ compute_mkdir_command(installed_path, mkdir_cmd, sizeof(mkdir_cmd));
+
+ extract_dot_so_stmt(man_page_source_path, dot_so_stmt, sizeof(dot_so_stmt));
+
+ if (*dot_so_stmt != '\0')
+ create_symlink(installed_path, dot_so_stmt, mkdir_cmd, rc_p);
+ else
+ format_page(installed_path, man_page_source_path, mkdir_cmd, rc_p);
+}
+
+
+
+char *
+just_filename(const char *full_path) {
+/*----------------------------------------------------------------------------
+ Return pointer into <full_path> of start of filename part.
+ Return NULL if pathname ends with a slash (i.e. it's a directory).
+-----------------------------------------------------------------------------*/
+ char *slash_p; /* Pointer to last slash in <full_path> */
+ char *filename; /* Our eventual result */
+
+ slash_p = strrchr(full_path, '/');
+ if (slash_p == NULL) filename = (char *) full_path;
+ else if (*(slash_p+1) == '\0') {
+ filename = NULL;
+ } else filename = slash_p+1;
+ return(filename);
+}
+
+
+
+int main(int argc, char *argv[]) {
+ char *mandir;
+ /* The directory in which the formatted man pages are to go. This is
+ the parent directory of the cat1, cat2, etc. directories.
+ */
+ char default_mandir[] = "/usr/man/preformat";
+ /* default value for mandir, if user doesn't give --mandir option */
+ int error; /* boolean: we've encountered an error */
+ int i; /* local for loop index */
+
+ const optStruct option_def[] = {
+ { 0, (char *) "mandir", OPT_STRING, &mandir, 0},
+ { 0, 0, OPT_END, 0, 0}
+ };
+ int argc_parse; /* argc, except we modify it as we parse */
+ char **argv_parse; /* argv, except we modify it as we parse */
+
+ mandir = default_mandir; /* initial assumption - default */
+ argc_parse = argc; argv_parse = argv;
+ optParseOptions(&argc_parse, argv_parse, option_def, 0);
+ /* uses and sets argc_parse, argv_parse. */
+ /* sets mandir (via option_def) */
+
+ error = FALSE; /* no error yet */
+
+ for (i=1;i <= argc_parse-1 && !error; i++) {
+ /* Do one of the man pages specified in the program arguments */
+ char *man_page_source_path;
+ /* string: pathname of man page source file we're supposed to install
+ */
+ char *man_page_source_fn; /* pointer within pathname to filename */
+ char *dot_p; /* pointer within filename to last dot */
+
+ char man_section; /* man section number to which this page belongs */
+ char installed_path[100]; /* full pathname for installed man page file */
+
+ man_page_source_path = argv_parse[i];
+
+ man_page_source_fn = just_filename(man_page_source_path);
+ if (man_page_source_fn == NULL)
+ fprintf(stderr, "Need filename at the end of pathname: %s\n",
+ man_page_source_path);
+ else {
+ dot_p = strrchr(man_page_source_fn, '.');
+ if (dot_p == NULL) {
+ fprintf(stderr, "Invalid source file -- contains no period: %s\n",
+ man_page_source_fn);
+ } else if (*(dot_p+1) == '\0') {
+ fprintf(stderr, "Invalid source file -- need at least one character "
+ "after the last period: %s\n", man_page_source_fn);
+ } else {
+ int rc; /* local return code */
+ /* Filename has a dot with at least one character after it.
+ Manual section number is the character right after that dot.
+ */
+ man_section = *(dot_p+1);
+
+ sprintf(installed_path, "%s/cat%c/%s.gz",
+ mandir, man_section, man_page_source_fn);
+
+ install_it(installed_path, man_page_source_path, &rc);
+ if (rc != 0) error = TRUE;
+ }
+ }
+ }
+ return(error);
+}
+
diff --git a/misc/manlint b/misc/manlint
new file mode 100644
index 0000000..c919a94
--- /dev/null
+++ b/misc/manlint
@@ -0,0 +1,225 @@
+#!/usr/bin/perl -w
+
+# manlint - report "errors" in man page(s).
+
+# USAGE:
+# manlint [list of files to check]
+#
+# EXAMPLE:
+# manlint /usr/man/man*/*.* | less
+
+# An error is anything not known to be a safe construct in a man page;
+# see man(7) for more information.
+# Currently it's excessively paranoid, but that's the point -- this
+# program assumes there's a problem, and if it isn't we can add that to the
+# ruleset so that what's safe is explicitly spelled out.
+# Currently this program only examines tmac.an based pages, the normal
+# kind encountered in Linux. This is different than the BSD manddoc format,
+# which is used by a number of man pages.
+
+# (C) 1999 David A. Wheeler (dwheeler@ida.org)
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+require 5.002; # Requires Perl 5.002 because functions are prototyped.
+
+# First, set up configuration.
+
+$debug = 0;
+$errs = $totalerrs = 0;
+$goodfiles = $badfiles = $skipfiles = 0;
+$filename = '';
+
+# Allow options for small or large safe set; just printing if a file fails
+# instead of detail; auto-skip BSD files.
+
+# This is a list of "safe" macros, with their value being the
+# maximum number of allowed parameters (-1 = any, 0=no parameters allowed)
+%safemacros = (
+ 'TH' => 5,
+ # Font Control:
+ 'B' => -1, 'BI' => -1, 'BR' => -1,
+ 'I' => -1, 'IB' => -1, 'IR' => -1,
+ 'RB' => -1, 'RI' => -1, 'SB' => -1, 'SM' => -1,
+ # tmac.an other macros:
+ 'SH' => 1,
+ 'LP' => 0, 'P' => 0,
+ 'PP' => 0,
+ 'RS' => 1, 'RE' => 0,
+ 'HP' => 1, 'IP' => 2, 'TP' => 1,
+ 'DT' => 0, 'PD' => 1, 'SS' => 1,
+ # We'll allow IX (indexing).
+ 'IX' => -1,
+ # I'm adding the UR, UN, and UE macros that will permit embedded URIs.
+ 'UR' => 1,
+ 'UN' => 1,
+ 'UE' => 0,
+ # allowed troff macros
+ '\\"' => -1, # troff comments
+ 'ps' => 1, # Point size
+ 'ft' => 1, # Font commands (not recommended, may be ignored in some cases)
+ 'hy' => 1, # Hyphenation (probably ignored in translation)
+ 'bp' => 0, # Force page break; optional parameter forbidden.
+ 'ne' => 1, # Need lines (likely to be ignored in translation)
+ 'br' => 0,
+ 'nf' => 0, # No-fill; insert breaks at end of each line.
+ 'fi' => 0,
+ 'ig' => 1,
+ '.' => 0, # standard end-of-ignore/end-of-definition.
+ 'ce' => 1, # Center next N lines
+ 'ad' => 1,
+ 'na' => 0,
+ # Will probably need to handle some if.
+ 'if' => -1, # LIMITED VERSION.
+ 'ie' => -1, # LIMITED VERSION.
+ 'el' => -1,
+ 'so' => 1, # Handle 'so' for shared man pages
+ 'sp' => 1, # Vertical Space - only permit positive values.
+ 'de' => 1, # Handling 'macro define' is a pain, but many pages require it.
+ 'ds' => -1, # Allow string defines.
+ 'in' => 1, # Require that every indent be paired with a negative indent.
+ 'ti' => 1, # Temporary indent may be ignored
+ 'hy' => 1, # Hypenation almost certainly ignored by anyone else.
+ 'nh' => 1, # Again, hyphenation likely ignored.
+ 'tr' => 1, # Translations limited, see below.
+);
+
+# Allowed parameters for the ft (font) troff command.
+%allowed_ft_parameter = (
+ '1' => 1,
+ '2' => 1,
+ '3' => 1,
+ '4' => 1,
+ 'R' => 1,
+ 'I' => 1,
+ 'B' => 1,
+ 'P' => 1,
+ 'CW' => 1,
+ '' => 1,
+);
+
+%allowed_tr = (
+ '\\(ts"' => 1,
+ '\\(is\'' => 1,
+ '\\(if`' => 1,
+ '\\(pd"' => 1,
+ '\\(*W-|\(bv\*(Tr' => 1,
+ '\\*(Tr' => 1,
+);
+
+sub problem($) {
+ # Report a problem, if you should.
+ my $message = shift;
+ print "${ARGV}: $message\n";
+ $errs++;
+}
+
+sub clean_state {
+ %defined_macros = ();
+ $is_skipped = 0;
+}
+
+sub process_line {
+ # Process line already read in $_ (default input line).
+ my $macro;
+ my $parameters;
+ if (m/^[.']\s*([^\s]+)\s*(.*)?/) {
+ $macro=$1;
+ $parameters=$2;
+ $macro =~ s/\s//g;
+ print "Found macro: #${macro}#\n" if $debug;
+ if ($macro =~ m/Dd/) { # Is this the BSD macro set and not a tmac.an set?
+ problem("Uses BSD mandoc conventions instead of tmac.an");
+ $errs--; # Patch up error count.
+ # print "${ARGV}: Uses BSD mandoc conventions instead of tmac.an.\n";
+ close(ARGV); # Skip the rest of this file.
+ $is_skipped = 1;
+ return;
+ }
+ if ($macro =~ m/\\"/) {return;} # Skip troff comments.
+ if (exists($defined_macros{$macro})) {
+ return; # ??? Should examine the macro parameters.
+ }
+ if (exists($safemacros{$macro}) ) {
+ # ??? Check parameter count.
+ # ??? Check that .TH is the first macro (note: bash.1, etc., break this)
+ if ( ($macro eq 'if') || ($macro eq 'ie' )) {
+ # Only permit checking 't' or 'n' for now.
+ if ($parameters =~ m/^[tn]\s/) {
+ $_ = $parameters;
+ s/^[tn]\s+//;
+ process_line(); # Re-examine line without the if statement.
+ } else {
+ problem("unsafe use of if/ie");
+ }
+ # ??? sp: only no-parameter or positive values.
+ } elsif ($macro eq 'de') {
+ $parameters =~ m/^([^\s]+)/;
+ $is_defining = $1;
+ $defined_macros{$is_defining} = 1;
+ } elsif ($macro eq 'so') {
+ $parameters =~ m/^([^\s]+)/;
+ $new_file = $1;
+ while (<$new_file>) { process_line(); }
+ } elsif (($macro eq 'ft') && (defined($parameters))
+ && (! exists($allowed_ft_parameter{$parameters}))) {
+ problem("forbidden ft parameter $parameters");
+ } elsif (($macro eq 'tr') && (defined($parameters))
+ && (! exists($allowed_tr{$parameters}))) {
+ problem("forbidden tr parameter $parameters");
+ }
+ # ??? 'in': Require that every indent be paired with a negative indent.
+ # ??? For macros with text after them, check their text's escapes.
+ } else {
+ problem("unsafe macro $macro");
+ }
+ } else {
+ # ??? Regular text; check escape clauses.
+ }
+}
+
+
+# Main loop: Process files, looking for errors.
+
+clean_state();
+
+while (<>) {
+ if ($ARGV ne $filename) {
+ print "Processing $ARGV; up to now good=$goodfiles bad=$badfiles skip=$skipfiles\n";
+ $filename=$ARGV;
+ }
+ process_line();
+} continue {
+ if (eof) { # End of processing this file.
+ close ARGV; # Perl magic to get line #s to be accurate.
+ $totalerrs += $errs;
+ if ($errs) { $badfiles++ } else {
+ if ($is_skipped) {$skipfiles++} else {$goodfiles++};
+ }
+ $errs = 0;
+ clean_state();
+ }
+}
+
+print "Number of good files = $goodfiles\n";
+print "Number of bad files = $badfiles\n";
+print "Number of skipped files = $skipfiles\n";
+exit $errs;
+
+# ??? Handle .so better (esp. the error messages)
+# currently error messages don't report the traceback & they should.
+
+
diff --git a/misc/manlint.1 b/misc/manlint.1
new file mode 100644
index 0000000..ab76009
--- /dev/null
+++ b/misc/manlint.1
@@ -0,0 +1,63 @@
+'\"
+.\" (C) Copyright 1999 David A. Wheeler (dwheeler@ida.org)
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one
+.\"
+.\" Since the Linux kernel and libraries are constantly changing, this
+.\" manual page may be incorrect or out-of-date. The author(s) assume no
+.\" responsibility for errors or omissions, or for damages resulting from
+.\" the use of the information contained herein. The author(s) may not
+.\" have taken the same level of care in the production of this manual,
+.\" which is licensed free of charge, as they might when working
+.\" professionally.
+.\"
+.\" Formatted or processed versions of this manual, if unaccompanied by
+.\" the source, must acknowledge the copyright and authors of this work.
+.\"
+.\" Modified Wed Jul 14 23:00:00 1999 by David A. Wheeler (dwheeler@ida.org)
+.TH MANLINT 1 "14 July 1999" "Linux" "Linux Programmer's Manual"
+.SH NAME
+manlint \- program to report errors in man or mdoc pages
+.SH SYNOPSIS
+.BR manlint " [ " options " ] [ " "list of files" " ] "
+.SH DESCRIPTION
+manlint
+is a program that identifies lint (errors) in man or mdoc page formatting,
+similar to a spelling checker for ordinary text.
+Manlint has a list of permitted macros and escape sequences,
+and reports the use of any macro or escape sequence not in the
+permitted list.
+This permitted list includes all the macros defined in
+.BR man (7),
+.BR mdoc (7),
+and the safe subset of troff defined in
+.BR man (7).
+manlint also checks for conformance to various rules.
+.PP
+If a man page passes manlint, it should be widely portable, even
+to tools such as
+.BR man2html (1)
+which don't implement all of troff.
+.PP
+If a list of files is omitted, the standard input is used.
+A file by the name "-" is interpreted as the standard input.
+.SH BUGS
+Currently the program is very incomplete.
+It doesn't have any options, it doesn't actually check escape sequences,
+and it can't handle mdoc format.
+Still, it's clear what it should do, and even in its current form
+it can find lots of problems in existing man files.
+.SH AUTHOR
+David A. Wheeler (dwheeler@ida.org) was the original author of
+.BR "manlint" .
+Federico Lucifredi <flucifredi@acm.org> is the current maintainer.
+.SH "SEE ALSO"
+.BR man (7),
+.BR mdoc (7).