You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5686 lines
181 KiB
5686 lines
181 KiB
/*
|
|
This file is part of the KDE libraries
|
|
|
|
Copyright (C) 2005 Nicolas GOUTTE <goutte@kde.org>
|
|
|
|
### TODO: who else?
|
|
*/
|
|
|
|
// Start of verbatim comment
|
|
|
|
/*
|
|
** This program was written by Richard Verhoeven (NL:5482ZX35)
|
|
** at the Eindhoven University of Technology. Email: rcb5@win.tue.nl
|
|
**
|
|
** Permission is granted to distribute, modify and use this program as long
|
|
** as this comment is not removed or changed.
|
|
*/
|
|
|
|
// End of verbatim comment
|
|
|
|
// kate: space-indent on; indent-width 4; replace-tabs on;
|
|
|
|
/*
|
|
* man2html-linux-1.0/1.1
|
|
* This version modified for Redhat/Caldera linux - March 1996.
|
|
* Michael Hamilton <michael@actrix.gen.nz>.
|
|
*
|
|
* man2html-linux-1.2
|
|
* Added support for BSD mandoc pages - I didn't have any documentation
|
|
* on the mandoc macros, so I may have missed some.
|
|
* Michael Hamilton <michael@actrix.gen.nz>.
|
|
*
|
|
* vh-man2html-1.3
|
|
* Renamed to avoid confusion (V for Verhoeven, H for Hamilton).
|
|
*
|
|
* vh-man2html-1.4
|
|
* Now uses /etc/man.config
|
|
* Added support for compressed pages.
|
|
* Added "length-safe" string operations for client input parameters.
|
|
* More secure, -M secured, and client input string lengths checked.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
** If you want to use this program for your WWW server, adjust the line
|
|
** which defines the CGIBASE or compile it with the -DCGIBASE='"..."' option.
|
|
**
|
|
** You have to adjust the built-in manpath to your local system. Note that
|
|
** every directory should start and end with the '/' and that the first
|
|
** directory should be "/" to allow a full path as an argument.
|
|
**
|
|
** The program first check if PATH_INFO contains some information.
|
|
** If it does (t.i. man2html/some/thing is used), the program will look
|
|
** for a manpage called PATH_INFO in the manpath.
|
|
**
|
|
** Otherwise the manpath is searched for the specified command line argument,
|
|
** where the following options can be used:
|
|
**
|
|
** name name of manpage (csh, printf, xv, troff)
|
|
** section the section (1 2 3 4 5 6 7 8 9 n l 1v ...)
|
|
** -M path an extra directory to look for manpages (replaces "/")
|
|
**
|
|
** If man2html finds multiple manpages that satisfy the options, an index
|
|
** is displayed and the user can make a choice. If only one page is
|
|
** found, that page will be displayed.
|
|
**
|
|
** man2html will add links to the converted manpages. The function add_links
|
|
** is used for that. At the moment it will add links as follows, where
|
|
** indicates what should match to start with:
|
|
** ^^^
|
|
** Recognition Item Link
|
|
** ----------------------------------------------------------
|
|
** name(*) Manpage ../man?/name.*
|
|
** ^
|
|
** name@hostname Email address mailto:name@hostname
|
|
** ^
|
|
** method://string URL method://string
|
|
** ^^^
|
|
** www.host.name WWW server http://www.host.name
|
|
** ^^^^
|
|
** ftp.host.name FTP server ftp://ftp.host.name
|
|
** ^^^^
|
|
** <file.h> Include file file:/usr/include/file.h
|
|
** ^^^
|
|
**
|
|
** Since man2html does not check if manpages, hosts or email addresses exist,
|
|
** some links might not work. For manpages, some extra checks are performed
|
|
** to make sure not every () pair creates a link. Also out of date pages
|
|
** might point to incorrect places.
|
|
**
|
|
** The program will not allow users to get system specific files, such as
|
|
** /etc/passwd. It will check that "man" is part of the specified file and
|
|
** that "/../" isn't. Even if someone manages to get such file, man2html will
|
|
** handle it like a manpage and will usually not produce any output (or crash).
|
|
**
|
|
** If you find any bugs when normal manpages are converted, please report
|
|
** them to me (rcb5@win.tue.nl) after you have checked that man(1) can handle
|
|
** the manpage correct.
|
|
**
|
|
** Known bugs and missing features:
|
|
**
|
|
** * Equations are not converted at all.
|
|
** * Tables are converted but some features are not possible in html.
|
|
** * The tabbing environment is converted by counting characters and adding
|
|
** spaces. This might go wrong (outside <PRE>)
|
|
** * Some manpages rely on the fact that troff/nroff is used to convert
|
|
** them and use features which are not descripted in the man manpages.
|
|
** (definitions, calculations, conditionals, requests). I can't guarantee
|
|
** that all these features work on all manpages. (I didn't have the
|
|
** time to look through all the available manpages.)
|
|
*/
|
|
|
|
#ifdef SIMPLE_MAN2HTML
|
|
// We suppose that we run on a standard Linux
|
|
# define HAVE_STRING_H 1
|
|
# define HAVE_UNISTD_H 1
|
|
#else
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_STRING_H
|
|
# include <string.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <tqvaluestack.h>
|
|
#include <tqstring.h>
|
|
#include <tqptrlist.h>
|
|
#include <tqmap.h>
|
|
#include <tqdatetime.h>
|
|
|
|
#ifdef SIMPLE_MAN2HTML
|
|
# include <stdlib.h>
|
|
# include <iostream>
|
|
# include <dirent.h>
|
|
# include <sys/stat.h>
|
|
# define kdDebug(x) cerr
|
|
# define kdWarning(x) cerr << "WARNING "
|
|
#else
|
|
# include <tqtextcodec.h>
|
|
# include <kdebug.h>
|
|
# include <tdeversion.h>
|
|
#endif
|
|
|
|
|
|
|
|
#include "man2html.h"
|
|
|
|
using namespace std;
|
|
|
|
#define NULL_TERMINATED(n) ((n) + 1)
|
|
|
|
#define HUGE_STR_MAX 10000
|
|
#define LARGE_STR_MAX 2000
|
|
#define MED_STR_MAX 500
|
|
#define SMALL_STR_MAX 100
|
|
#define TINY_STR_MAX 10
|
|
|
|
|
|
#if 1
|
|
// The output is current too horrible to be called HTML 4.01
|
|
#define DOCTYPE "<!DOCTYPE HTML>"
|
|
#else
|
|
#define DOCTYPE "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
|
|
#endif
|
|
|
|
/* mdoc(7) Bl/El lists to HTML list types */
|
|
#define BL_DESC_LIST 1
|
|
#define BL_BULLET_LIST 2
|
|
#define BL_ENUM_LIST 4
|
|
|
|
/* mdoc(7) Bd/Ed example(?) blocks */
|
|
#define BD_LITERAL 1
|
|
#define BD_INDENT 2
|
|
|
|
static int s_nroff = 1; // NROFF mode by default
|
|
|
|
static int mandoc_name_count = 0; /* Don't break on the first Nm */
|
|
|
|
static char *stralloc(int len)
|
|
{
|
|
/* allocate enough for len + NULL */
|
|
char *news = new char [len+1];
|
|
#ifdef SIMPLE_MAN2HTML
|
|
if (!news)
|
|
{
|
|
cerr << "man2html: out of memory" << endl;
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
#else
|
|
// modern compilers do not return a NULL pointer for a new
|
|
#endif
|
|
return news;
|
|
}
|
|
|
|
static char *strlimitcpy(char *to, char *from, int n, int limit)
|
|
{ /* Assumes space for limit plus a null */
|
|
const int len = n > limit ? limit : n;
|
|
tqstrncpy(to, from, len + 1);
|
|
to[len] = '\0';
|
|
return to;
|
|
}
|
|
|
|
/* below this you should not change anything unless you know a lot
|
|
** about this program or about troff.
|
|
*/
|
|
|
|
|
|
/// Structure for character definitions
|
|
struct CSTRDEF {
|
|
int nr, slen;
|
|
const char *st;
|
|
};
|
|
|
|
|
|
|
|
const char NEWLINE[2]="\n";
|
|
|
|
/**
|
|
* Class for defining strings and macros
|
|
*/
|
|
class StringDefinition
|
|
{
|
|
public:
|
|
StringDefinition( void ) : m_length(0) {}
|
|
StringDefinition( int len, const char* cstr ) : m_length( len ), m_output( cstr ) {}
|
|
public:
|
|
int m_length; ///< Length of output text
|
|
TQCString m_output; ///< Defined string
|
|
};
|
|
|
|
/**
|
|
* Class for defining number registers
|
|
* \note Not for internal read-only registers
|
|
*/
|
|
class NumberDefinition
|
|
{
|
|
public:
|
|
NumberDefinition( void ) : m_value(0), m_increment(0) {}
|
|
NumberDefinition( int value ) : m_value( value ), m_increment(0) {}
|
|
NumberDefinition( int value, int incr) : m_value( value ), m_increment( incr ) {}
|
|
public:
|
|
int m_value; ///< value of number register
|
|
int m_increment; ///< Increment of number register
|
|
// ### TODO: display form (.af)
|
|
};
|
|
|
|
/**
|
|
* Map of character definitions
|
|
*/
|
|
static TQMap<TQCString,StringDefinition> s_characterDefinitionMap;
|
|
|
|
/**
|
|
* Map of string variable and macro definitions
|
|
* \note String variables and macros are the same thing!
|
|
*/
|
|
static TQMap<TQCString,StringDefinition> s_stringDefinitionMap;
|
|
|
|
/**
|
|
* Map of number registers
|
|
* \note Intern number registers (starting with a dot are not handled here)
|
|
*/
|
|
static TQMap<TQCString,NumberDefinition> s_numberDefinitionMap;
|
|
|
|
static void fill_old_character_definitions( void );
|
|
|
|
/**
|
|
* Initialize character variables
|
|
*/
|
|
static void InitCharacterDefinitions( void )
|
|
{
|
|
fill_old_character_definitions();
|
|
// ### HACK: as we are converting to HTML too early, define characters with HTML references
|
|
s_characterDefinitionMap.insert( "<-", StringDefinition( 1, "←" ) ); // <-
|
|
s_characterDefinitionMap.insert( "->", StringDefinition( 1, "→" ) ); // ->
|
|
s_characterDefinitionMap.insert( "<>", StringDefinition( 1, "↔" ) ); // <>
|
|
s_characterDefinitionMap.insert( "<=", StringDefinition( 1, "≤" ) ); // <=
|
|
s_characterDefinitionMap.insert( ">=", StringDefinition( 1, "≥" ) ); // >=
|
|
// End HACK
|
|
}
|
|
|
|
/**
|
|
* Initialize string variables
|
|
*/
|
|
static void InitStringDefinitions( void )
|
|
{
|
|
// mdoc-only, see mdoc.samples(7)
|
|
s_stringDefinitionMap.insert( "<=", StringDefinition( 1, "≤" ) );
|
|
s_stringDefinitionMap.insert( ">=", StringDefinition( 1, "≥" ) );
|
|
s_stringDefinitionMap.insert( "Rq", StringDefinition( 1, "”" ) );
|
|
s_stringDefinitionMap.insert( "Lq", StringDefinition( 1, "“" ) );
|
|
s_stringDefinitionMap.insert( "ua", StringDefinition( 1, "&circ" ) ); // Note this is different from \(ua
|
|
s_stringDefinitionMap.insert( "aa", StringDefinition( 1, "´" ) );
|
|
s_stringDefinitionMap.insert( "ga", StringDefinition( 1, "`" ) );
|
|
s_stringDefinitionMap.insert( "q", StringDefinition( 1, """ ) );
|
|
s_stringDefinitionMap.insert( "Pi", StringDefinition( 1, "π" ) );
|
|
s_stringDefinitionMap.insert( "Ne", StringDefinition( 1, "≠" ) );
|
|
s_stringDefinitionMap.insert( "Le", StringDefinition( 1, "≤" ) );
|
|
s_stringDefinitionMap.insert( "Ge", StringDefinition( 1, "≥" ) );
|
|
s_stringDefinitionMap.insert( "Lt", StringDefinition( 1, "<" ) );
|
|
s_stringDefinitionMap.insert( "Gt", StringDefinition( 1, ">" ) );
|
|
s_stringDefinitionMap.insert( "Pm", StringDefinition( 1, "±" ) );
|
|
s_stringDefinitionMap.insert( "If", StringDefinition( 1, "∞" ) );
|
|
s_stringDefinitionMap.insert( "Na", StringDefinition( 3, "NaN" ) );
|
|
s_stringDefinitionMap.insert( "Ba", StringDefinition( 1, "|" ) );
|
|
// end mdoc-only
|
|
// man(7)
|
|
s_stringDefinitionMap.insert( "Tm", StringDefinition( 1, "™" ) ); // \*(TM
|
|
s_stringDefinitionMap.insert( "R", StringDefinition( 1, "®" ) ); // \*R
|
|
// end man(7)
|
|
// Missing characters from man(7):
|
|
// \*S "Change to default font size"
|
|
#ifndef SIMPLE_MAN2HTML
|
|
// Special KDE KIO man:
|
|
const TQCString tdeversion(TDE_VERSION_STRING);
|
|
s_stringDefinitionMap.insert( ".TDE_VERSION_STRING", StringDefinition( tdeversion.length(), tdeversion ) );
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Initialize number registers
|
|
* \note Internal read-only registers are not handled here
|
|
*/
|
|
static void InitNumberDefinitions( void )
|
|
{
|
|
// As the date number registers are more for end-users, better choose local time.
|
|
// Groff seems to support Gregorian dates only
|
|
TQDate today( TQDate::currentDate( Qt::LocalTime ) );
|
|
s_numberDefinitionMap.insert( "year", today.year() ); // Y2K-correct year
|
|
s_numberDefinitionMap.insert( "yr", today.year() - 1900 ); // Y2K-incorrect year
|
|
s_numberDefinitionMap.insert( "mo", today.month() );
|
|
s_numberDefinitionMap.insert( "dy", today.day() );
|
|
s_numberDefinitionMap.insert( "dw", today.dayOfWeek() );
|
|
}
|
|
|
|
|
|
#define V(A,B) ((A)*256+(B))
|
|
|
|
//used in expand_char, e.g. for "\(bu"
|
|
// see groff_char(7) for list
|
|
static CSTRDEF standardchar[] = {
|
|
{ V('*','*'), 1, "*" },
|
|
{ V('*','A'), 1, "Α" },
|
|
{ V('*','B'), 1, "Β" },
|
|
{ V('*','C'), 1, "Ξ" },
|
|
{ V('*','D'), 1, "Δ" },
|
|
{ V('*','E'), 1, "Ε" },
|
|
{ V('*','F'), 1, "Φ" },
|
|
{ V('*','G'), 1, "Γ" },
|
|
{ V('*','H'), 1, "Θ" },
|
|
{ V('*','I'), 1, "Ι" },
|
|
{ V('*','K'), 1, "Κ" },
|
|
{ V('*','L'), 1, "Λ" },
|
|
{ V('*','M'), 1, "&Mu:" },
|
|
{ V('*','N'), 1, "Ν" },
|
|
{ V('*','O'), 1, "Ο" },
|
|
{ V('*','P'), 1, "Π" },
|
|
{ V('*','Q'), 1, "Ψ" },
|
|
{ V('*','R'), 1, "Ρ" },
|
|
{ V('*','S'), 1, "Σ" },
|
|
{ V('*','T'), 1, "Τ" },
|
|
{ V('*','U'), 1, "Υ" },
|
|
{ V('*','W'), 1, "Ω" },
|
|
{ V('*','X'), 1, "Χ" },
|
|
{ V('*','Y'), 1, "Η" },
|
|
{ V('*','Z'), 1, "Ζ" },
|
|
{ V('*','a'), 1, "α"},
|
|
{ V('*','b'), 1, "β"},
|
|
{ V('*','c'), 1, "ξ"},
|
|
{ V('*','d'), 1, "δ"},
|
|
{ V('*','e'), 1, "ε"},
|
|
{ V('*','f'), 1, "φ"},
|
|
{ V('*','g'), 1, "γ"},
|
|
{ V('*','h'), 1, "θ"},
|
|
{ V('*','i'), 1, "ι"},
|
|
{ V('*','k'), 1, "κ"},
|
|
{ V('*','l'), 1, "λ"},
|
|
{ V('*','m'), 1, "μ" },
|
|
{ V('*','n'), 1, "ν"},
|
|
{ V('*','o'), 1, "ο"},
|
|
{ V('*','p'), 1, "π"},
|
|
{ V('*','q'), 1, "ψ"},
|
|
{ V('*','r'), 1, "ρ"},
|
|
{ V('*','s'), 1, "σ"},
|
|
{ V('*','t'), 1, "τ"},
|
|
{ V('*','u'), 1, "υ"},
|
|
{ V('*','w'), 1, "ω"},
|
|
{ V('*','x'), 1, "χ"},
|
|
{ V('*','y'), 1, "η"},
|
|
{ V('*','z'), 1, "ζ"},
|
|
{ V('+','-'), 1, "±" }, // not in groff_char(7)
|
|
{ V('+','f'), 1, "φ"}, // phi1, we use the standard phi
|
|
{ V('+','h'), 1, "θ"}, // theta1, we use the standard theta
|
|
{ V('+','p'), 1, "ω"}, // omega1, we use the standard omega
|
|
{ V('1','2'), 1, "½" },
|
|
{ V('1','4'), 1, "¼" },
|
|
{ V('3','4'), 1, "¾" },
|
|
{ V('F','i'), 1, "ffi" }, // ffi ligature
|
|
{ V('F','l'), 1, "ffl" }, // ffl ligature
|
|
{ V('a','p'), 1, "~" },
|
|
{ V('b','r'), 1, "|" },
|
|
{ V('b','u'), 1, "•" },
|
|
{ V('b','v'), 1, "|" },
|
|
{ V('c','i'), 1, "○" }, // circle ### TODO verify
|
|
{ V('c','o'), 1, "©" },
|
|
{ V('c','t'), 1, "¢" },
|
|
{ V('d','e'), 1, "°" },
|
|
{ V('d','g'), 1, "†" },
|
|
{ V('d','i'), 1, "÷" },
|
|
{ V('e','m'), 1, "&emdash;" },
|
|
{ V('e','n'), 1, "&endash;"},
|
|
{ V('e','q'), 1, "=" },
|
|
{ V('e','s'), 1, "∅" },
|
|
{ V('f','f'), 1, "�xFB00;" }, // ff ligature
|
|
{ V('f','i'), 1, "�xFB01;" }, // fi ligature
|
|
{ V('f','l'), 1, "�xFB02;" }, // fl ligature
|
|
{ V('f','m'), 1, "′" },
|
|
{ V('g','a'), 1, "`" },
|
|
{ V('h','y'), 1, "-" },
|
|
{ V('l','c'), 2, "|¯" }, // ### TODO: not in groff_char(7)
|
|
{ V('l','f'), 2, "|_" }, // ### TODO: not in groff_char(7)
|
|
{ V('l','k'), 1, "<FONT SIZE=+2>{</FONT>" }, // ### TODO: not in groff_char(7)
|
|
{ V('m','i'), 1, "-" }, // ### TODO: not in groff_char(7)
|
|
{ V('m','u'), 1, "×" },
|
|
{ V('n','o'), 1, "¬" },
|
|
{ V('o','r'), 1, "|" },
|
|
{ V('p','l'), 1, "+" },
|
|
{ V('r','c'), 2, "¯|" }, // ### TODO: not in groff_char(7)
|
|
{ V('r','f'), 2, "_|" }, // ### TODO: not in groff_char(7)
|
|
{ V('r','g'), 1, "®" },
|
|
{ V('r','k'), 1, "<FONT SIZE=+2>}</FONT>" }, // ### TODO: not in groff_char(7)
|
|
{ V('r','n'), 1, "‾" },
|
|
{ V('r','u'), 1, "_" },
|
|
{ V('s','c'), 1, "§" },
|
|
{ V('s','l'), 1, "/" },
|
|
{ V('s','q'), 2, "□" }, // WHITE SQUARE
|
|
{ V('t','s'), 1, "ς" }, // FINAL SIGMA
|
|
{ V('u','l'), 1, "_" },
|
|
{ V('-','D'), 1, "Ð" },
|
|
{ V('S','d'), 1, "ð" },
|
|
{ V('T','P'), 1, "Þ" },
|
|
{ V('T','p'), 1, "þ" },
|
|
{ V('A','E'), 1, "Æ" },
|
|
{ V('a','e'), 1, "æ" },
|
|
{ V('O','E'), 1, "Œ" },
|
|
{ V('o','e'), 1, "œ" },
|
|
{ V('s','s'), 1, "ß" },
|
|
{ V('\'','A'), 1, "Á" },
|
|
{ V('\'','E'), 1, "É" },
|
|
{ V('\'','I'), 1, "Í" },
|
|
{ V('\'','O'), 1, "Ó" },
|
|
{ V('\'','U'), 1, "Ú" },
|
|
{ V('\'','Y'), 1, "Ý" },
|
|
{ V('\'','a'), 1, "á" },
|
|
{ V('\'','e'), 1, "é" },
|
|
{ V('\'','i'), 1, "í" },
|
|
{ V('\'','o'), 1, "ó" },
|
|
{ V('\'','u'), 1, "ú" },
|
|
{ V('\'','y'), 1, "ý" },
|
|
{ V(':','A'), 1, "Ä" },
|
|
{ V(':','E'), 1, "Ë" },
|
|
{ V(':','I'), 1, "Ï" },
|
|
{ V(':','O'), 1, "Ö" },
|
|
{ V(':','U'), 1, "Ü" },
|
|
{ V(':','a'), 1, "ä" },
|
|
{ V(':','e'), 1, "ë" },
|
|
{ V(':','i'), 1, "ï" },
|
|
{ V(':','o'), 1, "ö" },
|
|
{ V(':','u'), 1, "ü" },
|
|
{ V(':','y'), 1, "ÿ" },
|
|
{ V('^','A'), 1, "Â" },
|
|
{ V('^','E'), 1, "Ê" },
|
|
{ V('^','I'), 1, "Î" },
|
|
{ V('^','O'), 1, "Ô" },
|
|
{ V('^','U'), 1, "Û" },
|
|
{ V('^','a'), 1, "â" },
|
|
{ V('^','e'), 1, "ê" },
|
|
{ V('^','i'), 1, "î" },
|
|
{ V('^','o'), 1, "ô" },
|
|
{ V('^','u'), 1, "û" },
|
|
{ V('`','A'), 1, "À" },
|
|
{ V('`','E'), 1, "È" },
|
|
{ V('`','I'), 1, "Ì" },
|
|
{ V('`','O'), 1, "Ò" },
|
|
{ V('`','U'), 1, "Ù" },
|
|
{ V('`','a'), 1, "à" },
|
|
{ V('`','e'), 1, "è" },
|
|
{ V('`','i'), 1, "ì" },
|
|
{ V('`','o'), 1, "ò" },
|
|
{ V('`','u'), 1, "ù" },
|
|
{ V('~','A'), 1, "Ã" },
|
|
{ V('~','N'), 1, "Ñ" },
|
|
{ V('~','O'), 1, "Õ" },
|
|
{ V('~','a'), 1, "ã" },
|
|
{ V('~','n'), 1, "&ntidle;" },
|
|
{ V('~','o'), 1, "&otidle;" },
|
|
{ V(',','C'), 1, "Ç" },
|
|
{ V(',','c'), 1, "ç" },
|
|
{ V('/','L'), 1, "Ł" },
|
|
{ V('/','l'), 1, "ł" },
|
|
{ V('/','O'), 1, "Ø" },
|
|
{ V('/','o'), 1, "ø" },
|
|
{ V('o','A'), 1, "Å" },
|
|
{ V('o','a'), 1, "å" },
|
|
{ V('a','"'), 1, "\"" },
|
|
{ V('a','-'), 1, "¯" },
|
|
{ V('a','.'), 1, "." },
|
|
{ V('a','^'), 1, "ˆ" },
|
|
{ V('a','a'), 1, "´" },
|
|
{ V('a','b'), 1, "`" },
|
|
{ V('a','c'), 1, "¸" },
|
|
{ V('a','d'), 1, "¨" },
|
|
{ V('a','h'), 1, "˂" }, // caron
|
|
{ V('a','o'), 1, "˚" }, // ring
|
|
{ V('a','~'), 1, "˜" },
|
|
{ V('h','o'), 1, "˛" }, // ogonek
|
|
{ V('.','i'), 1, "ı" }, // dot less i
|
|
{ V('C','s'), 1, "¤" },
|
|
{ V('D','o'), 1, "$" },
|
|
{ V('P','o'), 1, "£" },
|
|
{ V('Y','e'), 1, "¥" },
|
|
{ V('F','n'), 1, "ƒ" },
|
|
{ V('F','o'), 1, "«" },
|
|
{ V('F','c'), 1, "»" },
|
|
{ V('f','o'), 1, "‹" }, // single left guillemet
|
|
{ V('f','c'), 1, "›" }, // single right guillemet
|
|
{ V('r','!'), 1, "&iecl;" },
|
|
{ V('r','?'), 1, "¿" },
|
|
{ V('O','f'), 1, "ª" },
|
|
{ V('O','m'), 1, "º" },
|
|
{ V('p','c'), 1, "·" },
|
|
{ V('S','1'), 1, "¹" },
|
|
{ V('S','2'), 1, "²" },
|
|
{ V('S','3'), 1, "³" },
|
|
{ V('<','-'), 1, "←" },
|
|
{ V('-','>'), 1, "→" },
|
|
{ V('<','>'), 1, "↔" },
|
|
{ V('d','a'), 1, "↓" },
|
|
{ V('u','a'), 1, "↑" },
|
|
{ V('l','A'), 1, "⇐" },
|
|
{ V('r','A'), 1, "⇒" },
|
|
{ V('h','A'), 1, "⇔" },
|
|
{ V('d','A'), 1, "⇓" },
|
|
{ V('u','A'), 1, "⇑" },
|
|
{ V('b','a'), 1, "|" },
|
|
{ V('b','b'), 1, "¦" },
|
|
{ V('t','m'), 1, "™" },
|
|
{ V('d','d'), 1, "‡" },
|
|
{ V('p','s'), 1, "¶" },
|
|
{ V('%','0'), 1, "‰" },
|
|
{ V('f','/'), 1, "⁄" }, // Fraction slash
|
|
{ V('s','d'), 1, "″" },
|
|
{ V('h','a'), 1, "^" },
|
|
{ V('t','i'), 1, "&tidle;" },
|
|
{ V('l','B'), 1, "[" },
|
|
{ V('r','B'), 1, "]" },
|
|
{ V('l','C'), 1, "{" },
|
|
{ V('r','C'), 1, "}" },
|
|
{ V('l','a'), 1, "<" },
|
|
{ V('r','a'), 1, ">" },
|
|
{ V('l','h'), 1, "≤" },
|
|
{ V('r','h'), 1, "≥" },
|
|
{ V('B','q'), 1, "„" },
|
|
{ V('b','q'), 1, "‚" },
|
|
{ V('l','q'), 1, "“" },
|
|
{ V('r','q'), 1, "”" },
|
|
{ V('o','q'), 1, "‘" },
|
|
{ V('c','q'), 1, "’" },
|
|
{ V('a','q'), 1, "'" },
|
|
{ V('d','q'), 1, "\"" },
|
|
{ V('a','t'), 1, "@" },
|
|
{ V('s','h'), 1, "#" },
|
|
{ V('r','s'), 1, "\\" },
|
|
{ V('t','f'), 1, "∴" },
|
|
{ V('~','~'), 1, "≅" },
|
|
{ V('~','='), 1, "≈" },
|
|
{ V('!','='), 1, "≠" },
|
|
{ V('<','='), 1, "≤" },
|
|
{ V('=','='), 1, "≡" },
|
|
{ V('=','~'), 1, "≅" }, // ### TODO: verify
|
|
{ V('>','='), 1, "≥" },
|
|
{ V('A','N'), 1, "∧" },
|
|
{ V('O','R'), 1, "∨" },
|
|
{ V('t','e'), 1, "∃" },
|
|
{ V('f','a'), 1, "∀" },
|
|
{ V('A','h'), 1, "ℵ" },
|
|
{ V('I','m'), 1, "ℑ" },
|
|
{ V('R','e'), 1, "ℜ" },
|
|
{ V('i','f'), 1, "∞" },
|
|
{ V('m','d'), 1, "⋅" },
|
|
{ V('m','o'), 1, "∆" }, // element ### TODO verify
|
|
{ V('n','m'), 1, "∉" },
|
|
{ V('p','t'), 1, "∝" },
|
|
{ V('p','p'), 1, "⊥" },
|
|
{ V('s','b'), 1, "⊂" },
|
|
{ V('s','p'), 1, "⊃" },
|
|
{ V('i','b'), 1, "⊆" },
|
|
{ V('i','p'), 1, "⊇" },
|
|
{ V('i','s'), 1, "∫" },
|
|
{ V('s','r'), 1, "√" },
|
|
{ V('p','d'), 1, "∂" },
|
|
{ V('c','*'), 1, "⊗" },
|
|
{ V('c','+'), 1, "⊕" },
|
|
{ V('c','a'), 1, "∩" },
|
|
{ V('c','u'), 1, "∪" },
|
|
{ V('g','r'), 1, "V" }, // gradient ### TODO Where in Unicode?
|
|
{ V('C','R'), 1, "↵" },
|
|
{ V('s','t'), 2, "-)" }, // "such that" ### TODO Where in Unicode?
|
|
{ V('/','_'), 1, "∠" },
|
|
{ V('w','p'), 1, "℘" },
|
|
{ V('l','z'), 1, "◊" },
|
|
{ V('a','n'), 1, "-" }, // "horizontal arrow extension" ### TODO Where in Unicode?
|
|
};
|
|
|
|
/* default: print code */
|
|
|
|
|
|
/* static char eqndelimopen=0, eqndelimclose=0; */
|
|
static char escapesym='\\', nobreaksym='\'', controlsym='.', fieldsym=0, padsym=0;
|
|
|
|
static char *buffer=NULL;
|
|
static int buffpos=0, buffmax=0;
|
|
static bool scaninbuff=false;
|
|
static int itemdepth=0;
|
|
static int section=0;
|
|
static int dl_set[20]= { 0 };
|
|
static bool still_dd=0;
|
|
static int tabstops[20] = { 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96 };
|
|
static int maxtstop=12;
|
|
static int curpos=0;
|
|
|
|
static char *scan_troff(char *c, bool san, char **result);
|
|
static char *scan_troff_mandoc(char *c, bool san, char **result);
|
|
|
|
static TQValueList<char*> s_argumentList;
|
|
|
|
static TQCString htmlPath, cssPath;
|
|
|
|
static TQCString s_dollarZero; // Value of $0
|
|
|
|
void setResourcePath(const TQCString& _htmlPath, const TQCString& _cssPath)
|
|
{
|
|
htmlPath=_htmlPath;
|
|
cssPath=_cssPath;
|
|
}
|
|
|
|
static void fill_old_character_definitions( void )
|
|
{
|
|
for (size_t i = 0; i < sizeof(standardchar)/sizeof(CSTRDEF); i++)
|
|
{
|
|
const int nr = standardchar[i].nr;
|
|
const char temp[3] = { nr / 256, nr % 256, 0 };
|
|
TQCString name( temp );
|
|
s_characterDefinitionMap.insert( name, StringDefinition( standardchar[i].slen, standardchar[i].st ) );
|
|
}
|
|
}
|
|
|
|
static char outbuffer[NULL_TERMINATED(HUGE_STR_MAX)];
|
|
static int no_newline_output=0;
|
|
static int newline_for_fun=0;
|
|
static bool output_possible=false;
|
|
|
|
static const char *includedirs[] = {
|
|
"/usr/include",
|
|
"/usr/include/sys",
|
|
"/usr/local/include",
|
|
"/opt/local/include",
|
|
"/usr/ccs",
|
|
"/usr/X11R6/include",
|
|
"/usr/openwin/include",
|
|
"/usr/include/g++",
|
|
0
|
|
};
|
|
|
|
static bool ignore_links=false;
|
|
|
|
static void add_links(char *c)
|
|
{
|
|
/*
|
|
** Add the links to the output.
|
|
** At the moment the following are recognized:
|
|
**
|
|
** name(*) -> ../man?/name.*
|
|
** method://string -> method://string
|
|
** www.host.name -> http://www.host.name
|
|
** ftp.host.name -> ftp://ftp.host.name
|
|
** name@host -> mailto:name@host
|
|
** <name.h> -> file:/usr/include/name.h (guess)
|
|
**
|
|
** Other possible links to add in the future:
|
|
**
|
|
** /dir/dir/file -> file:/dir/dir/file
|
|
*/
|
|
if (ignore_links)
|
|
{
|
|
output_real(c);
|
|
return;
|
|
}
|
|
|
|
int i,j,nr;
|
|
char *f, *h;
|
|
char *g;
|
|
const int numtests=6; // Nmber of tests
|
|
char *idtest[numtests]; // url, mailto, www, ftp, manpage, C header file
|
|
bool ok;
|
|
/* search for (section) */
|
|
nr=0;
|
|
idtest[0]=strstr(c+1,"://");
|
|
idtest[1]=strchr(c+1,'@');
|
|
idtest[2]=strstr(c,"www.");
|
|
idtest[3]=strstr(c,"ftp.");
|
|
idtest[4]=strchr(c+1,'(');
|
|
idtest[5]=strstr(c+1,".h>");
|
|
for (i=0; i<numtests; ++i) nr += (idtest[i]!=NULL);
|
|
while (nr) {
|
|
j=-1;
|
|
for (i=0; i<numtests; i++)
|
|
if (idtest[i] && (j<0 || idtest[i]<idtest[j])) j=i;
|
|
switch (j) {
|
|
case 5: { /* <name.h> */
|
|
f=idtest[5];
|
|
h=f+2;
|
|
g=f;
|
|
while (g>c && g[-1]!=';') g--;
|
|
bool wrote_include = false;
|
|
|
|
if (g!=c) {
|
|
|
|
TQCString dir;
|
|
TQCString file(g, h - g + 1);
|
|
file = file.stripWhiteSpace();
|
|
for (int index = 0; includedirs[index]; index++) {
|
|
TQCString str = TQCString(includedirs[index]) + "/" + file;
|
|
if (!access(str, R_OK)) {
|
|
dir = includedirs[index];
|
|
break;
|
|
}
|
|
}
|
|
if (!dir.isEmpty()) {
|
|
|
|
char t;
|
|
t=*g;
|
|
*g=0;
|
|
output_real(c);
|
|
*g=t;*h=0;
|
|
|
|
TQCString str;
|
|
str.sprintf("<A HREF=\"file:%s/%s\">%s</A>>", dir.data(), file.data(), file.data());
|
|
output_real(str.data());
|
|
c=f+6;
|
|
wrote_include = true;
|
|
}
|
|
|
|
}
|
|
|
|
if (!wrote_include) {
|
|
f[5]=0;
|
|
output_real(c);
|
|
f[5]=';';
|
|
c=f+5;
|
|
}
|
|
}
|
|
break;
|
|
case 4: /* manpage */
|
|
f=idtest[j];
|
|
/* check section */
|
|
g=strchr(f,')');
|
|
// The character before f must alphanumeric, the end of a HTML tag or the end of a
|
|
if (g!=NULL && f>c && (g-f)<12 && (isalnum(f[-1]) || f[-1]=='>' || ( f[-1] == ';' ) ) &&
|
|
isdigit(f[1]) && f[1]!='0' && ((g-f)<=2 || isalpha(f[2])))
|
|
{
|
|
ok = TRUE;
|
|
h = f+2;
|
|
while (h<g)
|
|
{
|
|
if (!isalnum(*h++))
|
|
{
|
|
ok = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
ok = false;
|
|
|
|
h = f - 1;
|
|
if ( ok )
|
|
{
|
|
// Skip
|
|
kdDebug(7107) << "BEFORE SECTION:" << *h << endl;
|
|
if ( ( h > c + 5 ) && ( ! memcmp( h-5, " ", 6 ) ) )
|
|
{
|
|
h -= 6;
|
|
kdDebug(7107) << "Skip " << endl;
|
|
}
|
|
else if ( *h == ';' )
|
|
{
|
|
// Not a non-breaking space, so probably not ok
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
if (ok)
|
|
{
|
|
/* this might be a link */
|
|
/* skip html makeup */
|
|
while (h>c && *h=='>') {
|
|
while (h!=c && *h!='<') h--;
|
|
if (h!=c) h--;
|
|
}
|
|
if (isalnum(*h)) {
|
|
char t,sec, *e;
|
|
TQString subsec; // ### TODO avoid using TQString, as we do not know the encoding
|
|
TQString fstr(f); // ### TODO avoid using TQString, as we do not know the encoding
|
|
e=h+1;
|
|
sec=f[1];
|
|
subsec=f[2];
|
|
int index = fstr.find(')', 2);
|
|
if (index != -1)
|
|
subsec = fstr.mid(2, index - 2);
|
|
else // No closing ')' found, take first character as subsection.
|
|
subsec = fstr.mid(2, 1);
|
|
while (h>c && (isalnum(h[-1]) || h[-1]=='_'
|
|
|| h[-1]==':' || h[-1]=='-' || h[-1]=='.'))
|
|
h--;
|
|
t=*h;
|
|
*h='\0';
|
|
output_real(c);
|
|
*h=t;
|
|
t=*e;
|
|
*e='\0';
|
|
TQCString str;
|
|
if (subsec.isEmpty())
|
|
str.sprintf("<A HREF=\"man:%s(%c)\">%s</A>", h, sec, h);
|
|
else
|
|
str.sprintf("<A HREF=\"man:%s(%c%s)\">%s</A>", h, sec, subsec.lower().latin1(), h);
|
|
output_real(str.data());
|
|
*e=t;
|
|
c=e;
|
|
}
|
|
}
|
|
*f='\0';
|
|
output_real(c);
|
|
*f='(';
|
|
idtest[4]=f-1;
|
|
c=f;
|
|
break; /* manpage */
|
|
case 3: /* ftp */
|
|
case 2: /* www */
|
|
g=f=idtest[j];
|
|
while (*g && (isalnum(*g) || *g=='_' || *g=='-' || *g=='+' ||
|
|
*g=='.' || *g=='/')) g++;
|
|
if (g[-1]=='.') g--;
|
|
if (g-f>4) {
|
|
char t;
|
|
t=*f; *f='\0';
|
|
output_real(c);
|
|
*f=t; t=*g;*g='\0';
|
|
TQCString str;
|
|
str.sprintf("<A HREF=\"%s://%s\">%s</A>", ((j==3)?"ftp":"http"), f, f);
|
|
output_real(str.data());
|
|
*g=t;
|
|
c=g;
|
|
} else {
|
|
f[3]='\0';
|
|
output_real(c);
|
|
c=f+3;
|
|
f[3]='.';
|
|
}
|
|
break;
|
|
case 1: /* mailto */
|
|
g=f=idtest[1];
|
|
while (g>c && (isalnum(g[-1]) || g[-1]=='_' || g[-1]=='-' ||
|
|
g[-1]=='+' || g[-1]=='.' || g[-1]=='%')) g--;
|
|
if (g-7>=c && g[-1]==':')
|
|
{
|
|
// We have perhaps an email address starting with mailto:
|
|
if (!tqstrncmp("mailto:",g-7,7))
|
|
g-=7;
|
|
}
|
|
h=f+1;
|
|
while (*h && (isalnum(*h) || *h=='_' || *h=='-' || *h=='+' ||
|
|
*h=='.')) h++;
|
|
if (*h=='.') h--;
|
|
if (h-f>4 && f-g>1) {
|
|
char t;
|
|
t=*g;
|
|
*g='\0';
|
|
output_real(c);
|
|
*g=t;t=*h;*h='\0';
|
|
TQCString str;
|
|
str.sprintf("<A HREF=\"mailto:%s\">%s</A>", g, g);
|
|
output_real(str.data());
|
|
*h=t;
|
|
c=h;
|
|
} else {
|
|
*f='\0';
|
|
output_real(c);
|
|
*f='@';
|
|
idtest[1]=c;
|
|
c=f;
|
|
}
|
|
break;
|
|
case 0: /* url */
|
|
g=f=idtest[0];
|
|
while (g>c && isalpha(g[-1]) && islower(g[-1])) g--;
|
|
h=f+3;
|
|
while (*h && !isspace(*h) && *h!='<' && *h!='>' && *h!='"' &&
|
|
*h!='&') h++;
|
|
if (f-g>2 && f-g<7 && h-f>3) {
|
|
char t;
|
|
t=*g;
|
|
*g='\0';
|
|
output_real(c);
|
|
*g=t; t=*h; *h='\0';
|
|
TQCString str;
|
|
str.sprintf("<A HREF=\"%s\">%s</A>", g, g);
|
|
output_real(str.data());
|
|
*h=t;
|
|
c=h;
|
|
} else {
|
|
f[1]='\0';
|
|
output_real(c);
|
|
f[1]='/';
|
|
c=f+1;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
nr=0;
|
|
if (idtest[0] && idtest[0]<=c) idtest[0]=strstr(c+1,"://");
|
|
if (idtest[1] && idtest[1]<=c) idtest[1]=strchr(c+1,'@');
|
|
if (idtest[2] && idtest[2]<c) idtest[2]=strstr(c,"www.");
|
|
if (idtest[3] && idtest[3]<c) idtest[3]=strstr(c,"ftp.");
|
|
if (idtest[4] && idtest[4]<=c) idtest[4]=strchr(c+1,'(');
|
|
if (idtest[5] && idtest[5]<=c) idtest[5]=strstr(c+1,".h>");
|
|
for (i=0; i<numtests; i++) nr += (idtest[i]!=NULL);
|
|
}
|
|
output_real(c);
|
|
}
|
|
|
|
static TQCString current_font;
|
|
static int current_size=0;
|
|
static int fillout=1;
|
|
|
|
static void out_html(const char *c)
|
|
{
|
|
if (!c) return;
|
|
|
|
// Added, probably due to the const?
|
|
char *c2 = tqstrdup(c);
|
|
char *c3 = c2;
|
|
|
|
static int obp=0;
|
|
|
|
if (no_newline_output) {
|
|
int i=0;
|
|
no_newline_output=1;
|
|
while (c2[i]) {
|
|
if (!no_newline_output) c2[i-1]=c2[i];
|
|
if (c2[i]=='\n') no_newline_output=0;
|
|
i++;
|
|
}
|
|
if (!no_newline_output) c2[i-1]=0;
|
|
}
|
|
if (scaninbuff) {
|
|
while (*c2) {
|
|
if (buffpos>=buffmax) {
|
|
char *h = new char[buffmax*2];
|
|
|
|
#ifdef SIMPLE_MAN2HTML
|
|
if (!h)
|
|
{
|
|
cerr << "Memory full, cannot output!" << endl;
|
|
exit(1);
|
|
}
|
|
#else
|
|
// modern compiler do not return a NULL for a new
|
|
#endif
|
|
memcpy(h, buffer, buffmax);
|
|
delete [] buffer;
|
|
buffer=h;
|
|
buffmax=buffmax*2;
|
|
}
|
|
buffer[buffpos++]=*c2++;
|
|
}
|
|
} else
|
|
if (output_possible) {
|
|
while (*c2) {
|
|
outbuffer[obp++]=*c2;
|
|
if (*c=='\n' || obp >= HUGE_STR_MAX) {
|
|
outbuffer[obp]='\0';
|
|
add_links(outbuffer);
|
|
obp=0;
|
|
}
|
|
c2++;
|
|
}
|
|
}
|
|
delete [] c3;
|
|
}
|
|
|
|
static TQCString set_font( const TQCString& name )
|
|
{
|
|
// Every font but R (Regular) creates <span> elements
|
|
TQCString markup;
|
|
if ( current_font != "R" && !current_font.isEmpty() )
|
|
markup += "</span>";
|
|
const uint len = name.length();
|
|
bool fontok = true;
|
|
if ( len == 1 )
|
|
{
|
|
const char lead = name[0];
|
|
switch (lead)
|
|
{
|
|
case 'P': // Palatino?
|
|
case 'R': break; // regular, do nothing
|
|
case 'I': markup += "<span style=\"font-style:italic\">"; break;
|
|
case 'B': markup += "<span style=\"font-weight:bold\">"; break;
|
|
case 'L': markup += "<span style=\"font-family:monospace\">"; break; // ### What's L?
|
|
default: fontok = false;
|
|
}
|
|
}
|
|
else if ( len == 2 )
|
|
{
|
|
if ( name == "BI" )
|
|
markup += "<span style=\"font-style:italic;font-weight:bold\">";
|
|
// Courier
|
|
else if ( name == "CR" )
|
|
markup += "<span style=\"font-family:monospace\">";
|
|
else if ( name == "CW" ) // CW is used by pod2man(1) (alias PerlDoc)
|
|
markup += "<span style=\"font-family:monospace\">";
|
|
else if ( name == "CI" )
|
|
markup += "<span style=\"font-family:monospace;font-style:italic\">";
|
|
else if ( name == "CB" )
|
|
markup += "<span style=\"font-family:monospace;font-weight:bold\">";
|
|
// Times
|
|
else if ( name == "TR" )
|
|
markup += "<span style=\"font-family:serif\">";
|
|
else if ( name == "TI" )
|
|
markup += "<span style=\"font-family:serif;font-style:italic\">";
|
|
else if ( name == "TB" )
|
|
markup += "<span style=\"font-family:serif;font-weight:bold\">";
|
|
// Helvetica
|
|
else if ( name == "HR" )
|
|
markup += "<span style=\"font-family:sans-serif\">";
|
|
else if ( name == "HI" )
|
|
markup += "<span style=\"font-family:sans-serif;font-style:italic\">";
|
|
else if ( name == "HB" )
|
|
markup += "<span style=\"font-family:sans-serif;font-weight:bold\">";
|
|
else
|
|
fontok = false;
|
|
}
|
|
else if ( len == 3 )
|
|
{
|
|
if ( name == "CBI" )
|
|
markup += "<span style=\"font-family:monospace;font-style:italic;font-weight:bold\">";
|
|
else if ( name == "TBI" )
|
|
markup += "<span style=\"font-family:serif;font-style:italic;font-weight:bold\">";
|
|
else if ( name == "HBI" )
|
|
markup += "<span style=\"font-family:sans-serif;font-style:italic;font-weight:bold\">";
|
|
}
|
|
if (fontok)
|
|
current_font = name;
|
|
else
|
|
current_font = "R"; // Still nothing, then it is 'R' (Regular)
|
|
return markup;
|
|
}
|
|
|
|
/// \deprecated
|
|
static TQCString set_font( const char ch )
|
|
#ifndef SIMPLE_MAN2HTML
|
|
KDE_DEPRECATED;
|
|
|
|
static TQCString set_font( const char ch )
|
|
#endif
|
|
{
|
|
const TQCString name = &ch;
|
|
return set_font( name );
|
|
}
|
|
|
|
static TQCString change_to_size(int nr)
|
|
{
|
|
switch (nr)
|
|
{
|
|
case '0': case '1': case '2': case '3': case '4': case '5': case '6':
|
|
case '7': case '8': case '9': nr=nr-'0'; break;
|
|
case '\0': break;
|
|
default: nr=current_size+nr; if (nr>9) nr=9; if (nr< -9) nr=-9; break;
|
|
}
|
|
if ( nr == current_size )
|
|
return "";
|
|
const TQCString font ( current_font );
|
|
TQCString markup;
|
|
markup = set_font("R");
|
|
if (current_size)
|
|
markup += "</FONT>";
|
|
current_size=nr;
|
|
if (nr)
|
|
{
|
|
markup += "<FONT SIZE=\"";
|
|
if (nr>0)
|
|
markup += '+';
|
|
else
|
|
{
|
|
markup += '-';
|
|
nr=-nr;
|
|
}
|
|
markup += char( nr + '0' );
|
|
markup += "\">";
|
|
}
|
|
markup += set_font( font );
|
|
return markup;
|
|
}
|
|
|
|
/* static int asint=0; */
|
|
static int intresult=0;
|
|
|
|
#define SKIPEOL while (*c && *c++!='\n')
|
|
|
|
static bool skip_escape=false;
|
|
static bool single_escape=false;
|
|
|
|
static char *scan_escape_direct( char *c, TQCString& cstr );
|
|
|
|
/**
|
|
* scan a named character
|
|
* param c position
|
|
*/
|
|
static TQCString scan_named_character( char*& c )
|
|
{
|
|
TQCString name;
|
|
if ( *c == '(' )
|
|
{
|
|
// \*(ab Name of two characters
|
|
if ( c[1] == escapesym )
|
|
{
|
|
TQCString cstr;
|
|
c = scan_escape_direct( c+2, cstr );
|
|
// ### HACK: as we convert characters too early to HTML, we need to support more than 2 characters here and assume that all characters passed by the variable are to be used.
|
|
name = cstr;
|
|
}
|
|
else
|
|
{
|
|
name+=c[1];
|
|
name+=c[2];
|
|
c+=3;
|
|
}
|
|
}
|
|
else if ( *c == '[' )
|
|
{
|
|
// \*[long_name] Long name
|
|
// Named character groff(7)
|
|
// We must find the ] to get a name
|
|
c++;
|
|
while ( *c && *c != ']' && *c != '\n' )
|
|
{
|
|
if ( *c == escapesym )
|
|
{
|
|
TQCString cstr;
|
|
c = scan_escape_direct( c+1, cstr );
|
|
const int result = cstr.find(']');
|
|
if ( result == -1 )
|
|
name += cstr;
|
|
else
|
|
{
|
|
// Note: we drop the characters after the ]
|
|
name += cstr.left( result );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
name+=*c;
|
|
c++;
|
|
}
|
|
}
|
|
if ( !*c || *c == '\n' )
|
|
{
|
|
kdDebug(7107) << "Found linefeed! Could not parse character name: " << name << endl;
|
|
return "";
|
|
}
|
|
c++;
|
|
}
|
|
else if ( *c =='C' || c[1]== '\'' )
|
|
{
|
|
// \C'name'
|
|
c+=2;
|
|
while ( *c && *c != '\'' && *c != '\n' )
|
|
{
|
|
if ( *c == escapesym )
|
|
{
|
|
TQCString cstr;
|
|
c = scan_escape_direct( c+1, cstr );
|
|
const int result = cstr.find('\'');
|
|
if ( result == -1 )
|
|
name += cstr;
|
|
else
|
|
{
|
|
// Note: we drop the characters after the ]
|
|
name += cstr.left( result );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
name+=*c;
|
|
c++;
|
|
}
|
|
}
|
|
if ( !*c || *c == '\n' )
|
|
{
|
|
kdDebug(7107) << "Found linefeed! Could not parse (\\C mode) character name: " << name << endl;
|
|
return "";
|
|
}
|
|
c++;
|
|
}
|
|
// Note: characters with a one character length name doe not exist, as they would collide with other escapes
|
|
|
|
// Now we have the name, let us find it between the string names
|
|
TQMap<TQCString,StringDefinition>::iterator it=s_characterDefinitionMap.find(name);
|
|
if (it==s_characterDefinitionMap.end())
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: cannot find character with name: " << name << endl;
|
|
// No output, as an undefined string is empty by default
|
|
return "";
|
|
}
|
|
else
|
|
{
|
|
kdDebug(7107) << "Character with name: \"" << name << "\" => " << (*it).m_output << endl;
|
|
return (*it).m_output;
|
|
}
|
|
}
|
|
|
|
static TQCString scan_named_string(char*& c)
|
|
{
|
|
TQCString name;
|
|
if ( *c == '(' )
|
|
{
|
|
// \*(ab Name of two characters
|
|
if ( c[1] == escapesym )
|
|
{
|
|
TQCString cstr;
|
|
c = scan_escape_direct( c+2, cstr );
|
|
kdDebug(7107) << "\\(" << cstr << endl;
|
|
// ### HACK: as we convert characters too early to HTML, we need to support more than 2 characters here and assume that all characters passed by the variable are to be used.
|
|
name = cstr;
|
|
}
|
|
else
|
|
{
|
|
name+=c[1];
|
|
name+=c[2];
|
|
c+=3;
|
|
}
|
|
}
|
|
else if ( *c == '[' )
|
|
{
|
|
// \*[long_name] Long name
|
|
// Named character groff(7)
|
|
// We must find the ] to get a name
|
|
c++;
|
|
while ( *c && *c != ']' && *c != '\n' )
|
|
{
|
|
if ( *c == escapesym )
|
|
{
|
|
TQCString cstr;
|
|
c = scan_escape_direct( c+1, cstr );
|
|
const int result = cstr.find(']');
|
|
if ( result == -1 )
|
|
name += cstr;
|
|
else
|
|
{
|
|
// Note: we drop the characters after the ]
|
|
name += cstr.left( result );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
name+=*c;
|
|
c++;
|
|
}
|
|
}
|
|
if ( !*c || *c == '\n' )
|
|
{
|
|
kdDebug(7107) << "Found linefeed! Could not parse string name: " << name << endl;
|
|
return "";
|
|
}
|
|
c++;
|
|
}
|
|
else
|
|
{
|
|
// \*a Name of one character
|
|
name+=*c;
|
|
c++;
|
|
}
|
|
// Now we have the name, let us find it between the string names
|
|
TQMap<TQCString,StringDefinition>::iterator it=s_stringDefinitionMap.find(name);
|
|
if (it==s_stringDefinitionMap.end())
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: cannot find string with name: " << name << endl;
|
|
// No output, as an undefined string is empty by default
|
|
return "";
|
|
}
|
|
else
|
|
{
|
|
kdDebug(7107) << "String with name: \"" << name << "\" => " << (*it).m_output << endl;
|
|
return (*it).m_output;
|
|
}
|
|
}
|
|
|
|
static TQCString scan_dollar_parameter(char*& c)
|
|
{
|
|
unsigned int argno = 0; // No dollar argument number yet!
|
|
if ( *c == '0' )
|
|
{
|
|
//kdDebug(7107) << "$0" << endl;
|
|
c++;
|
|
return s_dollarZero;
|
|
}
|
|
else if ( *c >= '1' && *c <= '9' )
|
|
{
|
|
//kdDebug(7107) << "$ direct" << endl;
|
|
argno = ( *c - '0' );
|
|
c++;
|
|
}
|
|
else if ( *c == '(' )
|
|
{
|
|
//kdDebug(7107) << "$(" << endl;
|
|
if ( c[1] && c[2] && c[1] >= '0' && c[1] <= '9' && c[2] >= '0' && c[2] <= '9' )
|
|
{
|
|
argno = ( c[1] - '0' ) * 10 + ( c[2] - '0' );
|
|
c += 3;
|
|
}
|
|
else
|
|
{
|
|
if ( !c[1] )
|
|
c++;
|
|
else if ( !c[2] )
|
|
c+=2;
|
|
else
|
|
c += 3;
|
|
return "";
|
|
}
|
|
}
|
|
else if ( *c == '[' )
|
|
{
|
|
//kdDebug(7107) << "$[" << endl;
|
|
argno = 0;
|
|
c++;
|
|
while ( *c && *c>='0' && *c<='9' && *c!=']' )
|
|
{
|
|
argno *= 10;
|
|
argno += ( *c - '0' );
|
|
c++;
|
|
}
|
|
if ( *c != ']' )
|
|
{
|
|
return "";
|
|
}
|
|
c++;
|
|
}
|
|
else if ( ( *c == '*' ) || ( *c == '@' ) )
|
|
{
|
|
const bool quote = ( *c == '@' );
|
|
TQValueList<char*>::const_iterator it = s_argumentList.begin();
|
|
TQCString param;
|
|
bool space = false;
|
|
for ( ; it != s_argumentList.end(); ++it )
|
|
{
|
|
if (space)
|
|
param += " ";
|
|
if (quote)
|
|
param += '\"'; // Not as HTML, as it could be used by macros !
|
|
param += (*it);
|
|
if (quote)
|
|
param += '\"'; // Not as HTML, as it could be used by macros!
|
|
space = true;
|
|
}
|
|
c++;
|
|
return param;
|
|
}
|
|
else
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: unknown parameter $" << *c << endl;
|
|
return "";
|
|
}
|
|
//kdDebug(7107) << "ARG $" << argno << endl;
|
|
if ( !s_argumentList.isEmpty() && argno > 0 )
|
|
{
|
|
//kdDebug(7107) << "ARG $" << argno << " OK!" << endl;
|
|
argno--;
|
|
if ( argno >= s_argumentList.size() )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: cannot find parameter $" << (argno+1) << endl;
|
|
return "";
|
|
}
|
|
|
|
return s_argumentList[argno];
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/// return the value of read-only number registers
|
|
static int read_only_number_register( const TQCString& name )
|
|
{
|
|
// Internal read-only variables
|
|
if ( name == ".$" )
|
|
{
|
|
kdDebug(7107) << "\\n[.$] == " << s_argumentList.size() << endl;
|
|
return s_argumentList.size();
|
|
}
|
|
else if ( name == ".g" )
|
|
return 0; // We are not groff(1)
|
|
else if ( name == ".s" )
|
|
return current_size;
|
|
#if 0
|
|
// ### TODO: map the fonts to a number
|
|
else if ( name == ".f" )
|
|
return current_font;
|
|
#endif
|
|
else if ( name == ".P" )
|
|
return 0; // We are not printing
|
|
else if ( name == ".A" )
|
|
return s_nroff;
|
|
#ifndef SIMPLE_MAN2HTML
|
|
// Special KDE KIO man:
|
|
else if ( name == ".TDE_VERSION_MAJOR" )
|
|
return TDE_VERSION_MAJOR;
|
|
else if ( name == ".TDE_VERSION_MINOR" )
|
|
return TDE_VERSION_MINOR;
|
|
else if ( name == ".TDE_VERSION_RELEASE" )
|
|
return TDE_VERSION_RELEASE;
|
|
else if ( name == ".TDE_VERSION" )
|
|
return TDE_VERSION;
|
|
#endif
|
|
// ### TODO: should .T be set to "html"? But we are not the HTML post-processor. :-(
|
|
|
|
// ### TODO: groff defines much more read-only number registers
|
|
#ifndef SIMPLE_MAN2HTML
|
|
kdDebug(7107) << "EXCEPTION: unknown read-only number register: " << name << endl;
|
|
#endif
|
|
|
|
return 0; // Undefined variable
|
|
|
|
}
|
|
|
|
/// get the value of a number register and auto-increment if asked
|
|
static int scan_number_register( char*& c)
|
|
{
|
|
int sign = 0; // Sign for auto-increment (if any)
|
|
switch (*c)
|
|
{
|
|
case '+': sign = 1; c++; break;
|
|
case '-': sign = -1; c++; break;
|
|
default: break;
|
|
}
|
|
TQCString name;
|
|
if ( *c == '[' )
|
|
{
|
|
c++;
|
|
if ( *c == '+' )
|
|
{
|
|
sign = 1;
|
|
c++;
|
|
}
|
|
else if ( *c == '-' )
|
|
{
|
|
sign = -1;
|
|
c++;
|
|
}
|
|
while ( *c && *c != ']' && *c != '\n' )
|
|
{
|
|
// ### TODO: a \*[string] could be inside and should be processed
|
|
name+=*c;
|
|
c++;
|
|
}
|
|
if ( !*c || *c == '\n' )
|
|
{
|
|
kdDebug(7107) << "Found linefeed! Could not parse number register name: " << name << endl;
|
|
return 0;
|
|
}
|
|
c++;
|
|
}
|
|
else if ( *c == '(' )
|
|
{
|
|
c++;
|
|
if ( *c == '+' )
|
|
{
|
|
sign = 1;
|
|
c++;
|
|
}
|
|
else if ( *c == '-' )
|
|
{
|
|
sign = -1;
|
|
c++;
|
|
}
|
|
name+=c[0];
|
|
name+=c[1];
|
|
c+=2;
|
|
}
|
|
else
|
|
{
|
|
name += *c;
|
|
c++;
|
|
}
|
|
if ( name[0] == '.' )
|
|
{
|
|
return read_only_number_register( name );
|
|
}
|
|
else
|
|
{
|
|
TQMap< TQCString, NumberDefinition >::iterator it = s_numberDefinitionMap.find( name );
|
|
if ( it == s_numberDefinitionMap.end() )
|
|
{
|
|
return 0; // Undefined variable
|
|
}
|
|
else
|
|
{
|
|
(*it).m_value += sign * (*it).m_increment;
|
|
return (*it).m_value;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// get and set font
|
|
static TQCString scan_named_font( char*& c )
|
|
{
|
|
TQCString name;
|
|
if ( *c == '(' )
|
|
{
|
|
// \f(ab Name of two characters
|
|
if ( c[1] == escapesym )
|
|
{
|
|
TQCString cstr;
|
|
c = scan_escape_direct( c+2, cstr );
|
|
kdDebug(7107) << "\\(" << cstr << endl;
|
|
// ### HACK: as we convert characters too early to HTML, we need to support more than 2 characters here and assume that all characters passed by the variable are to be used.
|
|
name = cstr;
|
|
}
|
|
else
|
|
{
|
|
name+=c[1];
|
|
name+=c[2];
|
|
c+=3;
|
|
}
|
|
}
|
|
else if ( *c == '[' )
|
|
{
|
|
// \f[long_name] Long name
|
|
// We must find the ] to get a name
|
|
c++;
|
|
while ( *c && *c != ']' && *c != '\n' )
|
|
{
|
|
if ( *c == escapesym )
|
|
{
|
|
TQCString cstr;
|
|
c = scan_escape_direct( c+1, cstr );
|
|
const int result = cstr.find(']');
|
|
if ( result == -1 )
|
|
name += cstr;
|
|
else
|
|
{
|
|
// Note: we drop the characters after the ]
|
|
name += cstr.left( result );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
name+=*c;
|
|
c++;
|
|
}
|
|
}
|
|
if ( !*c || *c == '\n' )
|
|
{
|
|
kdDebug(7107) << "Found linefeed! Could not parse font name: " << name << endl;
|
|
return "";
|
|
}
|
|
c++;
|
|
}
|
|
else
|
|
{
|
|
// \fa Font name with one character or one digit
|
|
// ### HACK do *not* use: name = *c; or name would be empty
|
|
name += *c;
|
|
c++;
|
|
}
|
|
//kdDebug(7107) << "FONT NAME: " << name << endl;
|
|
// Now we have the name, let us find the font
|
|
bool ok = false;
|
|
const unsigned int number = name.toUInt( &ok );
|
|
if ( ok )
|
|
{
|
|
if ( number < 5 )
|
|
{
|
|
const char* fonts[] = { "R", "I", "B", "BI", "CR" }; // Regular, Italic, Bold, Bold Italic, Courier regular
|
|
name = fonts[ number ];
|
|
}
|
|
else
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: font has too big number: " << name << " => " << number << endl;
|
|
name = "R"; // Let assume Regular
|
|
}
|
|
}
|
|
else if ( name.isEmpty() )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: font has no name: " << name << endl;
|
|
name = "R"; // Let assume Regular
|
|
}
|
|
if ( !skip_escape )
|
|
return set_font( name );
|
|
else
|
|
return "";
|
|
}
|
|
|
|
static TQCString scan_number_code( char*& c )
|
|
{
|
|
TQCString number;
|
|
if ( *c != '\'' )
|
|
return "";
|
|
while ( *c && ( *c != '\n' ) && ( *c != '\'' ) )
|
|
{
|
|
number += *c;
|
|
c++;
|
|
}
|
|
bool ok = false;
|
|
unsigned int result = number.toUInt( &ok );
|
|
if ( ( result < ' ' ) || ( result > 65535 ) )
|
|
return "";
|
|
else if ( result == '\t' )
|
|
{
|
|
curpos += 8;
|
|
curpos &= 0xfff8;
|
|
return "\t";
|
|
}
|
|
number.setNum( result );
|
|
number.prepend( "&#" );
|
|
number.append( ";" );
|
|
curpos ++;
|
|
return number;
|
|
}
|
|
|
|
// ### TODO known missing escapes from groff(7):
|
|
// ### TODO \& \! \) \: \R
|
|
|
|
static char *scan_escape_direct( char *c, TQCString& cstr )
|
|
{
|
|
bool exoutputp;
|
|
bool exskipescape;
|
|
int i,j;
|
|
bool cplusplus = true; // Should the c++ be done at the end of the function
|
|
|
|
cstr = "";
|
|
intresult=0;
|
|
switch (*c) {
|
|
case 'e': cstr = "\\"; curpos++;break; // ### FIXME: it should be the current escape symbol
|
|
case '0': // ### TODO Where in Unicode? (space of digit width)
|
|
case '~': // non-breakable-space (resizeable!)
|
|
case ' ':
|
|
case '|': // half-non-breakable-space
|
|
case '^': // quarter-non-breakable-space
|
|
cstr = " "; curpos++; break;
|
|
case '"': SKIPEOL; c--; break;
|
|
// ### TODO \# like \" but does not ignore the end of line (groff(7))
|
|
case '$':
|
|
{
|
|
c++;
|
|
cstr = scan_dollar_parameter( c );
|
|
cplusplus = false;
|
|
break;
|
|
}
|
|
case 'z':
|
|
{
|
|
c++;
|
|
if (*c=='\\')
|
|
{
|
|
c=scan_escape_direct( c+1, cstr );
|
|
c--;
|
|
}
|
|
else
|
|
cstr = TQCString( c, 1 );
|
|
break;
|
|
}
|
|
case 'k': c++; if (*c=='(') c+=2; // ### FIXME \k[REG] exists too
|
|
case '!':
|
|
case '%':
|
|
case 'a':
|
|
case 'd':
|
|
case 'r':
|
|
case 'u':
|
|
case '\n':
|
|
case '&':
|
|
cstr = ""; break;
|
|
case '(':
|
|
case '[':
|
|
case 'C':
|
|
{
|
|
// Do not go forward as scan_named_character needs the leading symbol
|
|
cstr = scan_named_character( c );
|
|
cplusplus = false;
|
|
break;
|
|
}
|
|
case '*':
|
|
{
|
|
c++;
|
|
cstr = scan_named_string( c );
|
|
cplusplus = false;
|
|
break;
|
|
}
|
|
case 'f':
|
|
{
|
|
c++;
|
|
cstr = scan_named_font( c );
|
|
cplusplus = false;
|
|
break;
|
|
}
|
|
case 's': // ### FIXME: many forms are missing
|
|
c++;
|
|
j=0;i=0;
|
|
if (*c=='-') {j= -1; c++;} else if (*c=='+') {j=1; c++;}
|
|
if (*c=='0') c++; else if (*c=='\\') {
|
|
c++;
|
|
c=scan_escape_direct( c, cstr );
|
|
i=intresult; if (!j) j=1;
|
|
} else
|
|
while (isdigit(*c) && (!i || (!j && i<4))) i=i*10+(*c++)-'0';
|
|
if (!j) { j=1; if (i) i=i-10; }
|
|
if (!skip_escape) cstr=change_to_size(i*j);
|
|
c--;
|
|
break;
|
|
case 'n':
|
|
{
|
|
c++;
|
|
intresult = scan_number_register( c );
|
|
cplusplus = false;
|
|
break;
|
|
}
|
|
case 'w':
|
|
c++;
|
|
i=*c;
|
|
c++;
|
|
exoutputp=output_possible;
|
|
exskipescape=skip_escape;
|
|
output_possible=false;
|
|
skip_escape=true;
|
|
j=0;
|
|
while (*c!=i)
|
|
{
|
|
j++;
|
|
if ( *c == escapesym )
|
|
c = scan_escape_direct( c+1, cstr);
|
|
else
|
|
c++;
|
|
}
|
|
output_possible=exoutputp;
|
|
skip_escape=exskipescape;
|
|
intresult=j;
|
|
break;
|
|
case 'l': cstr = "<HR>"; curpos=0;
|
|
case 'b':
|
|
case 'v':
|
|
case 'x':
|
|
case 'o':
|
|
case 'L':
|
|
case 'h':
|
|
c++;
|
|
i=*c;
|
|
c++;
|
|
exoutputp=output_possible;
|
|
exskipescape=skip_escape;
|
|
output_possible=0;
|
|
skip_escape=true;
|
|
while (*c != i)
|
|
if (*c==escapesym) c=scan_escape_direct( c+1, cstr );
|
|
else c++;
|
|
output_possible=exoutputp;
|
|
skip_escape=exskipescape;
|
|
break;
|
|
case 'c': no_newline_output=1; break;
|
|
case '{': newline_for_fun++; break; // Start conditional block
|
|
case '}': if (newline_for_fun) newline_for_fun--; break; // End conditional block
|
|
case 'p': cstr = "<BR>\n";curpos=0; break;
|
|
case 't': cstr = "\t";curpos=(curpos+8)&0xfff8; break;
|
|
case '<': cstr = "<";curpos++; break;
|
|
case '>': cstr = ">";curpos++; break;
|
|
case '\\':
|
|
{
|
|
if (single_escape)
|
|
c--;
|
|
else
|
|
cstr="\\";
|
|
break;
|
|
}
|
|
case 'N':
|
|
{
|
|
c++;
|
|
cstr = scan_number_code( c );
|
|
cplusplus = false;
|
|
break;
|
|
}
|
|
#if 0
|
|
{
|
|
if (*++c) c++; // c += 2
|
|
if (sscanf(c, "%d", &i) != 1) // (### FIXME ugly!)
|
|
break;
|
|
TQCString temp;
|
|
temp.sprintf( "%d", i ); // Skip over number (### FIXME ugly!)
|
|
c += temp.length();
|
|
switch(i) {
|
|
case 8: cstr = "\t"; curpos=(curpos+8)&0xfff8; break;
|
|
case 34: cstr = """; curpos++; break;
|
|
default: cstr = char( i ); curpos++; break;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
case '\'': cstr = "´";curpos++; break; // groff(7) ### TODO verify
|
|
case '`': cstr = "`";curpos++; break; // groff(7)
|
|
case '-': cstr = "-";curpos++; break; // groff(7)
|
|
case '.': cstr = ".";curpos++; break; // groff(7)
|
|
default: cstr = *c; curpos++; break;
|
|
}
|
|
if (cplusplus)
|
|
c++;
|
|
return c;
|
|
}
|
|
|
|
static char *scan_escape(char *c)
|
|
{
|
|
TQCString cstr;
|
|
char* result = scan_escape_direct( c, cstr );
|
|
if ( !skip_escape )
|
|
out_html(cstr);
|
|
return result;
|
|
}
|
|
|
|
class TABLEROW;
|
|
|
|
class TABLEITEM {
|
|
public:
|
|
TABLEITEM(TABLEROW *row);
|
|
~TABLEITEM() {
|
|
delete [] contents;
|
|
}
|
|
void setContents(const char *_contents) {
|
|
delete [] contents;
|
|
contents = tqstrdup(_contents);
|
|
}
|
|
const char *getContents() const { return contents; }
|
|
|
|
void init() {
|
|
delete [] contents;
|
|
contents = 0;
|
|
size = 0;
|
|
align = 0;
|
|
valign = 0;
|
|
colspan = 1;
|
|
rowspan = 1;
|
|
font = 0;
|
|
vleft = 0;
|
|
vright = 0;
|
|
space = 0;
|
|
width = 0;
|
|
}
|
|
|
|
void copyLayout(const TABLEITEM *orig) {
|
|
size = orig->size;
|
|
align = orig->align;
|
|
valign = orig->valign;
|
|
colspan = orig->colspan;
|
|
rowspan = orig->rowspan;
|
|
font = orig->font;
|
|
vleft = orig->vleft;
|
|
vright = orig->vright;
|
|
space = orig->space;
|
|
width = orig->width;
|
|
}
|
|
|
|
public:
|
|
int size,align,valign,colspan,rowspan,font,vleft,vright,space,width;
|
|
|
|
private:
|
|
char *contents;
|
|
TABLEROW *_parent;
|
|
};
|
|
|
|
class TABLEROW {
|
|
char *test;
|
|
public:
|
|
TABLEROW() {
|
|
test = new char;
|
|
items.setAutoDelete(true);
|
|
prev = 0; next = 0;
|
|
}
|
|
~TABLEROW() {
|
|
delete test;
|
|
|
|
}
|
|
int length() const { return items.count(); }
|
|
bool has(int index) {
|
|
return (index >= 0) && (index < (int)items.count());
|
|
}
|
|
TABLEITEM &at(int index) {
|
|
return *items.at(index);
|
|
}
|
|
|
|
TABLEROW *copyLayout() const;
|
|
|
|
void addItem(TABLEITEM *item) {
|
|
items.append(item);
|
|
}
|
|
TABLEROW *prev, *next;
|
|
|
|
private:
|
|
TQPtrList<TABLEITEM> items;
|
|
};
|
|
|
|
TABLEITEM::TABLEITEM(TABLEROW *row) : contents(0), _parent(row) {
|
|
init();
|
|
_parent->addItem(this);
|
|
}
|
|
|
|
TABLEROW *TABLEROW::copyLayout() const {
|
|
TABLEROW *newrow = new TABLEROW();
|
|
|
|
TQPtrListIterator<TABLEITEM> it(items);
|
|
for ( ; it.current(); ++it) {
|
|
TABLEITEM *newitem = new TABLEITEM(newrow);
|
|
newitem->copyLayout(it.current());
|
|
}
|
|
return newrow;
|
|
}
|
|
|
|
static const char *tableopt[]= { "center", "expand", "box", "allbox",
|
|
"doublebox", "tab", "linesize",
|
|
"delim", NULL };
|
|
static int tableoptl[] = { 6,6,3,6,9,3,8,5,0};
|
|
|
|
|
|
static void clear_table(TABLEROW *table)
|
|
{
|
|
TABLEROW *tr1,*tr2;
|
|
|
|
tr1=table;
|
|
while (tr1->prev) tr1=tr1->prev;
|
|
while (tr1) {
|
|
tr2=tr1;
|
|
tr1=tr1->next;
|
|
delete tr2;
|
|
}
|
|
}
|
|
|
|
static char *scan_expression(char *c, int *result);
|
|
|
|
static char *scan_format(char *c, TABLEROW **result, int *maxcol)
|
|
{
|
|
TABLEROW *layout, *currow;
|
|
TABLEITEM *curfield;
|
|
int i,j;
|
|
if (*result) {
|
|
clear_table(*result);
|
|
}
|
|
layout= currow=new TABLEROW();
|
|
curfield=new TABLEITEM(currow);
|
|
while (*c && *c!='.') {
|
|
switch (*c) {
|
|
case 'C': case 'c': case 'N': case 'n':
|
|
case 'R': case 'r': case 'A': case 'a':
|
|
case 'L': case 'l': case 'S': case 's':
|
|
case '^': case '_':
|
|
if (curfield->align)
|
|
curfield=new TABLEITEM(currow);
|
|
curfield->align=toupper(*c);
|
|
c++;
|
|
break;
|
|
case 'i': case 'I': case 'B': case 'b':
|
|
curfield->font = toupper(*c);
|
|
c++;
|
|
break;
|
|
case 'f': case 'F':
|
|
c++;
|
|
curfield->font = toupper(*c);
|
|
c++;
|
|
if (!isspace(*c) && *c!='.') c++;
|
|
break;
|
|
case 't': case 'T': curfield->valign='t'; c++; break;
|
|
case 'p': case 'P':
|
|
c++;
|
|
i=j=0;
|
|
if (*c=='+') { j=1; c++; }
|
|
if (*c=='-') { j=-1; c++; }
|
|
while (isdigit(*c)) i=i*10+(*c++)-'0';
|
|
if (j) curfield->size= i*j; else curfield->size=j-10;
|
|
break;
|
|
case 'v': case 'V':
|
|
case 'w': case 'W':
|
|
c=scan_expression(c+2,&curfield->width);
|
|
break;
|
|
case '|':
|
|
if (curfield->align) curfield->vleft++;
|
|
else curfield->vright++;
|
|
c++;
|
|
break;
|
|
case 'e': case 'E':
|
|
c++;
|
|
break;
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
i=0;
|
|
while (isdigit(*c)) i=i*10+(*c++)-'0';
|
|
curfield->space=i;
|
|
break;
|
|
case ',': case '\n':
|
|
currow->next=new TABLEROW();
|
|
currow->next->prev=currow;
|
|
currow=currow->next;
|
|
currow->next=NULL;
|
|
curfield=new TABLEITEM(currow);
|
|
c++;
|
|
break;
|
|
default:
|
|
c++;
|
|
break;
|
|
}
|
|
}
|
|
if (*c=='.') while (*c++!='\n');
|
|
*maxcol=0;
|
|
currow=layout;
|
|
while (currow) {
|
|
i=currow->length();
|
|
if (i>*maxcol) *maxcol=i;
|
|
currow=currow->next;
|
|
}
|
|
*result=layout;
|
|
return c;
|
|
}
|
|
|
|
static TABLEROW *next_row(TABLEROW *tr)
|
|
{
|
|
if (tr->next) {
|
|
tr=tr->next;
|
|
if (!tr->next)
|
|
return next_row(tr);
|
|
return tr;
|
|
} else {
|
|
tr->next = tr->copyLayout();
|
|
tr->next->prev = tr;
|
|
return tr->next;
|
|
}
|
|
}
|
|
|
|
static char itemreset[20]="\\fR\\s0";
|
|
|
|
#define FORWARDCUR do { curfield++; } while (currow->has(curfield) && currow->at(curfield).align=='S');
|
|
|
|
static char *scan_table(char *c)
|
|
{
|
|
char *h;
|
|
char *g;
|
|
int center=0, expand=0, box=0, border=0, linesize=1;
|
|
int i,j,maxcol=0, finished=0;
|
|
TQCString oldfont;
|
|
int oldsize,oldfillout;
|
|
char itemsep='\t';
|
|
TABLEROW *layout=NULL, *currow;
|
|
int curfield = -1;
|
|
while (*c++!='\n');
|
|
h=c;
|
|
if (*h=='.') return c-1;
|
|
oldfont=current_font;
|
|
oldsize=current_size;
|
|
oldfillout=fillout;
|
|
out_html(set_font("R"));
|
|
out_html(change_to_size(0));
|
|
if (!fillout) {
|
|
fillout=1;
|
|
out_html("</PRE>");
|
|
}
|
|
while (*h && *h!='\n') h++;
|
|
if (h[-1]==';') {
|
|
/* scan table options */
|
|
while (c<h) {
|
|
while (isspace(*c)) c++;
|
|
for (i=0; tableopt[i] && tqstrncmp(tableopt[i],c,tableoptl[i]);i++);
|
|
c=c+tableoptl[i];
|
|
switch (i) {
|
|
case 0: center=1; break;
|
|
case 1: expand=1; break;
|
|
case 2: box=1; break;
|
|
case 3: border=1; break;
|
|
case 4: box=2; break;
|
|
case 5: while (*c++!='('); itemsep=*c++; break;
|
|
case 6: while (*c++!='('); linesize=0;
|
|
while (isdigit(*c)) linesize=linesize*10+(*c++)-'0';
|
|
break;
|
|
case 7: while (*c!=')') c++;
|
|
default: break;
|
|
}
|
|
c++;
|
|
}
|
|
c=h+1;
|
|
}
|
|
/* scan layout */
|
|
c=scan_format(c,&layout, &maxcol);
|
|
// currow=layout;
|
|
currow=next_row(layout);
|
|
curfield=0;
|
|
i=0;
|
|
while (!finished && *c) {
|
|
/* search item */
|
|
h=c;
|
|
if ((*c=='_' || *c=='=') && (c[1]==itemsep || c[1]=='\n')) {
|
|
if (c[-1]=='\n' && c[1]=='\n') {
|
|
if (currow->prev) {
|
|
currow->prev->next=new TABLEROW();
|
|
currow->prev->next->next=currow;
|
|
currow->prev->next->prev=currow->prev;
|
|
currow->prev=currow->prev->next;
|
|
} else {
|
|
currow->prev=layout=new TABLEROW();
|
|
currow->prev->prev=NULL;
|
|
currow->prev->next=currow;
|
|
}
|
|
TABLEITEM *newitem = new TABLEITEM(currow->prev);
|
|
newitem->align=*c;
|
|
newitem->colspan=maxcol;
|
|
curfield=0;
|
|
c=c+2;
|
|
} else {
|
|
if (currow->has(curfield)) {
|
|
currow->at(curfield).align=*c;
|
|
FORWARDCUR;
|
|
}
|
|
if (c[1]=='\n') {
|
|
currow=next_row(currow);
|
|
curfield=0;
|
|
}
|
|
c=c+2;
|
|
}
|
|
} else if (*c=='T' && c[1]=='{') {
|
|
h=c+2;
|
|
c=strstr(h,"\nT}");
|
|
c++;
|
|
*c='\0';
|
|
g=NULL;
|
|
scan_troff(h,0,&g);
|
|
scan_troff(itemreset, 0, &g);
|
|
*c='T';
|
|
c+=3;
|
|
if (currow->has(curfield)) {
|
|
currow->at(curfield).setContents(g);
|
|
FORWARDCUR;
|
|
}
|
|
delete [] g;
|
|
|
|
if (c[-1]=='\n') {
|
|
currow=next_row(currow);
|
|
curfield=0;
|
|
}
|
|
} else if (*c=='.' && c[1]=='T' && c[2]=='&' && c[-1]=='\n') {
|
|
TABLEROW *hr;
|
|
while (*c++!='\n');
|
|
hr=currow;
|
|
currow=currow->prev;
|
|
hr->prev=NULL;
|
|
c=scan_format(c,&hr, &i);
|
|
hr->prev=currow;
|
|
currow->next=hr;
|
|
currow=hr;
|
|
next_row(currow);
|
|
curfield=0;
|
|
} else if (*c=='.' && c[1]=='T' && c[2]=='E' && c[-1]=='\n') {
|
|
finished=1;
|
|
while (*c++!='\n');
|
|
if (currow->prev)
|
|
currow->prev->next=NULL;
|
|
currow->prev=NULL;
|
|
clear_table(currow);
|
|
currow = 0;
|
|
} else if (*c=='.' && c[-1]=='\n' && !isdigit(c[1])) {
|
|
/* skip troff request inside table (usually only .sp ) */
|
|
while (*c++!='\n');
|
|
} else {
|
|
h=c;
|
|
while (*c && (*c!=itemsep || c[-1]=='\\') &&
|
|
(*c!='\n' || c[-1]=='\\')) c++;
|
|
i=0;
|
|
if (*c==itemsep) {i=1; *c='\n'; }
|
|
if (h[0]=='\\' && h[2]=='\n' &&
|
|
(h[1]=='_' || h[1]=='^')) {
|
|
if (currow->has(curfield)) {
|
|
currow->at(curfield).align=h[1];
|
|
FORWARDCUR;
|
|
}
|
|
h=h+3;
|
|
} else {
|
|
g=NULL;
|
|
h=scan_troff(h,1,&g);
|
|
scan_troff(itemreset,0, &g);
|
|
if (currow->has(curfield)) {
|
|
currow->at(curfield).setContents(g);
|
|
FORWARDCUR;
|
|
}
|
|
delete [] g;
|
|
}
|
|
if (i) *c=itemsep;
|
|
c=h;
|
|
if (c[-1]=='\n') {
|
|
currow=next_row(currow);
|
|
curfield=0;
|
|
}
|
|
}
|
|
}
|
|
/* calculate colspan and rowspan */
|
|
currow=layout;
|
|
while (currow->next) currow=currow->next;
|
|
while (currow) {
|
|
int ti = 0, ti1 = 0, ti2 = -1;
|
|
TABLEROW *prev = currow->prev;
|
|
if (!prev)
|
|
break;
|
|
|
|
while (prev->has(ti1)) {
|
|
if (currow->has(ti))
|
|
switch (currow->at(ti).align) {
|
|
case 'S':
|
|
if (currow->has(ti2)) {
|
|
currow->at(ti2).colspan++;
|
|
if (currow->at(ti2).rowspan<prev->at(ti1).rowspan)
|
|
currow->at(ti2).rowspan=prev->at(ti1).rowspan;
|
|
}
|
|
break;
|
|
case '^':
|
|
if (prev->has(ti1)) prev->at(ti1).rowspan++;
|
|
default:
|
|
if (ti2 < 0) ti2=ti;
|
|
else {
|
|
do {
|
|
ti2++;
|
|
} while (currow->has(ti2) && currow->at(ti2).align=='S');
|
|
}
|
|
break;
|
|
}
|
|
ti++;
|
|
if (ti1 >= 0) ti1++;
|
|
}
|
|
currow=currow->prev;
|
|
}
|
|
/* produce html output */
|
|
if (center) out_html("<CENTER>");
|
|
if (box==2) out_html("<TABLE BORDER><TR><TD>");
|
|
out_html("<TABLE");
|
|
if (box || border) {
|
|
out_html(" BORDER");
|
|
if (!border) out_html("><TR><TD><TABLE");
|
|
if (expand) out_html(" WIDTH=\"100%\"");
|
|
}
|
|
out_html(">\n");
|
|
currow=layout;
|
|
while (currow) {
|
|
j=0;
|
|
out_html("<TR VALIGN=top>");
|
|
curfield=0;
|
|
while (currow->has(curfield)) {
|
|
if (currow->at(curfield).align!='S' && currow->at(curfield).align!='^') {
|
|
out_html("<TD");
|
|
switch (currow->at(curfield).align) {
|
|
case 'N':
|
|
currow->at(curfield).space+=4;
|
|
case 'R':
|
|
out_html(" ALIGN=right");
|
|
break;
|
|
case 'C':
|
|
out_html(" ALIGN=center");
|
|
default:
|
|
break;
|
|
}
|
|
if (!currow->at(curfield).valign && currow->at(curfield).rowspan>1)
|
|
out_html(" VALIGN=center");
|
|
if (currow->at(curfield).colspan>1) {
|
|
char buf[5];
|
|
out_html(" COLSPAN=");
|
|
sprintf(buf, "%i", currow->at(curfield).colspan);
|
|
out_html(buf);
|
|
}
|
|
if (currow->at(curfield).rowspan>1) {
|
|
char buf[5];
|
|
out_html(" ROWSPAN=");
|
|
sprintf(buf, "%i", currow->at(curfield).rowspan);
|
|
out_html(buf);
|
|
}
|
|
j=j+currow->at(curfield).colspan;
|
|
out_html(">");
|
|
if (currow->at(curfield).size) out_html(change_to_size(currow->at(curfield).size));
|
|
if (currow->at(curfield).font) out_html(set_font(currow->at(curfield).font));
|
|
switch (currow->at(curfield).align) {
|
|
case '=': out_html("<HR><HR>"); break;
|
|
case '_': out_html("<HR>"); break;
|
|
default:
|
|
out_html(currow->at(curfield).getContents());
|
|
break;
|
|
}
|
|
if (currow->at(curfield).space)
|
|
for (i=0; i<currow->at(curfield).space;i++) out_html(" ");
|
|
if (currow->at(curfield).font) out_html(set_font("R"));
|
|
if (currow->at(curfield).size) out_html(change_to_size(0));
|
|
if (j>=maxcol && currow->at(curfield).align>'@' && currow->at(curfield).align!='_')
|
|
out_html("<BR>");
|
|
out_html("</TD>");
|
|
}
|
|
curfield++;
|
|
}
|
|
out_html("</TR>\n");
|
|
currow=currow->next;
|
|
}
|
|
|
|
clear_table(layout);
|
|
|
|
if (box && !border) out_html("</TABLE>");
|
|
out_html("</TABLE>");
|
|
if (box==2) out_html("</TABLE>");
|
|
if (center) out_html("</CENTER>\n");
|
|
else out_html("\n");
|
|
if (!oldfillout) out_html("<PRE>");
|
|
fillout=oldfillout;
|
|
out_html(change_to_size(oldsize));
|
|
out_html(set_font(oldfont));
|
|
return c;
|
|
}
|
|
|
|
static char *scan_expression( char *c, int *result, const unsigned int numLoop )
|
|
{
|
|
int value=0,value2,sign=1,opex=0;
|
|
char oper='c';
|
|
|
|
if (*c=='!') {
|
|
c=scan_expression(c+1, &value);
|
|
value= (!value);
|
|
} else if (*c=='n') {
|
|
c++;
|
|
value=s_nroff;
|
|
} else if (*c=='t') {
|
|
c++;
|
|
value=1-s_nroff;
|
|
} else if (*c=='\'' || *c=='"' || *c<' ' || (*c=='\\' && c[1]=='(')) {
|
|
/* ?string1?string2?
|
|
** test if string1 equals string2.
|
|
*/
|
|
char *st1=NULL, *st2=NULL, *h;
|
|
char *tcmp=NULL;
|
|
char sep;
|
|
sep=*c;
|
|
if (sep=='\\') {
|
|
tcmp=c;
|
|
c=c+3;
|
|
}
|
|
c++;
|
|
h=c;
|
|
while (*c!= sep && (!tcmp || tqstrncmp(c,tcmp,4))) c++;
|
|
*c='\n';
|
|
scan_troff(h, 1, &st1);
|
|
*c=sep;
|
|
if (tcmp) c=c+3;
|
|
c++;
|
|
h=c;
|
|
while (*c!=sep && (!tcmp || tqstrncmp(c,tcmp,4))) c++;
|
|
*c='\n';
|
|
scan_troff(h,1,&st2);
|
|
*c=sep;
|
|
if (!st1 && !st2) value=1;
|
|
else if (!st1 || !st2) value=0;
|
|
else value=(!qstrcmp(st1, st2));
|
|
delete [] st1;
|
|
delete [] st2;
|
|
if (tcmp) c=c+3;
|
|
c++;
|
|
} else {
|
|
while (*c && ( !isspace(*c) || ( numLoop > 0 ) ) && *c!=')' && opex >= 0) {
|
|
opex=0;
|
|
switch (*c) {
|
|
case '(':
|
|
c = scan_expression( c + 1, &value2, numLoop + 1 );
|
|
value2=sign*value2;
|
|
opex=1;
|
|
break;
|
|
case '.':
|
|
case '0': case '1':
|
|
case '2': case '3':
|
|
case '4': case '5':
|
|
case '6': case '7':
|
|
case '8': case '9': {
|
|
int num=0,denum=1;
|
|
value2=0;
|
|
while (isdigit(*c)) value2=value2*10+((*c++)-'0');
|
|
if (*c=='.' && isdigit(c[1])) {
|
|
c++;
|
|
while (isdigit(*c)) {
|
|
num=num*10+((*c++)-'0');
|
|
denum=denum*10;
|
|
}
|
|
}
|
|
if (isalpha(*c)) {
|
|
/* scale indicator */
|
|
switch (*c) {
|
|
case 'i': /* inch -> 10pt */
|
|
value2=value2*10+(num*10+denum/2)/denum;
|
|
num=0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
c++;
|
|
}
|
|
value2=value2+(num+denum/2)/denum;
|
|
value2=sign*value2;
|
|
opex=1;
|
|
if (*c=='.')
|
|
opex = -1;
|
|
|
|
}
|
|
break;
|
|
case '\\':
|
|
c=scan_escape(c+1);
|
|
value2=intresult*sign;
|
|
if (isalpha(*c)) c++; /* scale indicator */
|
|
opex=1;
|
|
break;
|
|
case '-':
|
|
if (oper) { sign=-1; c++; break; }
|
|
case '>':
|
|
case '<':
|
|
case '+':
|
|
case '/':
|
|
case '*':
|
|
case '%':
|
|
case '&':
|
|
case '=':
|
|
case ':':
|
|
if (c[1]=='=') oper=(*c++) +16; else oper=*c;
|
|
c++;
|
|
break;
|
|
default: c++; break;
|
|
}
|
|
if (opex > 0) {
|
|
sign=1;
|
|
switch (oper) {
|
|
case 'c': value=value2; break;
|
|
case '-': value=value-value2; break;
|
|
case '+': value=value+value2; break;
|
|
case '*': value=value*value2; break;
|
|
case '/': if (value2) value=value/value2; break;
|
|
case '%': if (value2) value=value%value2; break;
|
|
case '<': value=(value<value2); break;
|
|
case '>': value=(value>value2); break;
|
|
case '>'+16: value=(value>=value2); break;
|
|
case '<'+16: value=(value<=value2); break;
|
|
case '=': case '='+16: value=(value==value2); break;
|
|
case '&': value = (value && value2); break;
|
|
case ':': value = (value || value2); break;
|
|
default:
|
|
{
|
|
kdDebug(7107) << "Unknown operator " << char(oper) << endl;
|
|
}
|
|
}
|
|
oper=0;
|
|
}
|
|
}
|
|
if (*c==')') c++;
|
|
}
|
|
*result=value;
|
|
return c;
|
|
}
|
|
|
|
static char *scan_expression(char *c, int *result)
|
|
{
|
|
return scan_expression( c, result, 0 );
|
|
}
|
|
|
|
static void trans_char(char *c, char s, char t)
|
|
{
|
|
char *sl=c;
|
|
int slash=0;
|
|
while (*sl!='\n' || slash) {
|
|
if (!slash) {
|
|
if (*sl==escapesym)
|
|
slash=1;
|
|
else if (*sl==s)
|
|
*sl=t;
|
|
} else slash=0;
|
|
sl++;
|
|
}
|
|
}
|
|
|
|
// 2004-10-19, patched by Waldo Bastian <bastian@kde.org>:
|
|
// Fix handling of lines like:
|
|
// .TH FIND 1L \" -*- nroff -*-
|
|
// Where \" indicates the start of comment.
|
|
//
|
|
// The problem is the \" handling in fill_words(), the return value
|
|
// indicates the end of the word as well as the end of the line, which makes it
|
|
// basically impossible to express that the end of the last word is not the end of
|
|
// the line.
|
|
//
|
|
// I have corrected that by adding an extra parameter 'next_line' that returns a
|
|
// pointer to the next line, while the function itself returns a pointer to the end
|
|
// of the last word.
|
|
static char *fill_words(char *c, char *words[], int *n, bool newline, char **next_line)
|
|
{
|
|
char *sl=c;
|
|
int slash=0;
|
|
int skipspace=0;
|
|
*n=0;
|
|
words[*n]=sl;
|
|
while (*sl && (*sl!='\n' || slash)) {
|
|
if (!slash) {
|
|
if (*sl=='"') {
|
|
if (skipspace && (*(sl+1)=='"'))
|
|
*sl++ = '\a';
|
|
else {
|
|
*sl='\a';
|
|
skipspace=!skipspace;
|
|
}
|
|
} else if (*sl==escapesym) {
|
|
slash=1;
|
|
if (sl[1]=='\n')
|
|
*sl='\a';
|
|
} else if ((*sl==' ' || *sl=='\t') && !skipspace) {
|
|
if (newline) *sl='\n';
|
|
if (words[*n]!=sl) (*n)++;
|
|
words[*n]=sl+1;
|
|
}
|
|
} else {
|
|
if (*sl=='"') {
|
|
sl--;
|
|
if (newline) *sl='\n';
|
|
if (words[*n]!=sl) (*n)++;
|
|
if (next_line)
|
|
{
|
|
char *eow = sl;
|
|
sl++;
|
|
while (*sl && *sl !='\n') sl++;
|
|
*next_line = sl;
|
|
return eow;
|
|
}
|
|
return sl;
|
|
}
|
|
slash=0;
|
|
}
|
|
sl++;
|
|
}
|
|
if (sl!=words[*n]) (*n)++;
|
|
if (next_line) *next_line = sl+1;
|
|
return sl;
|
|
}
|
|
|
|
static const char *abbrev_list[] = {
|
|
"GSBG", "Getting Started ",
|
|
"SUBG", "Customizing SunOS",
|
|
"SHBG", "Basic Troubleshooting",
|
|
"SVBG", "SunView User's Guide",
|
|
"MMBG", "Mail and Messages",
|
|
"DMBG", "Doing More with SunOS",
|
|
"UNBG", "Using the Network",
|
|
"GDBG", "Games, Demos & Other Pursuits",
|
|
"CHANGE", "SunOS 4.1 Release Manual",
|
|
"INSTALL", "Installing SunOS 4.1",
|
|
"ADMIN", "System and Network Administration",
|
|
"SECUR", "Security Features Guide",
|
|
"PROM", "PROM User's Manual",
|
|
"DIAG", "Sun System Diagnostics",
|
|
"SUNDIAG", "Sundiag User's Guide",
|
|
"MANPAGES", "SunOS Reference Manual",
|
|
"REFMAN", "SunOS Reference Manual",
|
|
"SSI", "Sun System Introduction",
|
|
"SSO", "System Services Overview",
|
|
"TEXT", "Editing Text Files",
|
|
"DOCS", "Formatting Documents",
|
|
"TROFF", "Using <B>nroff</B> and <B>troff</B>",
|
|
"INDEX", "Global Index",
|
|
"CPG", "C Programmer's Guide",
|
|
"CREF", "C Reference Manual",
|
|
"ASSY", "Assembly Language Reference",
|
|
"PUL", "Programming Utilities and Libraries",
|
|
"DEBUG", "Debugging Tools",
|
|
"NETP", "Network Programming",
|
|
"DRIVER", "Writing Device Drivers",
|
|
"STREAMS", "STREAMS Programming",
|
|
"SBDK", "SBus Developer's Kit",
|
|
"WDDS", "Writing Device Drivers for the SBus",
|
|
"FPOINT", "Floating-Point Programmer's Guide",
|
|
"SVPG", "SunView 1 Programmer's Guide",
|
|
"SVSPG", "SunView 1 System Programmer's Guide",
|
|
"PIXRCT", "Pixrect Reference Manual",
|
|
"CGI", "SunCGI Reference Manual",
|
|
"CORE", "SunCore Reference Manual",
|
|
"4ASSY", "Sun-4 Assembly Language Reference",
|
|
"SARCH", "<FONT SIZE=\"-1\">SPARC</FONT> Architecture Manual",
|
|
"KR", "The C Programming Language",
|
|
NULL, NULL };
|
|
|
|
static const char *lookup_abbrev(char *c)
|
|
{
|
|
int i=0;
|
|
|
|
if (!c) return "";
|
|
while (abbrev_list[i] && qstrcmp(c,abbrev_list[i])) i=i+2;
|
|
if (abbrev_list[i]) return abbrev_list[i+1];
|
|
else return c;
|
|
}
|
|
|
|
static const char *section_list[] = {
|
|
#ifdef Q_OS_SOLARIS
|
|
// for Solaris
|
|
"1", "User Commands",
|
|
"1B", "SunOS/BSD Compatibility Package Commands",
|
|
"1b", "SunOS/BSD Compatibility Package Commands",
|
|
"1C", "Communication Commands ",
|
|
"1c", "Communication Commands",
|
|
"1F", "FMLI Commands ",
|
|
"1f", "FMLI Commands",
|
|
"1G", "Graphics and CAD Commands ",
|
|
"1g", "Graphics and CAD Commands ",
|
|
"1M", "Maintenance Commands",
|
|
"1m", "Maintenance Commands",
|
|
"1S", "SunOS Specific Commands",
|
|
"1s", "SunOS Specific Commands",
|
|
"2", "System Calls",
|
|
"3", "C Library Functions",
|
|
"3B", "SunOS/BSD Compatibility Library Functions",
|
|
"3b", "SunOS/BSD Compatibility Library Functions",
|
|
"3C", "C Library Functions",
|
|
"3c", "C Library Functions",
|
|
"3E", "C Library Functions",
|
|
"3e", "C Library Functions",
|
|
"3F", "Fortran Library Routines",
|
|
"3f", "Fortran Library Routines",
|
|
"3G", "C Library Functions",
|
|
"3g", "C Library Functions",
|
|
"3I", "Wide Character Functions",
|
|
"3i", "Wide Character Functions",
|
|
"3K", "Kernel VM Library Functions",
|
|
"3k", "Kernel VM Library Functions",
|
|
"3L", "Lightweight Processes Library",
|
|
"3l", "Lightweight Processes Library",
|
|
"3M", "Mathematical Library",
|
|
"3m", "Mathematical Library",
|
|
"3N", "Network Functions",
|
|
"3n", "Network Functions",
|
|
"3R", "Realtime Library",
|
|
"3r", "Realtime Library",
|
|
"3S", "Standard I/O Functions",
|
|
"3s", "Standard I/O Functions",
|
|
"3T", "Threads Library",
|
|
"3t", "Threads Library",
|
|
"3W", "C Library Functions",
|
|
"3w", "C Library Functions",
|
|
"3X", "Miscellaneous Library Functions",
|
|
"3x", "Miscellaneous Library Functions",
|
|
"4", "File Formats",
|
|
"4B", "SunOS/BSD Compatibility Package File Formats",
|
|
"4b", "SunOS/BSD Compatibility Package File Formats",
|
|
"5", "Headers, Tables, and Macros",
|
|
"6", "Games and Demos",
|
|
"7", "Special Files",
|
|
"7B", "SunOS/BSD Compatibility Special Files",
|
|
"7b", "SunOS/BSD Compatibility Special Files",
|
|
"8", "Maintenance Procedures",
|
|
"8C", "Maintenance Procedures",
|
|
"8c", "Maintenance Procedures",
|
|
"8S", "Maintenance Procedures",
|
|
"8s", "Maintenance Procedures",
|
|
"9", "DDI and DKI",
|
|
"9E", "DDI and DKI Driver Entry Points",
|
|
"9e", "DDI and DKI Driver Entry Points",
|
|
"9F", "DDI and DKI Kernel Functions",
|
|
"9f", "DDI and DKI Kernel Functions",
|
|
"9S", "DDI and DKI Data Structures",
|
|
"9s", "DDI and DKI Data Structures",
|
|
"L", "Local Commands",
|
|
#elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
|
|
"1", "General Commands",
|
|
"2", "System Calls",
|
|
"3", "Library Functions",
|
|
"4", "Kernel Interfaces",
|
|
"5", "File Formats",
|
|
"6", "Games",
|
|
"7", "Miscellaneous Information",
|
|
"8", "System Manager's Manuals",
|
|
"9", "Kernel Developer's Manuals",
|
|
#else
|
|
// Other OS
|
|
"1", "User Commands ",
|
|
"1C", "User Commands",
|
|
"1G", "User Commands",
|
|
"1S", "User Commands",
|
|
"1V", "User Commands ",
|
|
"2", "System Calls",
|
|
"2V", "System Calls",
|
|
"3", "C Library Functions",
|
|
"3C", "Compatibility Functions",
|
|
"3F", "Fortran Library Routines",
|
|
"3K", "Kernel VM Library Functions",
|
|
"3L", "Lightweight Processes Library",
|
|
"3M", "Mathematical Library",
|
|
"3N", "Network Functions",
|
|
"3R", "RPC Services Library",
|
|
"3S", "Standard I/O Functions",
|
|
"3V", "C Library Functions",
|
|
"3X", "Miscellaneous Library Functions",
|
|
"4", "Devices and Network Interfaces",
|
|
"4F", "Protocol Families",
|
|
"4I", "Devices and Network Interfaces",
|
|
"4M", "Devices and Network Interfaces",
|
|
"4N", "Devices and Network Interfaces",
|
|
"4P", "Protocols",
|
|
"4S", "Devices and Network Interfaces",
|
|
"4V", "Devices and Network Interfaces",
|
|
"5", "File Formats",
|
|
"5V", "File Formats",
|
|
"6", "Games and Demos",
|
|
"7", "Environments, Tables, and Troff Macros",
|
|
"7V", "Environments, Tables, and Troff Macros",
|
|
"8", "Maintenance Commands",
|
|
"8C", "Maintenance Commands",
|
|
"8S", "Maintenance Commands",
|
|
"8V", "Maintenance Commands",
|
|
"L", "Local Commands",
|
|
#endif
|
|
// The defaults
|
|
NULL, "Misc. Reference Manual Pages",
|
|
NULL, NULL
|
|
};
|
|
|
|
static const char *section_name(char *c)
|
|
{
|
|
int i=0;
|
|
|
|
if (!c) return "";
|
|
while (section_list[i] && qstrcmp(c,section_list[i])) i=i+2;
|
|
if (section_list[i+1]) return section_list[i+1];
|
|
else return c;
|
|
}
|
|
|
|
static char *skip_till_newline(char *c)
|
|
{
|
|
int lvl=0;
|
|
|
|
while (*c && (*c!='\n' || lvl>0)) {
|
|
if (*c=='\\') {
|
|
c++;
|
|
if (*c=='}') lvl--; else if (*c=='{') lvl++;
|
|
}
|
|
c++;
|
|
}
|
|
if (*c) c++;
|
|
if (lvl<0 && newline_for_fun) {
|
|
newline_for_fun = newline_for_fun+lvl;
|
|
if (newline_for_fun<0) newline_for_fun=0;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
static bool s_whileloop = false;
|
|
|
|
/// Processing the .while request
|
|
static void request_while( char*& c, int j, bool mdoc )
|
|
{
|
|
// ### TODO: .break and .continue
|
|
kdDebug(7107) << "Entering .while" << endl;
|
|
c += j;
|
|
char* newline = skip_till_newline( c );
|
|
const char oldchar = *newline;
|
|
*newline = 0;
|
|
// We store the full .while stuff into a TQCString as if it would be a macro
|
|
const TQCString macro = c ;
|
|
kdDebug(7107) << "'Macro' of .while" << endl << macro << endl;
|
|
// Prepare for continuing after .while loop end
|
|
*newline = oldchar;
|
|
c = newline;
|
|
// Process -while loop
|
|
const bool oldwhileloop = s_whileloop;
|
|
s_whileloop = true;
|
|
int result = true; // It must be an int due to the call to scan_expression
|
|
while ( result )
|
|
{
|
|
// Unlike for a normal macro, we have the condition at start, so we do not need to prepend extra bytes
|
|
char* liveloop = tqstrdup( macro.data() );
|
|
kdDebug(7107) << "Scanning .while condition" << endl;
|
|
kdDebug(7101) << "Loop macro " << liveloop << endl;
|
|
char* end_expression = scan_expression( liveloop, &result );
|
|
kdDebug(7101) << "After " << end_expression << endl;
|
|
if ( result )
|
|
{
|
|
kdDebug(7107) << "New .while iteration" << endl;
|
|
// The condition is true, so call the .while's content
|
|
char* help = end_expression + 1;
|
|
while ( *help && ( *help == ' ' || *help == '\t' ) )
|
|
++help;
|
|
if ( ! *help )
|
|
{
|
|
// We have a problem, so stop .while
|
|
result = false;
|
|
break;
|
|
}
|
|
if ( mdoc )
|
|
scan_troff_mandoc( help, false, 0 );
|
|
else
|
|
scan_troff( help, false, 0 );
|
|
}
|
|
delete[] liveloop;
|
|
}
|
|
|
|
//
|
|
s_whileloop = oldwhileloop;
|
|
kdDebug(7107) << "Ending .while" << endl;
|
|
}
|
|
|
|
const int max_wordlist = 100;
|
|
|
|
/// Processing mixed fonts reqiests like .BI
|
|
static void request_mixed_fonts( char*& c, int j, const char* font1, const char* font2, const bool mode, const bool inFMode )
|
|
{
|
|
c += j;
|
|
if (*c=='\n') c++;
|
|
int words;
|
|
char *wordlist[max_wordlist];
|
|
fill_words(c, wordlist, &words, true, &c);
|
|
for (int i=0; i<words; i++)
|
|
{
|
|
if ((mode) || (inFMode))
|
|
{
|
|
out_html(" ");
|
|
curpos++;
|
|
}
|
|
wordlist[i][-1]=' ';
|
|
out_html( set_font( (i&1) ? font2 : font1 ) );
|
|
scan_troff(wordlist[i],1,NULL);
|
|
}
|
|
out_html(set_font("R"));
|
|
if (mode)
|
|
{
|
|
out_html(" ]");
|
|
curpos++;
|
|
}
|
|
out_html(NEWLINE);
|
|
if (!fillout)
|
|
curpos=0;
|
|
else
|
|
curpos++;
|
|
}
|
|
|
|
// Some known missing requests from man(7):
|
|
// - see "safe subset": .tr
|
|
|
|
// Some known missing requests from mdoc(7):
|
|
// - start or end of quotings
|
|
|
|
// Some of the requests are from mdoc.
|
|
// On Linux see the man pages mdoc(7), mdoc.samples(7) and groff_mdoc(7)
|
|
// See also the online man pages of FreeBSD: mdoc(7)
|
|
|
|
#define REQ_UNKNOWN -1
|
|
#define REQ_ab 0
|
|
#define REQ_di 1
|
|
#define REQ_ds 2
|
|
#define REQ_as 3
|
|
#define REQ_br 4
|
|
#define REQ_c2 5
|
|
#define REQ_cc 6
|
|
#define REQ_ce 7
|
|
#define REQ_ec 8
|
|
#define REQ_eo 9
|
|
#define REQ_ex 10
|
|
#define REQ_fc 11
|
|
#define REQ_fi 12
|
|
#define REQ_ft 13 // groff(7) "FonT"
|
|
#define REQ_el 14
|
|
#define REQ_ie 15
|
|
#define REQ_if 16
|
|
#define REQ_ig 17
|
|
#define REQ_nf 18
|
|
#define REQ_ps 19
|
|
#define REQ_sp 20
|
|
#define REQ_so 21
|
|
#define REQ_ta 22
|
|
#define REQ_ti 23
|
|
#define REQ_tm 24
|
|
#define REQ_B 25
|
|
#define REQ_I 26
|
|
#define REQ_Fd 27
|
|
#define REQ_Fn 28
|
|
#define REQ_Fo 29
|
|
#define REQ_Fc 30
|
|
#define REQ_OP 31
|
|
#define REQ_Ft 32
|
|
#define REQ_Fa 33
|
|
#define REQ_BR 34
|
|
#define REQ_BI 35
|
|
#define REQ_IB 36
|
|
#define REQ_IR 37
|
|
#define REQ_RB 38
|
|
#define REQ_RI 39
|
|
#define REQ_DT 40
|
|
#define REQ_IP 41 // man(7) "Indent Paragraph"
|
|
#define REQ_TP 42
|
|
#define REQ_IX 43
|
|
#define REQ_P 44
|
|
#define REQ_LP 45
|
|
#define REQ_PP 46
|
|
#define REQ_HP 47
|
|
#define REQ_PD 48
|
|
#define REQ_Rs 49
|
|
#define REQ_RS 50
|
|
#define REQ_Re 51
|
|
#define REQ_RE 52
|
|
#define REQ_SB 53
|
|
#define REQ_SM 54
|
|
#define REQ_Ss 55
|
|
#define REQ_SS 56
|
|
#define REQ_Sh 57
|
|
#define REQ_SH 58 // man(7) "Sub Header"
|
|
#define REQ_Sx 59
|
|
#define REQ_TS 60
|
|
#define REQ_Dt 61
|
|
#define REQ_TH 62
|
|
#define REQ_TX 63
|
|
#define REQ_rm 64
|
|
#define REQ_rn 65
|
|
#define REQ_nx 66
|
|
#define REQ_in 67
|
|
#define REQ_nr 68 // groff(7) "Number Register"
|
|
#define REQ_am 69
|
|
#define REQ_de 70
|
|
#define REQ_Bl 71 // mdoc(7) "Begin List"
|
|
#define REQ_El 72 // mdoc(7) "End List"
|
|
#define REQ_It 73 // mdoc(7) "ITem"
|
|
#define REQ_Bk 74
|
|
#define REQ_Ek 75
|
|
#define REQ_Dd 76
|
|
#define REQ_Os 77 // mdoc(7)
|
|
#define REQ_Bt 78
|
|
#define REQ_At 79 // mdoc(7) "AT&t" (not parsable, not callable)
|
|
#define REQ_Fx 80 // mdoc(7) "Freebsd" (not parsable, not callable)
|
|
#define REQ_Nx 81
|
|
#define REQ_Ox 82
|
|
#define REQ_Bx 83 // mdoc(7) "Bsd"
|
|
#define REQ_Ux 84 // mdoc(7) "UniX"
|
|
#define REQ_Dl 85
|
|
#define REQ_Bd 86
|
|
#define REQ_Ed 87
|
|
#define REQ_Be 88
|
|
#define REQ_Xr 89 // mdoc(7) "eXternal Reference"
|
|
#define REQ_Fl 90 // mdoc(7) "FLag"
|
|
#define REQ_Pa 91
|
|
#define REQ_Pf 92
|
|
#define REQ_Pp 93
|
|
#define REQ_Dq 94 // mdoc(7) "Double Quote"
|
|
#define REQ_Op 95
|
|
#define REQ_Oo 96
|
|
#define REQ_Oc 97
|
|
#define REQ_Pq 98 // mdoc(7) "Parenthese Quote"
|
|
#define REQ_Ql 99
|
|
#define REQ_Sq 100 // mdoc(7) "Single Quote"
|
|
#define REQ_Ar 101
|
|
#define REQ_Ad 102
|
|
#define REQ_Em 103 // mdoc(7) "EMphasis"
|
|
#define REQ_Va 104
|
|
#define REQ_Xc 105
|
|
#define REQ_Nd 106
|
|
#define REQ_Nm 107
|
|
#define REQ_Cd 108
|
|
#define REQ_Cm 109
|
|
#define REQ_Ic 110
|
|
#define REQ_Ms 111
|
|
#define REQ_Or 112
|
|
#define REQ_Sy 113
|
|
#define REQ_Dv 114
|
|
#define REQ_Ev 115
|
|
#define REQ_Fr 116
|
|
#define REQ_Li 117
|
|
#define REQ_No 118
|
|
#define REQ_Ns 119
|
|
#define REQ_Tn 120
|
|
#define REQ_nN 121
|
|
#define REQ_perc_A 122
|
|
#define REQ_perc_D 123
|
|
#define REQ_perc_N 124
|
|
#define REQ_perc_O 125
|
|
#define REQ_perc_P 126
|
|
#define REQ_perc_Q 127
|
|
#define REQ_perc_V 128
|
|
#define REQ_perc_B 129
|
|
#define REQ_perc_J 130
|
|
#define REQ_perc_R 131
|
|
#define REQ_perc_T 132
|
|
#define REQ_An 133 // mdoc(7) "Author Name"
|
|
#define REQ_Aq 134 // mdoc(7) "Angle bracket Quote"
|
|
#define REQ_Bq 135 // mdoc(7) "Bracket Quote"
|
|
#define REQ_Qq 136 // mdoc(7) "straight double Quote"
|
|
#define REQ_UR 137 // man(7) "URl"
|
|
#define REQ_UE 138 // man(7) "Url End"
|
|
#define REQ_UN 139 // man(7) "Url Name" (a.k.a. anchors)
|
|
#define REQ_troff 140 // groff(7) "TROFF mode"
|
|
#define REQ_nroff 141 // groff(7) "NROFF mode"
|
|
#define REQ_als 142 // groff(7) "ALias String"
|
|
#define REQ_rr 143 // groff(7) "Remove number Register"
|
|
#define REQ_rnn 144 // groff(7) "ReName Number register"
|
|
#define REQ_aln 145 // groff(7) "ALias Number register"
|
|
#define REQ_shift 146 // groff(7) "SHIFT parameter"
|
|
#define REQ_while 147 // groff(7) "WHILE loop"
|
|
#define REQ_do 148 // groff(7) "DO command"
|
|
#define REQ_Dx 149 // mdoc(7) "DragonFly" macro
|
|
|
|
static int get_request(char *req, int len)
|
|
{
|
|
static const char *requests[] = {
|
|
"ab", "di", "ds", "as", "br", "c2", "cc", "ce", "ec", "eo", "ex", "fc",
|
|
"fi", "ft", "el", "ie", "if", "ig", "nf", "ps", "sp", "so", "ta", "ti",
|
|
"tm", "B", "I", "Fd", "Fn", "Fo", "Fc", "OP", "Ft", "Fa", "BR", "BI",
|
|
"IB", "IR", "RB", "RI", "DT", "IP", "TP", "IX", "P", "LP", "PP", "HP",
|
|
"PD", "Rs", "RS", "Re", "RE", "SB", "SM", "Ss", "SS", "Sh", "SH", "Sx",
|
|
"TS", "Dt", "TH", "TX", "rm", "rn", "nx", "in", "nr", "am", "de", "Bl",
|
|
"El", "It", "Bk", "Ek", "Dd", "Os", "Bt", "At", "Fx", "Nx", "Ox", "Bx",
|
|
"Ux", "Dl", "Bd", "Ed", "Be", "Xr", "Fl", "Pa", "Pf", "Pp", "Dq", "Op",
|
|
"Oo", "Oc", "Pq", "Ql", "Sq", "Ar", "Ad", "Em", "Va", "Xc", "Nd", "Nm",
|
|
"Cd", "Cm", "Ic", "Ms", "Or", "Sy", "Dv", "Ev", "Fr", "Li", "No", "Ns",
|
|
"Tn", "nN", "%A", "%D", "%N", "%O", "%P", "%Q", "%V", "%B", "%J", "%R",
|
|
"%T", "An", "Aq", "Bq", "Qq", "UR", "UE", "UN", "troff", "nroff", "als",
|
|
"rr", "rnn", "aln", "shift", "while", "do", "Dx", 0 };
|
|
int r = 0;
|
|
while (requests[r] && tqstrncmp(req, requests[r], len)) r++;
|
|
return requests[r] ? r : REQ_UNKNOWN;
|
|
}
|
|
|
|
// &%(#@ c programs !!!
|
|
//static int ifelseval=0;
|
|
// If/else can be nested!
|
|
static TQValueStack<int> s_ifelseval;
|
|
|
|
// Process a (mdoc) request involving quotes
|
|
static char* process_quote(char* c, int j, const char* open, const char* close)
|
|
{
|
|
trans_char(c,'"','\a');
|
|
c+=j;
|
|
if (*c=='\n') c++; // ### TODO: why? Quote requests cannot be empty!
|
|
out_html(open);
|
|
c=scan_troff_mandoc(c,1,0);
|
|
out_html(close);
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
return c;
|
|
}
|
|
|
|
/**
|
|
* Is the char \p ch a puntuaction in sence of mdoc(7)
|
|
*/
|
|
static bool is_mdoc_punctuation( const char ch )
|
|
{
|
|
if ( ( ch >= '0' && ch <= '9' ) || ( ch >='A' && ch <='Z' ) || ( ch >= 'a' && ch <= 'z' ) )
|
|
return false;
|
|
else if ( ch == '.' || ch == ',' || ch == ';' || ch == ':' || ch == '(' || ch == ')'
|
|
|| ch == '[' || ch == ']' )
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Can the char \p c be part of an identifier
|
|
* \note For groff, an identifier can consist of nearly all ASCII printable non-white-space characters
|
|
* See info:/groff/Identifiers
|
|
*/
|
|
static bool is_identifier_char( const char c )
|
|
{
|
|
if ( c >= '!' && c <= '[' ) // Include digits and upper case
|
|
return true;
|
|
else if ( c >= ']' && c <= '~' ) // Include lower case
|
|
return true;
|
|
else if ( c== '\\' )
|
|
return false; // ### TODO: it should be treated as escape instead!
|
|
return false;
|
|
}
|
|
|
|
static TQCString scan_identifier( char*& c )
|
|
{
|
|
char* h = c; // help pointer
|
|
// ### TODO Groff seems to eat nearly everything as identifier name (info:/groff/Identifiers)
|
|
while ( *h && *h != '\a' && *h != '\n' && is_identifier_char( *h ) )
|
|
++h;
|
|
const char tempchar = *h;
|
|
*h = 0;
|
|
const TQCString name = c;
|
|
*h = tempchar;
|
|
if ( name.isEmpty() )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: identifier empty!" << endl;
|
|
}
|
|
c = h;
|
|
return name;
|
|
}
|
|
|
|
static char *scan_request(char *c)
|
|
{
|
|
// mdoc(7) stuff
|
|
static bool mandoc_synopsis=false; /* True if we are in the synopsis section */
|
|
static bool mandoc_command=false; /* True if this is mdoc(7) page */
|
|
static int mandoc_bd_options; /* Only copes with non-nested Bd's */
|
|
static int function_argument=0; // Number of function argument (.Fo, .Fa, .Fc)
|
|
// man(7) stuff
|
|
static bool ur_ignore=false; // Has .UR a parameter : (for .UE to know if or not to write </a>)
|
|
|
|
int i=0;
|
|
bool mode=false;
|
|
char *h=0;
|
|
char *wordlist[max_wordlist];
|
|
int words;
|
|
char *sl;
|
|
while (*c==' ' || *c=='\t') c++; // Spaces or tabs allowed between control character and request
|
|
if (c[0]=='\n') return c+1;
|
|
if (c[0]==escapesym)
|
|
{
|
|
/* some pages use .\" .\$1 .\} */
|
|
/* .\$1 is too difficult/stuppid */
|
|
if (c[1]=='$')
|
|
{
|
|
kdDebug(7107) << "Found .\\$" << endl;
|
|
c=skip_till_newline(c); // ### TODO
|
|
}
|
|
else
|
|
|
|
c = scan_escape(c+1);
|
|
}
|
|
else
|
|
{
|
|
int nlen = 0;
|
|
TQCString macroName;
|
|
while (c[nlen] && (c[nlen] != ' ') && (c[nlen] != '\t') && (c[nlen] != '\n') && (c[nlen] != escapesym))
|
|
{
|
|
macroName+=c[nlen];
|
|
nlen++;
|
|
}
|
|
int j = nlen;
|
|
while (c[j] && c[j]==' ' || c[j]=='\t') j++;
|
|
/* search macro database of self-defined macros */
|
|
TQMap<TQCString,StringDefinition>::iterator it=s_stringDefinitionMap.find(macroName);
|
|
if (it!=s_stringDefinitionMap.end())
|
|
{
|
|
kdDebug(7107) << "CALLING MACRO: " << macroName << endl;
|
|
const TQCString oldDollarZero = s_dollarZero; // Previous value of $0
|
|
s_dollarZero = macroName;
|
|
sl=fill_words(c+j, wordlist, &words, true, &c);
|
|
*sl='\0';
|
|
for (i=1;i<words; i++) wordlist[i][-1]='\0';
|
|
for (i=0; i<words; i++)
|
|
{
|
|
char *h=NULL;
|
|
if (mandoc_command)
|
|
scan_troff_mandoc(wordlist[i],1,&h);
|
|
else
|
|
scan_troff(wordlist[i],1,&h);
|
|
wordlist[i] = tqstrdup(h);
|
|
delete [] h;
|
|
}
|
|
for ( i=words; i<max_wordlist; i++ ) wordlist[i]=NULL;
|
|
if ( !(*it).m_output.isEmpty() )
|
|
{
|
|
//kdDebug(7107) << "Macro content is: " << endl << (*it).m_output << endl;
|
|
const unsigned int length = (*it).m_output.length();
|
|
char* work = new char [length+2];
|
|
work[0] = '\n'; // The macro must start after an end of line to allow a request on first line
|
|
tqstrncpy(work+1,(*it).m_output.data(),length+1);
|
|
const TQValueList<char*> oldArgumentList( s_argumentList );
|
|
s_argumentList.clear();
|
|
for ( i = 0 ; i < max_wordlist; i++ )
|
|
{
|
|
if (!wordlist[i])
|
|
break;
|
|
s_argumentList.push_back( wordlist[i] );
|
|
}
|
|
const int onff=newline_for_fun;
|
|
if (mandoc_command)
|
|
scan_troff_mandoc( work + 1, 0, NULL );
|
|
else
|
|
scan_troff( work + 1, 0, NULL);
|
|
delete[] work;
|
|
newline_for_fun=onff;
|
|
s_argumentList = oldArgumentList;
|
|
}
|
|
for (i=0; i<words; i++) delete [] wordlist[i];
|
|
*sl='\n';
|
|
s_dollarZero = oldDollarZero;
|
|
kdDebug(7107) << "ENDING MACRO: " << macroName << endl;
|
|
}
|
|
else
|
|
{
|
|
kdDebug(7107) << "REQUEST: " << macroName << endl;
|
|
switch (int request = get_request(c, nlen))
|
|
{
|
|
case REQ_ab: // groff(7) "ABort"
|
|
{
|
|
h=c+j;
|
|
while (*h && *h !='\n') h++;
|
|
*h='\0';
|
|
if (scaninbuff && buffpos)
|
|
{
|
|
buffer[buffpos]='\0';
|
|
kdDebug(7107) << "ABORT: " << buffer << endl;
|
|
}
|
|
// ### TODO find a way to display it to the user
|
|
kdDebug(7107) << "Aborting: .ab " << (c+j) << endl;
|
|
return 0;
|
|
break;
|
|
}
|
|
case REQ_An: // mdoc(7) "Author Name"
|
|
{
|
|
c+=j;
|
|
c=scan_troff_mandoc(c,1,0);
|
|
break;
|
|
}
|
|
case REQ_di: // groff(7) "end current DIversion"
|
|
{
|
|
kdDebug(7107) << "Start .di" << endl;
|
|
c+=j;
|
|
if (*c=='\n')
|
|
{
|
|
++c;
|
|
break;
|
|
}
|
|
const TQCString name ( scan_identifier( c ) );
|
|
while (*c && *c!='\n') c++;
|
|
c++;
|
|
h=c;
|
|
while (*c && tqstrncmp(c,".di",3)) while (*c && *c++!='\n');
|
|
*c='\0';
|
|
char* result=0;
|
|
scan_troff(h,0,&result);
|
|
TQMap<TQCString,StringDefinition>::iterator it=s_stringDefinitionMap.find(name);
|
|
if (it==s_stringDefinitionMap.end())
|
|
{
|
|
StringDefinition def;
|
|
def.m_length=0;
|
|
def.m_output=result;
|
|
s_stringDefinitionMap.insert(name,def);
|
|
}
|
|
else
|
|
{
|
|
(*it).m_length=0;
|
|
(*it).m_output=result;
|
|
}
|
|
delete[] result;
|
|
if (*c) *c='.';
|
|
c=skip_till_newline(c);
|
|
kdDebug(7107) << "end .di" << endl;
|
|
break;
|
|
}
|
|
case REQ_ds: // groff(7) "Define String variable"
|
|
mode=true;
|
|
case REQ_as: // groff (7) "Append String variable"
|
|
{
|
|
kdDebug(7107) << "start .ds/.as" << endl;
|
|
int oldcurpos=curpos;
|
|
c+=j;
|
|
const TQCString name( scan_identifier( c) );
|
|
if ( name.isEmpty() )
|
|
break;
|
|
while (*c && isspace(*c)) c++;
|
|
if (*c && *c=='"') c++;
|
|
single_escape=true;
|
|
curpos=0;
|
|
char* result=0;
|
|
c=scan_troff(c,1,&result);
|
|
TQMap<TQCString,StringDefinition>::iterator it=s_stringDefinitionMap.find(name);
|
|
if (it==s_stringDefinitionMap.end())
|
|
{
|
|
StringDefinition def;
|
|
def.m_length=curpos;
|
|
def.m_output=result;
|
|
s_stringDefinitionMap.insert(name,def);
|
|
}
|
|
else
|
|
{
|
|
if (mode)
|
|
{ // .ds Defining String
|
|
(*it).m_length=curpos;
|
|
(*it).m_output=result;
|
|
}
|
|
else
|
|
{ // .as Appending String
|
|
(*it).m_length+=curpos;
|
|
(*it).m_output+=result;
|
|
}
|
|
}
|
|
delete[] result;
|
|
single_escape=false;
|
|
curpos=oldcurpos;
|
|
kdDebug(7107) << "end .ds/.as" << endl;
|
|
break;
|
|
}
|
|
case REQ_br: // groff(7) "line BReak"
|
|
{
|
|
if (still_dd)
|
|
out_html("<DD>"); // ### VERIFY (does not look like generating good HTML)
|
|
else
|
|
out_html("<BR>\n");
|
|
curpos=0;
|
|
c=c+j;
|
|
if (c[0]==escapesym) c=scan_escape(c+1);
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_c2: // groff(7) "reset non-break Control character" (2 means non-break)
|
|
{
|
|
c=c+j;
|
|
if (*c!='\n')
|
|
nobreaksym=*c;
|
|
else
|
|
nobreaksym='\'';
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_cc: // groff(7) "reset Control Character"
|
|
{
|
|
c=c+j;
|
|
if (*c!='\n')
|
|
controlsym=*c;
|
|
else
|
|
controlsym='.';
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_ce: // groff (7) "CEnter"
|
|
{
|
|
c=c+j;
|
|
if (*c=='\n')
|
|
i=1;
|
|
else
|
|
{
|
|
i=0;
|
|
while ('0'<=*c && *c<='9')
|
|
{
|
|
i=i*10+*c-'0';
|
|
c++;
|
|
}
|
|
}
|
|
c=skip_till_newline(c);
|
|
/* center next i lines */
|
|
if (i>0)
|
|
{
|
|
out_html("<CENTER>\n");
|
|
while (i && *c)
|
|
{
|
|
char *line=NULL;
|
|
c=scan_troff(c,1, &line);
|
|
if (line && tqstrncmp(line, "<BR>", 4))
|
|
{
|
|
out_html(line);
|
|
out_html("<BR>\n");
|
|
delete [] line; // ### FIXME: memory leak!
|
|
i--;
|
|
}
|
|
}
|
|
out_html("</CENTER>\n");
|
|
curpos=0;
|
|
}
|
|
break;
|
|
}
|
|
case REQ_ec: // groff(7) "reset Escape Character"
|
|
{
|
|
c=c+j;
|
|
if (*c!='\n')
|
|
escapesym=*c;
|
|
else
|
|
escapesym='\\';
|
|
break;
|
|
c=skip_till_newline(c);
|
|
}
|
|
case REQ_eo: // groff(7) "turn Escape character Off"
|
|
{
|
|
escapesym='\0';
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_ex: // groff(7) "EXit"
|
|
{
|
|
return 0;
|
|
break;
|
|
}
|
|
case REQ_fc: // groff(7) "set Field and pad Character"
|
|
{
|
|
c=c+j;
|
|
if (*c=='\n')
|
|
fieldsym=padsym='\0';
|
|
else
|
|
{
|
|
fieldsym=c[0];
|
|
padsym=c[1];
|
|
}
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_fi: // groff(7) "FIll"
|
|
{
|
|
if (!fillout)
|
|
{
|
|
out_html(set_font("R"));
|
|
out_html(change_to_size('0'));
|
|
out_html("</PRE>\n");
|
|
}
|
|
curpos=0;
|
|
fillout=1;
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_ft: // groff(7) "FonT"
|
|
{
|
|
c += j;
|
|
h = skip_till_newline( c );
|
|
const char oldChar = *h;
|
|
*h = 0;
|
|
const TQCString name = c;
|
|
// ### TODO: name might contain a variable
|
|
if ( name.isEmpty() )
|
|
out_html( set_font( "P" ) ); // Previous font
|
|
else
|
|
out_html( set_font( name ) );
|
|
*h = oldChar;
|
|
c = h;
|
|
break;
|
|
}
|
|
case REQ_el: // groff(7) "ELse"
|
|
{
|
|
int ifelseval = s_ifelseval.pop();
|
|
/* .el anything : else part of if else */
|
|
if (ifelseval)
|
|
{
|
|
c=c+j;
|
|
c[-1]='\n';
|
|
c=scan_troff(c,1,NULL);
|
|
}
|
|
else
|
|
c=skip_till_newline(c+j);
|
|
break;
|
|
}
|
|
case REQ_ie: // groff(7) "If with Else"
|
|
/* .ie c anything : then part of if else */
|
|
case REQ_if: // groff(7) "IF"
|
|
{
|
|
/* .if c anything
|
|
* .if !c anything
|
|
* .if N anything
|
|
* .if !N anything
|
|
* .if 'string1'string2' anything
|
|
* .if !'string1'string2' anything
|
|
*/
|
|
c=c+j;
|
|
c=scan_expression(c, &i);
|
|
if (request == REQ_ie)
|
|
{
|
|
int ifelseval=!i;
|
|
s_ifelseval.push( ifelseval );
|
|
}
|
|
if (i)
|
|
{
|
|
*c='\n';
|
|
c++;
|
|
c=scan_troff(c,1,NULL);
|
|
}
|
|
else
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_ig: // groff(7) "IGnore"
|
|
{
|
|
const char *endwith="..\n";
|
|
i=3;
|
|
c=c+j;
|
|
if (*c!='\n' && *c != '\\')
|
|
{
|
|
/* Not newline or comment */
|
|
endwith=c-1;i=1;
|
|
c[-1]='.';
|
|
while (*c && *c!='\n') c++,i++;
|
|
}
|
|
c++;
|
|
while (*c && tqstrncmp(c,endwith,i)) while (*c++!='\n');
|
|
while (*c && *c++!='\n');
|
|
break;
|
|
}
|
|
case REQ_nf: // groff(7) "No Filling"
|
|
{
|
|
if (fillout)
|
|
{
|
|
out_html(set_font("R"));
|
|
out_html(change_to_size('0'));
|
|
out_html("<PRE>\n");
|
|
}
|
|
curpos=0;
|
|
fillout=0;
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_ps: // groff(7) "previous Point Size"
|
|
{
|
|
c=c+j;
|
|
if (*c=='\n')
|
|
out_html(change_to_size('0'));
|
|
else
|
|
{
|
|
j=0; i=0;
|
|
if (*c=='-')
|
|
{
|
|
j= -1;
|
|
c++;
|
|
}
|
|
else if (*c=='+')
|
|
j=1;c++;
|
|
c=scan_expression(c, &i);
|
|
if (!j)
|
|
{
|
|
j=1;
|
|
if (i>5) i=i-10;
|
|
}
|
|
out_html(change_to_size(i*j));
|
|
}
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_sp: // groff(7) "SKip one line"
|
|
{
|
|
c=c+j;
|
|
if (fillout)
|
|
out_html("<br><br>");
|
|
else
|
|
{
|
|
out_html(NEWLINE);
|
|
}
|
|
curpos=0;
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_so: // groff(7) "Include SOurce file"
|
|
{
|
|
char *buf;
|
|
char *name=NULL;
|
|
curpos=0;
|
|
c=c+j;
|
|
if (*c=='/')
|
|
h=c;
|
|
else
|
|
{
|
|
h=c-3;
|
|
h[0]='.';
|
|
h[1]='.';
|
|
h[2]='/';
|
|
}
|
|
while (*c!='\n') c++;
|
|
*c='\0';
|
|
scan_troff(h,1, &name);
|
|
if (name[3]=='/')
|
|
h=name+3;
|
|
else
|
|
h=name;
|
|
/* this works alright, except for section 3 */
|
|
buf=read_man_page(h);
|
|
if (!buf)
|
|
{
|
|
kdDebug(7107) << "Unable to open or read file: .so " << (h) << endl;
|
|
out_html("<BLOCKQUOTE>"
|
|
"man2html: unable to open or read file.\n");
|
|
out_html(h);
|
|
out_html("</BLOCKQUOTE>\n");
|
|
}
|
|
else
|
|
scan_troff(buf+1,0,NULL);
|
|
delete [] buf;
|
|
delete [] name;
|
|
|
|
*c++='\n';
|
|
break;
|
|
}
|
|
case REQ_ta: // gorff(7) "set TAbulators"
|
|
{
|
|
c=c+j;
|
|
j=0;
|
|
while (*c!='\n')
|
|
{
|
|
sl=scan_expression(c, &tabstops[j]);
|
|
if (j>0 && (*c=='-' || *c=='+')) tabstops[j]+=tabstops[j-1];
|
|
c=sl;
|
|
while (*c==' ' || *c=='\t') c++;
|
|
j++;
|
|
}
|
|
maxtstop=j;
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_ti: // groff(7) "Temporary Indent"
|
|
{
|
|
/*while (itemdepth || dl_set[itemdepth]) {
|
|
out_html("</DL>\n");
|
|
if (dl_set[itemdepth]) dl_set[itemdepth]=0;
|
|
else itemdepth--;
|
|
}*/
|
|
out_html("<BR>\n");
|
|
c=c+j;
|
|
c=scan_expression(c, &j);
|
|
for (i=0; i<j; i++) out_html(" ");
|
|
curpos=j;
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_tm: // groff(7) "TerMinal" ### TODO: what are useful uses for it
|
|
{
|
|
c=c+j;
|
|
h=c;
|
|
while (*c!='\n') c++;
|
|
*c='\0';
|
|
kdDebug(7107) << ".tm " << (h) << endl;
|
|
*c='\n';
|
|
break;
|
|
}
|
|
case REQ_B: // man(7) "Bold"
|
|
mode=1;
|
|
case REQ_I: // man(7) "Italic"
|
|
{
|
|
/* parse one line in a certain font */
|
|
out_html( set_font( mode?"B":"I" ) );
|
|
fill_words(c, wordlist, &words, false, 0);
|
|
c=c+j;
|
|
if (*c=='\n') c++;
|
|
c=scan_troff(c, 1, NULL);
|
|
out_html(set_font("R"));
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Fd: // mdoc(7) "Function Definition"
|
|
{
|
|
// Normal text must be printed in bold, punctuation in regular font
|
|
c+=j;
|
|
if (*c=='\n') c++; // ### TODO: verify
|
|
sl=fill_words(c, wordlist, &words, true, &c);
|
|
for (i=0; i<words; i++)
|
|
{
|
|
wordlist[i][-1]=' ';
|
|
// ### FIXME In theory, only a single punctuation character is recognized as punctuation
|
|
if ( is_mdoc_punctuation ( *wordlist[i] ) )
|
|
out_html( set_font ( "R" ) );
|
|
else
|
|
out_html( set_font ( "B" ) );
|
|
scan_troff(wordlist[i],1,NULL);
|
|
out_html(" ");
|
|
}
|
|
// In the mdoc synopsis, there are automatical line breaks (### TODO: before or after?)
|
|
if (mandoc_synopsis)
|
|
{
|
|
out_html("<br>");
|
|
};
|
|
out_html(set_font("R"));
|
|
out_html(NEWLINE);
|
|
if (!fillout)
|
|
curpos=0;
|
|
else
|
|
curpos++;
|
|
break;
|
|
}
|
|
case REQ_Fn: // mdoc(7) for "Function calls"
|
|
{
|
|
// brackets and commas have to be inserted automatically
|
|
c+=j;
|
|
if (*c=='\n') c++;
|
|
sl=fill_words(c, wordlist, &words, true, &c);
|
|
if ( words )
|
|
{
|
|
for (i=0; i<words; i++)
|
|
{
|
|
wordlist[i][-1]=' ';
|
|
if ( i )
|
|
out_html( set_font( "I" ) );
|
|
else
|
|
out_html( set_font( "B" ) );
|
|
scan_troff(wordlist[i],1,NULL);
|
|
out_html( set_font( "R" ) );
|
|
if (i==0)
|
|
{
|
|
out_html(" (");
|
|
}
|
|
else if (i<words-1)
|
|
out_html(", ");
|
|
}
|
|
out_html(")");
|
|
}
|
|
out_html(set_font("R"));
|
|
if (mandoc_synopsis)
|
|
out_html("<br>");
|
|
out_html(NEWLINE);
|
|
if (!fillout)
|
|
curpos=0;
|
|
else
|
|
curpos++;
|
|
break;
|
|
}
|
|
case REQ_Fo: // mdoc(7) "Function definition Opening"
|
|
{
|
|
char* font[2] = { "B", "R" };
|
|
c+=j;
|
|
if (*c=='\n') c++;
|
|
char *eol=strchr(c,'\n');
|
|
char *semicolon=strchr(c,';');
|
|
if ((semicolon!=0) && (semicolon<eol)) *semicolon=' ';
|
|
|
|
sl=fill_words(c, wordlist, &words, true, &c);
|
|
// Normally a .Fo has only one parameter
|
|
for (i=0; i<words; i++)
|
|
{
|
|
wordlist[i][-1]=' ';
|
|
out_html(set_font(font[i&1]));
|
|
scan_troff(wordlist[i],1,NULL);
|
|
if (i==0)
|
|
{
|
|
out_html(" (");
|
|
}
|
|
// ### TODO What should happen if there is more than one argument
|
|
// else if (i<words-1) out_html(", ");
|
|
}
|
|
function_argument=1; // Must be > 0
|
|
out_html(set_font("R"));
|
|
out_html(NEWLINE);
|
|
if (!fillout)
|
|
curpos=0;
|
|
else
|
|
curpos++;
|
|
break;
|
|
}
|
|
case REQ_Fc:// mdoc(7) "Function definition Close"
|
|
{
|
|
// .Fc has no parameter
|
|
c+=j;
|
|
c=skip_till_newline(c);
|
|
char* font[2] = { "B", "R" };
|
|
out_html(set_font(font[i&1]));
|
|
out_html(")");
|
|
out_html(set_font("R"));
|
|
if (mandoc_synopsis)
|
|
out_html("<br>");
|
|
out_html(NEWLINE);
|
|
if (!fillout)
|
|
curpos=0;
|
|
else
|
|
curpos++;
|
|
function_argument=0; // Reset the count variable
|
|
break;
|
|
}
|
|
case REQ_Fa: // mdoc(7) "Function definition argument"
|
|
{
|
|
char* font[2] = { "B", "R" };
|
|
c+=j;
|
|
if (*c=='\n') c++;
|
|
sl=fill_words(c, wordlist, &words, true, &c);
|
|
out_html(set_font(font[i&1]));
|
|
// function_argument==0 means that we had no .Fo before, e.g. in mdoc.samples(7)
|
|
if (function_argument > 1)
|
|
{
|
|
out_html(", ");
|
|
curpos+=2;
|
|
function_argument++;
|
|
}
|
|
else if (function_argument==1)
|
|
{
|
|
// We are only at the first parameter
|
|
function_argument++;
|
|
}
|
|
for (i=0; i<words; i++)
|
|
{
|
|
wordlist[i][-1]=' ';
|
|
scan_troff(wordlist[i],1,NULL);
|
|
}
|
|
out_html(set_font("R"));
|
|
if (!fillout)
|
|
curpos=0;
|
|
else
|
|
curpos++;
|
|
break;
|
|
}
|
|
|
|
case REQ_OP: /* groff manpages use this construction */
|
|
{
|
|
/* .OP a b : [ <B>a</B> <I>b</I> ] */
|
|
mode=true;
|
|
out_html(set_font("R"));
|
|
out_html("[");
|
|
curpos++;
|
|
request_mixed_fonts( c, j, "B", "I", true, false );
|
|
break;
|
|
// Do not break!
|
|
}
|
|
case REQ_Ft: //perhaps "Function return type"
|
|
{
|
|
request_mixed_fonts( c, j, "B", "I", false, true );
|
|
break;
|
|
}
|
|
case REQ_BR:
|
|
{
|
|
request_mixed_fonts( c, j, "B", "R", false, false );
|
|
break;
|
|
}
|
|
case REQ_BI:
|
|
{
|
|
request_mixed_fonts( c, j, "B", "I", false, false );
|
|
break;
|
|
}
|
|
case REQ_IB:
|
|
{
|
|
request_mixed_fonts( c, j, "I", "B", false, false );
|
|
break;
|
|
}
|
|
case REQ_IR:
|
|
{
|
|
request_mixed_fonts( c, j, "I", "R", false, false );
|
|
break;
|
|
}
|
|
case REQ_RB:
|
|
{
|
|
request_mixed_fonts( c, j, "R", "B", false, false );
|
|
break;
|
|
}
|
|
case REQ_RI:
|
|
{
|
|
request_mixed_fonts( c, j, "R", "I", false, false );
|
|
break;
|
|
}
|
|
case REQ_DT: // man(7) "Default Tabulators"
|
|
{
|
|
for (j=0;j<20; j++) tabstops[j]=(j+1)*8;
|
|
maxtstop=20;
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_IP: // man(7) "Ident Paragraph"
|
|
{
|
|
sl=fill_words(c+j, wordlist, &words, true, &c);
|
|
if (!dl_set[itemdepth])
|
|
{
|
|
out_html("<DL>\n");
|
|
dl_set[itemdepth]=1;
|
|
}
|
|
out_html("<DT>");
|
|
if (words)
|
|
scan_troff(wordlist[0], 1,NULL);
|
|
out_html("<DD>");
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_TP: // man(7) "hanging Tag Paragraph"
|
|
{
|
|
if (!dl_set[itemdepth])
|
|
{
|
|
out_html("<br><br><DL>\n");
|
|
dl_set[itemdepth]=1;
|
|
}
|
|
out_html("<DT>");
|
|
c=skip_till_newline(c);
|
|
/* somewhere a definition ends with '.TP' */
|
|
if (!*c)
|
|
still_dd=true;
|
|
else
|
|
{
|
|
// HACK for proc(5)
|
|
while (c[0]=='.' && c[1]=='\\' && c[2]=='\"')
|
|
{
|
|
// We have a comment, so skip the line
|
|
c=skip_till_newline(c);
|
|
}
|
|
c=scan_troff(c,1,NULL);
|
|
out_html("<DD>");
|
|
}
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_IX: // "INdex" ### TODO: where is it defined?
|
|
{
|
|
/* general index */
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_P: // man(7) "Paragraph"
|
|
case REQ_LP:// man(7) "Paragraph"
|
|
case REQ_PP:// man(7) "Paragraph; reset Prevailing indent"
|
|
{
|
|
if (dl_set[itemdepth])
|
|
{
|
|
out_html("</DL>\n");
|
|
dl_set[itemdepth]=0;
|
|
}
|
|
if (fillout)
|
|
out_html("<br><br>\n");
|
|
else
|
|
{
|
|
out_html(NEWLINE);
|
|
}
|
|
curpos=0;
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_HP: // man(7) "Hanging indent Paragraph"
|
|
{
|
|
if (!dl_set[itemdepth])
|
|
{
|
|
out_html("<DL>");
|
|
dl_set[itemdepth]=1;
|
|
}
|
|
out_html("<DT>\n");
|
|
still_dd=true;
|
|
c=skip_till_newline(c);
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_PD: // man(7) "Paragraph Distance"
|
|
{
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_Rs: // mdoc(7) "Relative margin Start"
|
|
case REQ_RS: // man(7) "Relative margin Start"
|
|
{
|
|
sl=fill_words(c+j, wordlist, &words, true, 0);
|
|
j=1;
|
|
if (words>0) scan_expression(wordlist[0], &j);
|
|
if (j>=0)
|
|
{
|
|
itemdepth++;
|
|
dl_set[itemdepth]=0;
|
|
out_html("<DL><DT><DD>");
|
|
c=skip_till_newline(c);
|
|
curpos=0;
|
|
break;
|
|
}
|
|
}
|
|
case REQ_Re: // mdoc(7) "Relative margin End"
|
|
case REQ_RE: // man(7) "Relative margin End"
|
|
{
|
|
if (itemdepth > 0)
|
|
{
|
|
if (dl_set[itemdepth]) out_html("</DL>");
|
|
out_html("</DL>\n");
|
|
itemdepth--;
|
|
}
|
|
c=skip_till_newline(c);
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_SB: // man(7) "Small; Bold"
|
|
{
|
|
out_html(set_font("B"));
|
|
out_html("<small>");
|
|
trans_char(c,'"','\a'); // ### VERIFY
|
|
c=scan_troff(c+j, 1, NULL);
|
|
out_html("</small>");
|
|
out_html(set_font("R"));
|
|
break;
|
|
}
|
|
case REQ_SM: // man(7) "SMall"
|
|
{
|
|
c=c+j;
|
|
if (*c=='\n') c++;
|
|
out_html("<small>");
|
|
trans_char(c,'"','\a'); // ### VERIFY
|
|
c=scan_troff(c,1,NULL);
|
|
out_html("</small>");
|
|
break;
|
|
}
|
|
case REQ_Ss: // mdoc(7) "Sub Section"
|
|
mandoc_command = 1;
|
|
case REQ_SS: // mdoc(7) "Sub Section"
|
|
mode=true;
|
|
case REQ_Sh: // mdoc(7) "Sub Header"
|
|
/* hack for fallthru from above */
|
|
mandoc_command = !mode || mandoc_command;
|
|
case REQ_SH: // man(7) "Sub Header"
|
|
{
|
|
c=c+j;
|
|
if (*c=='\n') c++;
|
|
while (itemdepth || dl_set[itemdepth])
|
|
{
|
|
out_html("</DL>\n");
|
|
if (dl_set[itemdepth])
|
|
dl_set[itemdepth]=0;
|
|
else if (itemdepth > 0)
|
|
itemdepth--;
|
|
}
|
|
out_html(set_font("R"));
|
|
out_html(change_to_size(0));
|
|
if (!fillout)
|
|
{
|
|
fillout=1;
|
|
out_html("</PRE>");
|
|
}
|
|
trans_char(c,'"', '\a');
|
|
if (section)
|
|
{
|
|
out_html("</div>\n");
|
|
section=0;
|
|
}
|
|
if (mode)
|
|
out_html("\n<H3>");
|
|
else
|
|
out_html("\n<H2>");
|
|
mandoc_synopsis = tqstrncmp(c, "SYNOPSIS", 8) == 0;
|
|
c = mandoc_command ? scan_troff_mandoc(c,1,NULL) : scan_troff(c,1,NULL);
|
|
if (mode)
|
|
out_html("</H3>\n");
|
|
else
|
|
out_html("</H2>\n");
|
|
out_html("<div>\n");
|
|
|
|
section=1;
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Sx: // mdoc(7)
|
|
{
|
|
// reference to a section header
|
|
out_html(set_font("B"));
|
|
trans_char(c,'"','\a');
|
|
c=c+j;
|
|
if (*c=='\n') c++;
|
|
c=scan_troff(c, 1, NULL);
|
|
out_html(set_font("R"));
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_TS: // ### TODO where is it defined? (tbl?)
|
|
{
|
|
c=scan_table(c);
|
|
break;
|
|
}
|
|
case REQ_Dt: /* mdoc(7) */
|
|
mandoc_command = true;
|
|
case REQ_TH: // man(7) "Title Header"
|
|
{
|
|
if (!output_possible)
|
|
{
|
|
sl = fill_words(c+j, wordlist, &words, true, &c);
|
|
// ### TODO: the page should be displayed even if it is "anonymous" (words==0)
|
|
if (words>=1)
|
|
{
|
|
for (i=1; i<words; i++) wordlist[i][-1]='\0';
|
|
*sl='\0';
|
|
for (i=0; i<words; i++)
|
|
{
|
|
if (wordlist[i][0] == '\007')
|
|
wordlist[i]++;
|
|
if (wordlist[i][tqstrlen(wordlist[i])-1] == '\007')
|
|
wordlist[i][tqstrlen(wordlist[i])-1] = 0;
|
|
}
|
|
output_possible=true;
|
|
out_html( DOCTYPE"<HTML>\n<HEAD>\n");
|
|
#ifdef SIMPLE_MAN2HTML
|
|
// Most English man pages are in ISO-8859-1
|
|
out_html("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">\n");
|
|
#else
|
|
// kio_man transforms from local to UTF-8
|
|
out_html("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=");
|
|
out_html(TQTextCodec::codecForLocale()->mimeName());
|
|
out_html("\">\n");
|
|
#endif
|
|
out_html("<TITLE>");
|
|
out_html(scan_troff(wordlist[0], 0, NULL));
|
|
out_html( " Manpage</TITLE>\n");
|
|
out_html( "<link rel=\"stylesheet\" href=\"");
|
|
out_html(htmlPath);
|
|
out_html("/kde-default.css\" type=\"text/css\">\n" );
|
|
out_html( "<meta name=\"ROFF Type\" content=\"");
|
|
if (mandoc_command)
|
|
out_html("mdoc");
|
|
else
|
|
out_html("man");
|
|
out_html("\">\n");
|
|
out_html( "</HEAD>\n\n" );
|
|
out_html("<BODY BGCOLOR=\"#FFFFFF\">\n\n" );
|
|
out_html("<div style=\"background-image: url(");
|
|
out_html(cssPath);
|
|
out_html("/top-middle.png); width: 100%; height: 131pt;\">\n" );
|
|
out_html("<div style=\"position: absolute; right: 0pt;\">\n");
|
|
out_html("<img src=\"");
|
|
out_html(htmlPath);
|
|
out_html("/top-right-konqueror.png\" style=\"margin: 0pt\" alt=\"Top right\">\n");
|
|
out_html("</div>\n");
|
|
|
|
out_html("<div style=\"position: absolute; left: 0pt;\">\n");
|
|
out_html("<img src=\"");
|
|
out_html(htmlPath);
|
|
out_html("/top-left.png\" style=\"margin: 0pt\" alt=\"Top left\">\n");
|
|
out_html("</div>\n");
|
|
out_html("<div style=\"position: absolute; top: 25pt; right: 100pt; text-align: right; font-size: xx-large; font-weight: bold; text-shadow: #fff 0pt 0pt 5pt; color: #444\">\n");
|
|
out_html( scan_troff(wordlist[0], 0, NULL ) );
|
|
out_html("</div>\n");
|
|
out_html("</div>\n");
|
|
out_html("<div style=\"margin-left: 5em; margin-right: 5em;\">\n");
|
|
out_html("<h1>" );
|
|
out_html( scan_troff(wordlist[0], 0, NULL ) );
|
|
out_html( "</h1>\n" );
|
|
if (words>1)
|
|
{
|
|
out_html("Section: " );
|
|
if (!mandoc_command && words>4)
|
|
out_html(scan_troff(wordlist[4], 0, NULL) );
|
|
else
|
|
out_html(section_name(wordlist[1]));
|
|
out_html(" (");
|
|
out_html(scan_troff(wordlist[1], 0, NULL));
|
|
out_html(")\n");
|
|
}
|
|
else
|
|
{
|
|
out_html("Section not specified");
|
|
}
|
|
*sl='\n';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
kdWarning(7107) << ".TH found but output not possible" << endl;
|
|
c=skip_till_newline(c);
|
|
}
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_TX: // mdoc(7)
|
|
{
|
|
sl=fill_words(c+j, wordlist, &words, true, &c);
|
|
*sl='\0';
|
|
out_html(set_font("I"));
|
|
if (words>1) wordlist[1][-1]='\0';
|
|
const char *c2=lookup_abbrev(wordlist[0]);
|
|
curpos+=tqstrlen(c2);
|
|
out_html(c2);
|
|
out_html(set_font("R"));
|
|
if (words>1)
|
|
out_html(wordlist[1]);
|
|
*sl='\n';
|
|
break;
|
|
}
|
|
case REQ_rm: // groff(7) "ReMove"
|
|
/* .rm xx : Remove request, macro or string */
|
|
mode=true;
|
|
case REQ_rn: // groff(7) "ReName"
|
|
/* .rn xx yy : Rename request, macro or string xx to yy */
|
|
{
|
|
kdDebug(7107) << "start .rm/.rn" << endl;
|
|
c+=j;
|
|
const TQCString name( scan_identifier( c ) );
|
|
if ( name.isEmpty() )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: empty origin string to remove/rename: " << endl;
|
|
break;
|
|
}
|
|
TQCString name2;
|
|
if ( !mode )
|
|
{
|
|
while (*c && isspace(*c) && *c!='\n') ++c;
|
|
name2 = scan_identifier( c );
|
|
if ( name2.isEmpty() )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: empty destination string to rename: " << endl;
|
|
break;
|
|
}
|
|
}
|
|
c=skip_till_newline(c);
|
|
TQMap<TQCString,StringDefinition>::iterator it=s_stringDefinitionMap.find(name);
|
|
if (it==s_stringDefinitionMap.end())
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: cannot find string to rename or remove: " << name << endl;
|
|
}
|
|
else
|
|
{
|
|
if (mode)
|
|
{
|
|
// .rm ReMove
|
|
s_stringDefinitionMap.remove(name); // ### QT4: removeAll
|
|
}
|
|
else
|
|
{
|
|
// .rn ReName
|
|
StringDefinition def=(*it);
|
|
s_stringDefinitionMap.remove(name); // ### QT4: removeAll
|
|
s_stringDefinitionMap.insert(name2,def);
|
|
}
|
|
}
|
|
kdDebug(7107) << "end .rm/.rn" << endl;
|
|
break;
|
|
}
|
|
case REQ_nx: // ### TODO in man(7) it is "No filling", not "next file"
|
|
/* .nx filename : next file. */
|
|
case REQ_in: // groff(7) "INdent"
|
|
{
|
|
/* .in +-N : Indent */
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_nr: // groff(7) "Number Register"
|
|
{
|
|
kdDebug(7107) << "start .nr" << endl;
|
|
c += j;
|
|
const TQCString name( scan_identifier( c ) );
|
|
if ( name.isEmpty() )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: empty name for register variable" << endl;
|
|
break;
|
|
}
|
|
while ( *c && ( *c==' ' || *c=='\t' ) ) c++;
|
|
int sign = 0;
|
|
if ( *c && ( *c == '+' || *c == '-' ) )
|
|
{
|
|
if ( *c == '+' )
|
|
sign = 1;
|
|
else if ( *c == '-' )
|
|
sign = -1;
|
|
}
|
|
int value = 0;
|
|
int increment = 0;
|
|
c=scan_expression( c, &value );
|
|
if ( *c && *c!='\n')
|
|
{
|
|
while ( *c && ( *c==' ' || *c=='\t' ) ) c++;
|
|
c=scan_expression( c, &increment );
|
|
}
|
|
c = skip_till_newline( c );
|
|
TQMap <TQCString, NumberDefinition>::iterator it = s_numberDefinitionMap.find( name );
|
|
if ( it == s_numberDefinitionMap.end() )
|
|
{
|
|
if ( sign < 1 )
|
|
value = -value;
|
|
NumberDefinition def( value, increment );
|
|
s_numberDefinitionMap.insert( name, def );
|
|
}
|
|
else
|
|
{
|
|
if ( sign > 0 )
|
|
(*it).m_value += value;
|
|
else if ( sign < 0 )
|
|
(*it).m_value += - value;
|
|
else
|
|
(*it).m_value = value;
|
|
(*it).m_increment = increment;
|
|
}
|
|
kdDebug(7107) << "end .nr" << endl;
|
|
break;
|
|
}
|
|
case REQ_am: // groff(7) "Append Macro"
|
|
/* .am xx yy : append to a macro. */
|
|
/* define or handle as .ig yy */
|
|
mode=true;
|
|
case REQ_de: // groff(7) "DEfine macro"
|
|
/* .de xx yy : define or redefine macro xx; end at .yy (..) */
|
|
/* define or handle as .ig yy */
|
|
{
|
|
kdDebug(7107) << "Start .am/.de" << endl;
|
|
c+=j;
|
|
char *next_line;
|
|
sl = fill_words(c, wordlist, &words, true, &next_line);
|
|
char *nameStart = wordlist[0];
|
|
c = nameStart;
|
|
while (*c && (*c != ' ') && (*c != '\n')) c++;
|
|
*c = '\0';
|
|
const TQCString name(nameStart);
|
|
|
|
TQCString endmacro;
|
|
if (words == 1)
|
|
{
|
|
endmacro="..";
|
|
}
|
|
else
|
|
{
|
|
endmacro='.';
|
|
c = wordlist[1];
|
|
while (*c && (*c != ' ') && (*c != '\n'))
|
|
endmacro+=*c++;
|
|
}
|
|
c = next_line;
|
|
sl=c;
|
|
const int length=tqstrlen(endmacro);
|
|
while (*c && tqstrncmp(c,endmacro,length))
|
|
c=skip_till_newline(c);
|
|
|
|
TQCString macro;
|
|
while (sl!=c)
|
|
{
|
|
if (sl[0]=='\\' && sl[1]=='\\')
|
|
{
|
|
macro+='\\';
|
|
sl++;
|
|
}
|
|
else
|
|
macro+=*sl;
|
|
sl++;
|
|
}
|
|
|
|
TQMap<TQCString,StringDefinition>::iterator it=s_stringDefinitionMap.find(name);
|
|
if (it==s_stringDefinitionMap.end())
|
|
{
|
|
StringDefinition def;
|
|
def.m_length=0;
|
|
def.m_output=macro;
|
|
s_stringDefinitionMap.insert(name,def);
|
|
}
|
|
else if (mode)
|
|
{
|
|
// .am Append Macro
|
|
(*it).m_length=0; // It could be formerly a string
|
|
// if ((*it).m_output.right(1)!='\n')
|
|
if (*((*it).m_output.right(1).data())!='\n')
|
|
(*it).m_output+='\n';
|
|
(*it).m_output+=macro;
|
|
}
|
|
else
|
|
{
|
|
// .de DEfine macro
|
|
(*it).m_length=0; // It could be formerly a string
|
|
(*it).m_output=macro;
|
|
}
|
|
c=skip_till_newline(c);
|
|
kdDebug(7107) << "End .am/.de" << endl;
|
|
break;
|
|
}
|
|
case REQ_Bl: // mdoc(7) "Begin List"
|
|
{
|
|
char list_options[NULL_TERMINATED(MED_STR_MAX)];
|
|
char *nl = strchr(c,'\n');
|
|
c=c+j;
|
|
if (dl_set[itemdepth])
|
|
/* These things can nest. */
|
|
itemdepth++;
|
|
if (nl)
|
|
{
|
|
/* Parse list options */
|
|
strlimitcpy(list_options, c, nl - c, MED_STR_MAX);
|
|
}
|
|
if (strstr(list_options, "-bullet"))
|
|
{
|
|
/* HTML Unnumbered List */
|
|
dl_set[itemdepth] = BL_BULLET_LIST;
|
|
out_html("<UL>\n");
|
|
}
|
|
else if (strstr(list_options, "-enum"))
|
|
{
|
|
/* HTML Ordered List */
|
|
dl_set[itemdepth] = BL_ENUM_LIST;
|
|
out_html("<OL>\n");
|
|
}
|
|
else
|
|
{
|
|
/* HTML Descriptive List */
|
|
dl_set[itemdepth] = BL_DESC_LIST;
|
|
out_html("<DL>\n");
|
|
}
|
|
if (fillout)
|
|
out_html("<br><br>\n");
|
|
else
|
|
{
|
|
out_html(NEWLINE);
|
|
}
|
|
curpos=0;
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_El: // mdoc(7) "End List"
|
|
{
|
|
c=c+j;
|
|
if (dl_set[itemdepth] & BL_DESC_LIST)
|
|
out_html("</DL>\n");
|
|
else if (dl_set[itemdepth] & BL_BULLET_LIST)
|
|
out_html("</UL>\n");
|
|
else if (dl_set[itemdepth] & BL_ENUM_LIST)
|
|
out_html("</OL>\n");
|
|
dl_set[itemdepth]=0;
|
|
if (itemdepth > 0) itemdepth--;
|
|
if (fillout)
|
|
out_html("<br><br>\n");
|
|
else
|
|
{
|
|
out_html(NEWLINE);
|
|
}
|
|
curpos=0;
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_It: // mdoc(7) "list ITem"
|
|
{
|
|
c=c+j;
|
|
if (tqstrncmp(c, "Xo", 2) == 0 && isspace(*(c+2)))
|
|
c = skip_till_newline(c);
|
|
if (dl_set[itemdepth] & BL_DESC_LIST)
|
|
{
|
|
out_html("<DT>");
|
|
out_html(set_font("B"));
|
|
if (*c=='\n')
|
|
{
|
|
/* Don't allow embedded comms after a newline */
|
|
c++;
|
|
c=scan_troff(c,1,NULL);
|
|
}
|
|
else
|
|
{
|
|
/* Do allow embedded comms on the same line. */
|
|
c=scan_troff_mandoc(c,1,NULL);
|
|
}
|
|
out_html(set_font("R"));
|
|
out_html(NEWLINE);
|
|
out_html("<DD>");
|
|
}
|
|
else if (dl_set[itemdepth] & (BL_BULLET_LIST | BL_ENUM_LIST))
|
|
{
|
|
out_html("<LI>");
|
|
c=scan_troff_mandoc(c,1,NULL);
|
|
out_html(NEWLINE);
|
|
}
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Bk: /* mdoc(7) */
|
|
case REQ_Ek: /* mdoc(7) */
|
|
case REQ_Dd: /* mdoc(7) */
|
|
case REQ_Os: // mdoc(7) "Operating System"
|
|
{
|
|
trans_char(c,'"','\a');
|
|
c=c+j;
|
|
if (*c=='\n') c++;
|
|
c=scan_troff_mandoc(c, 1, NULL);
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Bt: // mdoc(7) "Beta Test"
|
|
{
|
|
trans_char(c,'"','\a');
|
|
c=c+j;
|
|
out_html(" is currently in beta test.");
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_At: /* mdoc(7) */
|
|
case REQ_Fx: /* mdoc(7) */
|
|
case REQ_Nx: /* mdoc(7) */
|
|
case REQ_Ox: /* mdoc(7) */
|
|
case REQ_Bx: /* mdoc(7) */
|
|
case REQ_Ux: /* mdoc(7) */
|
|
case REQ_Dx: /* mdoc(7) */
|
|
{
|
|
bool parsable=true;
|
|
trans_char(c,'"','\a');
|
|
c=c+j;
|
|
if (*c=='\n') c++;
|
|
if (request==REQ_At)
|
|
{
|
|
out_html("AT&T UNIX ");
|
|
parsable=false;
|
|
}
|
|
else if (request==REQ_Fx)
|
|
{
|
|
out_html("FreeBSD ");
|
|
parsable=false;
|
|
}
|
|
else if (request==REQ_Nx)
|
|
out_html("NetBSD ");
|
|
else if (request==REQ_Ox)
|
|
out_html("OpenBSD ");
|
|
else if (request==REQ_Bx)
|
|
out_html("BSD ");
|
|
else if (request==REQ_Ux)
|
|
out_html("UNIX ");
|
|
else if (request==REQ_Dx)
|
|
out_html("DragonFly ");
|
|
if (parsable)
|
|
c=scan_troff_mandoc(c,1,0);
|
|
else
|
|
c=scan_troff(c,1,0);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Dl: /* mdoc(7) */
|
|
{
|
|
c=c+j;
|
|
out_html(NEWLINE);
|
|
out_html("<BLOCKQUOTE>");
|
|
if (*c=='\n') c++;
|
|
c=scan_troff_mandoc(c, 1, NULL);
|
|
out_html("</BLOCKQUOTE>");
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Bd: /* mdoc(7) */
|
|
{ /* Seems like a kind of example/literal mode */
|
|
char bd_options[NULL_TERMINATED(MED_STR_MAX)];
|
|
char *nl = strchr(c,'\n');
|
|
c=c+j;
|
|
if (nl)
|
|
strlimitcpy(bd_options, c, nl - c, MED_STR_MAX);
|
|
out_html(NEWLINE);
|
|
mandoc_bd_options = 0; /* Remember options for terminating Bl */
|
|
if (strstr(bd_options, "-offset indent"))
|
|
{
|
|
mandoc_bd_options |= BD_INDENT;
|
|
out_html("<BLOCKQUOTE>\n");
|
|
}
|
|
if ( strstr(bd_options, "-literal") || strstr(bd_options, "-unfilled"))
|
|
{
|
|
if (fillout)
|
|
{
|
|
mandoc_bd_options |= BD_LITERAL;
|
|
out_html(set_font("R"));
|
|
out_html(change_to_size('0'));
|
|
out_html("<PRE>\n");
|
|
}
|
|
curpos=0;
|
|
fillout=0;
|
|
}
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_Ed: /* mdoc(7) */
|
|
{
|
|
if (mandoc_bd_options & BD_LITERAL)
|
|
{
|
|
if (!fillout)
|
|
{
|
|
out_html(set_font("R"));
|
|
out_html(change_to_size('0'));
|
|
out_html("</PRE>\n");
|
|
}
|
|
}
|
|
if (mandoc_bd_options & BD_INDENT)
|
|
out_html("</BLOCKQUOTE>\n");
|
|
curpos=0;
|
|
fillout=1;
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_Be: /* mdoc(7) */
|
|
{
|
|
c=c+j;
|
|
if (fillout)
|
|
out_html("<br><br>");
|
|
else
|
|
{
|
|
out_html(NEWLINE);
|
|
}
|
|
curpos=0;
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_Xr: /* mdoc(7) */ // ### FIXME: it should issue a <a href="man:somewhere(x)"> directly
|
|
{
|
|
/* Translate xyz 1 to xyz(1)
|
|
* Allow for multiple spaces. Allow the section to be missing.
|
|
*/
|
|
char buff[NULL_TERMINATED(MED_STR_MAX)];
|
|
char *bufptr;
|
|
trans_char(c,'"','\a');
|
|
bufptr = buff;
|
|
c = c+j;
|
|
if (*c == '\n') c++; /* Skip spaces */
|
|
while (isspace(*c) && *c != '\n') c++;
|
|
while (isalnum(*c) || *c == '.' || *c == ':' || *c == '_' || *c == '-')
|
|
{
|
|
/* Copy the xyz part */
|
|
*bufptr = *c;
|
|
bufptr++;
|
|
if (bufptr >= buff + MED_STR_MAX) break;
|
|
c++;
|
|
}
|
|
while (isspace(*c) && *c != '\n') c++; /* Skip spaces */
|
|
if (isdigit(*c))
|
|
{
|
|
/* Convert the number if there is one */
|
|
*bufptr = '(';
|
|
bufptr++;
|
|
if (bufptr < buff + MED_STR_MAX)
|
|
{
|
|
while (isalnum(*c))
|
|
{
|
|
*bufptr = *c;
|
|
bufptr++;
|
|
if (bufptr >= buff + MED_STR_MAX) break;
|
|
c++;
|
|
}
|
|
if (bufptr < buff + MED_STR_MAX)
|
|
{
|
|
*bufptr = ')';
|
|
bufptr++;
|
|
}
|
|
}
|
|
}
|
|
while (*c != '\n')
|
|
{
|
|
/* Copy the remainder */
|
|
if (!isspace(*c))
|
|
{
|
|
*bufptr = *c;
|
|
bufptr++;
|
|
if (bufptr >= buff + MED_STR_MAX) break;
|
|
}
|
|
c++;
|
|
}
|
|
*bufptr = '\n';
|
|
bufptr[1] = 0;
|
|
scan_troff_mandoc(buff, 1, NULL);
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Fl: // mdoc(7) "FLags"
|
|
{
|
|
trans_char(c,'"','\a');
|
|
c+=j;
|
|
sl=fill_words(c, wordlist, &words, true, &c);
|
|
out_html(set_font("B"));
|
|
if (!words)
|
|
{
|
|
out_html("-"); // stdin or stdout
|
|
}
|
|
else
|
|
{
|
|
for (i=0;i<words;++i)
|
|
{
|
|
if (ispunct(wordlist[i][0]) && wordlist[i][0]!='-')
|
|
{
|
|
scan_troff_mandoc(wordlist[i], 1, NULL);
|
|
}
|
|
else
|
|
{
|
|
if (i>0)
|
|
out_html(" "); // Put a space between flags
|
|
out_html("-");
|
|
scan_troff_mandoc(wordlist[i], 1, NULL);
|
|
}
|
|
}
|
|
}
|
|
out_html(set_font("R"));
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Pa: /* mdoc(7) */
|
|
case REQ_Pf: /* mdoc(7) */
|
|
{
|
|
trans_char(c,'"','\a');
|
|
c=c+j;
|
|
if (*c=='\n') c++;
|
|
c=scan_troff_mandoc(c, 1, NULL);
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Pp: /* mdoc(7) */
|
|
{
|
|
if (fillout)
|
|
out_html("<br><br>\n");
|
|
else
|
|
{
|
|
out_html(NEWLINE);
|
|
}
|
|
curpos=0;
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
case REQ_Aq: // mdoc(7) "Angle bracket Quote"
|
|
c=process_quote(c,j,"<",">");
|
|
break;
|
|
case REQ_Bq: // mdoc(7) "Bracket Quote"
|
|
c=process_quote(c,j,"[","]");
|
|
break;
|
|
case REQ_Dq: // mdoc(7) "Double Quote"
|
|
c=process_quote(c,j,"“","”");
|
|
break;
|
|
case REQ_Pq: // mdoc(7) "Parenthese Quote"
|
|
c=process_quote(c,j,"(",")");
|
|
break;
|
|
case REQ_Qq: // mdoc(7) "straight double Quote"
|
|
c=process_quote(c,j,""",""");
|
|
break;
|
|
case REQ_Sq: // mdoc(7) "Single Quote"
|
|
c=process_quote(c,j,"‘","’");
|
|
break;
|
|
case REQ_Op: /* mdoc(7) */
|
|
{
|
|
trans_char(c,'"','\a');
|
|
c=c+j;
|
|
if (*c=='\n') c++;
|
|
out_html(set_font("R"));
|
|
out_html("[");
|
|
c=scan_troff_mandoc(c, 1, NULL);
|
|
out_html(set_font("R"));
|
|
out_html("]");
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Oo: /* mdoc(7) */
|
|
{
|
|
trans_char(c,'"','\a');
|
|
c=c+j;
|
|
if (*c=='\n') c++;
|
|
out_html(set_font("R"));
|
|
out_html("[");
|
|
c=scan_troff_mandoc(c, 1, NULL);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Oc: /* mdoc(7) */
|
|
{
|
|
trans_char(c,'"','\a');
|
|
c=c+j;
|
|
c=scan_troff_mandoc(c, 1, NULL);
|
|
out_html(set_font("R"));
|
|
out_html("]");
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Ql: /* mdoc(7) */
|
|
{
|
|
/* Single quote first word in the line */
|
|
char *sp;
|
|
trans_char(c,'"','\a');
|
|
c=c+j;
|
|
if (*c=='\n') c++;
|
|
sp = c;
|
|
do
|
|
{
|
|
/* Find first whitespace after the
|
|
* first word that isn't a mandoc macro
|
|
*/
|
|
while (*sp && isspace(*sp)) sp++;
|
|
while (*sp && !isspace(*sp)) sp++;
|
|
} while (*sp && isupper(*(sp-2)) && islower(*(sp-1)));
|
|
|
|
/* Use a newline to mark the end of text to
|
|
* be quoted
|
|
*/
|
|
if (*sp) *sp = '\n';
|
|
out_html("`"); /* Quote the text */
|
|
c=scan_troff_mandoc(c, 1, NULL);
|
|
out_html("'");
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Ar: /* mdoc(7) */
|
|
{
|
|
/* parse one line in italics */
|
|
out_html(set_font("I"));
|
|
trans_char(c,'"','\a');
|
|
c=c+j;
|
|
if (*c=='\n')
|
|
{
|
|
/* An empty Ar means "file ..." */
|
|
out_html("file ...");
|
|
}
|
|
else
|
|
c=scan_troff_mandoc(c, 1, NULL);
|
|
out_html(set_font("R"));
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Em: /* mdoc(7) */
|
|
{
|
|
out_html("<em>");
|
|
trans_char(c,'"','\a');
|
|
c+=j;
|
|
if (*c=='\n') c++;
|
|
c=scan_troff_mandoc(c, 1, NULL);
|
|
out_html("</em>");
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Ad: /* mdoc(7) */
|
|
case REQ_Va: /* mdoc(7) */
|
|
case REQ_Xc: /* mdoc(7) */
|
|
{
|
|
/* parse one line in italics */
|
|
out_html(set_font("I"));
|
|
trans_char(c,'"','\a');
|
|
c=c+j;
|
|
if (*c=='\n') c++;
|
|
c=scan_troff_mandoc(c, 1, NULL);
|
|
out_html(set_font("R"));
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Nd: /* mdoc(7) */
|
|
{
|
|
trans_char(c,'"','\a');
|
|
c=c+j;
|
|
if (*c=='\n') c++;
|
|
out_html(" - ");
|
|
c=scan_troff_mandoc(c, 1, NULL);
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Nm: // mdoc(7) "Name Macro" ### FIXME
|
|
{
|
|
static char mandoc_name[NULL_TERMINATED(SMALL_STR_MAX)] = ""; // ### TODO Use QCString
|
|
trans_char(c,'"','\a');
|
|
c=c+j;
|
|
|
|
if (mandoc_synopsis && mandoc_name_count)
|
|
{
|
|
/* Break lines only in the Synopsis.
|
|
* The Synopsis section seems to be treated
|
|
* as a special case - Bummer!
|
|
*/
|
|
out_html("<BR>");
|
|
}
|
|
else if (!mandoc_name_count)
|
|
{
|
|
char *nextbreak = strchr(c, '\n');
|
|
char *nextspace = strchr(c, ' ');
|
|
if (nextspace < nextbreak)
|
|
nextbreak = nextspace;
|
|
|
|
if (nextbreak)
|
|
{
|
|
/* Remember the name for later. */
|
|
strlimitcpy(mandoc_name, c, nextbreak - c, SMALL_STR_MAX);
|
|
}
|
|
}
|
|
mandoc_name_count++;
|
|
|
|
out_html(set_font("B"));
|
|
// ### FIXME: fill_words must be used
|
|
while (*c == ' '|| *c == '\t') c++;
|
|
if ((tolower(*c) >= 'a' && tolower(*c) <= 'z' ) || (*c >= '0' && *c <= '9'))
|
|
{
|
|
// alphanumeric argument
|
|
c=scan_troff_mandoc(c, 1, NULL);
|
|
out_html(set_font("R"));
|
|
out_html(NEWLINE);
|
|
}
|
|
else
|
|
{
|
|
/* If Nm has no argument, use one from an earlier
|
|
* Nm command that did have one. Hope there aren't
|
|
* too many commands that do this.
|
|
*/
|
|
out_html(mandoc_name);
|
|
out_html(set_font("R"));
|
|
}
|
|
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_Cd: /* mdoc(7) */
|
|
case REQ_Cm: /* mdoc(7) */
|
|
case REQ_Ic: /* mdoc(7) */
|
|
case REQ_Ms: /* mdoc(7) */
|
|
case REQ_Or: /* mdoc(7) */
|
|
case REQ_Sy: /* mdoc(7) */
|
|
{
|
|
/* parse one line in bold */
|
|
out_html(set_font("B"));
|
|
trans_char(c,'"','\a');
|
|
c=c+j;
|
|
if (*c=='\n') c++;
|
|
c=scan_troff_mandoc(c, 1, NULL);
|
|
out_html(set_font("R"));
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
// ### FIXME: punctuation is handled badly!
|
|
case REQ_Dv: /* mdoc(7) */
|
|
case REQ_Ev: /* mdoc(7) */
|
|
case REQ_Fr: /* mdoc(7) */
|
|
case REQ_Li: /* mdoc(7) */
|
|
case REQ_No: /* mdoc(7) */
|
|
case REQ_Ns: /* mdoc(7) */
|
|
case REQ_Tn: /* mdoc(7) */
|
|
case REQ_nN: /* mdoc(7) */
|
|
{
|
|
trans_char(c,'"','\a');
|
|
c=c+j;
|
|
if (*c=='\n') c++;
|
|
out_html(set_font("B"));
|
|
c=scan_troff_mandoc(c, 1, NULL);
|
|
out_html(set_font("R"));
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_perc_A: /* mdoc(7) biblio stuff */
|
|
case REQ_perc_D:
|
|
case REQ_perc_N:
|
|
case REQ_perc_O:
|
|
case REQ_perc_P:
|
|
case REQ_perc_Q:
|
|
case REQ_perc_V:
|
|
{
|
|
c=c+j;
|
|
if (*c=='\n') c++;
|
|
c=scan_troff(c, 1, NULL); /* Don't allow embedded mandoc coms */
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_perc_B:
|
|
case REQ_perc_J:
|
|
case REQ_perc_R:
|
|
case REQ_perc_T:
|
|
{
|
|
c=c+j;
|
|
out_html(set_font("I"));
|
|
if (*c=='\n') c++;
|
|
c=scan_troff(c, 1, NULL); /* Don't allow embedded mandoc coms */
|
|
out_html(set_font("R"));
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
break;
|
|
}
|
|
case REQ_UR: // ### FIXME man(7) "URl"
|
|
{
|
|
ignore_links=true;
|
|
c+=j;
|
|
char* newc;
|
|
h=fill_words(c, wordlist, &words, false, &newc);
|
|
*h=0;
|
|
if (words>0)
|
|
{
|
|
h=wordlist[0];
|
|
// A parameter : means that we do not want an URL, not here and not until .UE
|
|
ur_ignore=(!qstrcmp(h,":"));
|
|
}
|
|
else
|
|
{
|
|
// We cannot find the URL, assume :
|
|
ur_ignore=true;
|
|
h=0;
|
|
}
|
|
if (!ur_ignore && words>0)
|
|
{
|
|
out_html("<a href=\"");
|
|
out_html(h);
|
|
out_html("\">");
|
|
}
|
|
c=newc; // Go to next line
|
|
break;
|
|
}
|
|
case REQ_UE: // ### FIXME man(7) "Url End"
|
|
{
|
|
c+=j;
|
|
c = skip_till_newline(c);
|
|
if (!ur_ignore)
|
|
{
|
|
out_html("</a>");
|
|
}
|
|
ur_ignore=false;
|
|
ignore_links=false;
|
|
break;
|
|
}
|
|
case REQ_UN: // ### FIXME man(7) "Url Named anchor"
|
|
{
|
|
c+=j;
|
|
char* newc;
|
|
h=fill_words(c, wordlist, &words, false, &newc);
|
|
*h=0;
|
|
if (words>0)
|
|
{
|
|
h=wordlist[0];
|
|
out_html("<a name=\">");
|
|
out_html(h);
|
|
out_html("\" id=\"");
|
|
out_html(h);
|
|
out_html("\"></a>");
|
|
}
|
|
c=newc;
|
|
break;
|
|
}
|
|
case REQ_nroff: // groff(7) "NROFF mode"
|
|
mode = true;
|
|
case REQ_troff: // groff(7) "TROFF mode"
|
|
{
|
|
s_nroff = mode;
|
|
c+=j;
|
|
c = skip_till_newline(c);
|
|
}
|
|
case REQ_als: // groff(7) "ALias String"
|
|
{
|
|
/*
|
|
* Note an alias is supposed to be something like a hard link
|
|
* However to make it simplier, we only copy the string.
|
|
*/
|
|
// Be careful: unlike .rn, the destination is first, origin is second
|
|
kdDebug(7107) << "start .als" << endl;
|
|
c+=j;
|
|
const TQCString name ( scan_identifier( c ) );
|
|
if ( name.isEmpty() )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: empty destination string to alias" << endl;
|
|
break;
|
|
}
|
|
while (*c && isspace(*c) && *c!='\n') ++c;
|
|
const TQCString name2 ( scan_identifier ( c ) );
|
|
if ( name2.isEmpty() )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: empty origin string to alias" << endl;
|
|
break;
|
|
}
|
|
kdDebug(7107) << "Alias " << name2 << " to " << name << endl;
|
|
c=skip_till_newline(c);
|
|
if ( name == name2 )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: same origin and destination string to alias: " << name << endl;
|
|
break;
|
|
}
|
|
// Second parametr is origin (unlike in .rn)
|
|
TQMap<TQCString,StringDefinition>::iterator it=s_stringDefinitionMap.find(name2);
|
|
if (it==s_stringDefinitionMap.end())
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: cannot find string to make alias: " << name2 << endl;
|
|
}
|
|
else
|
|
{
|
|
StringDefinition def=(*it);
|
|
s_stringDefinitionMap.insert(name,def);
|
|
}
|
|
kdDebug(7107) << "end .als" << endl;
|
|
break;
|
|
}
|
|
case REQ_rr: // groff(7) "Remove number Register"
|
|
{
|
|
kdDebug(7107) << "start .rr" << endl;
|
|
c += j;
|
|
const TQCString name ( scan_identifier( c ) );
|
|
if ( name.isEmpty() )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: empty origin string to remove/rename: " << endl;
|
|
break;
|
|
}
|
|
c = skip_till_newline( c );
|
|
TQMap <TQCString, NumberDefinition>::iterator it = s_numberDefinitionMap.find( name );
|
|
if ( it == s_numberDefinitionMap.end() )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: trying to remove inexistant number register: " << endl;
|
|
}
|
|
else
|
|
{
|
|
s_numberDefinitionMap.remove( name );
|
|
}
|
|
kdDebug(7107) << "end .rr" << endl;
|
|
break;
|
|
}
|
|
case REQ_rnn: // groff(7) "ReName Number register"
|
|
{
|
|
kdDebug(7107) << "start .rnn" << endl;
|
|
c+=j;
|
|
const TQCString name ( scan_identifier ( c ) );
|
|
if ( name.isEmpty() )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: empty origin to remove/rename number register" << endl;
|
|
break;
|
|
}
|
|
while (*c && isspace(*c) && *c!='\n') ++c;
|
|
const TQCString name2 ( scan_identifier ( c ) );
|
|
if ( name2.isEmpty() )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: empty destination to rename number register " << endl;
|
|
break;
|
|
}
|
|
c = skip_till_newline( c );
|
|
TQMap<TQCString,NumberDefinition>::iterator it=s_numberDefinitionMap.find(name);
|
|
if (it==s_numberDefinitionMap.end())
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: cannot find number register to rename: " << name << endl;
|
|
}
|
|
else
|
|
{
|
|
NumberDefinition def=(*it);
|
|
s_numberDefinitionMap.remove(name); // ### QT4: removeAll
|
|
s_numberDefinitionMap.insert(name2,def);
|
|
}
|
|
kdDebug(7107) << "end .rnn" << endl;
|
|
break;
|
|
}
|
|
case REQ_aln: // groff(7) "ALias Number Register"
|
|
{
|
|
/*
|
|
* Note an alias is supposed to be something like a hard link
|
|
* However to make it simplier, we only copy the string.
|
|
*/
|
|
// Be careful: unlike .rnn, the destination is first, origin is second
|
|
kdDebug(7107) << "start .aln" << endl;
|
|
c+=j;
|
|
const TQCString name ( scan_identifier( c ) );
|
|
if ( name.isEmpty() )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: empty destination number register to alias" << endl;
|
|
break;
|
|
}
|
|
while (*c && isspace(*c) && *c!='\n') ++c;
|
|
const TQCString name2 ( scan_identifier( c ) );
|
|
if ( name2.isEmpty() )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: empty origin number register to alias" << endl;
|
|
break;
|
|
}
|
|
kdDebug(7107) << "Alias " << name2 << " to " << name << endl;
|
|
c = skip_till_newline( c );
|
|
if ( name == name2 )
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: same origin and destination number register to alias: " << name << endl;
|
|
break;
|
|
}
|
|
// Second parametr is origin (unlike in .rnn)
|
|
TQMap<TQCString,NumberDefinition>::iterator it=s_numberDefinitionMap.find(name2);
|
|
if (it==s_numberDefinitionMap.end())
|
|
{
|
|
kdDebug(7107) << "EXCEPTION: cannot find string to make alias: " << name2 << endl;
|
|
}
|
|
else
|
|
{
|
|
NumberDefinition def=(*it);
|
|
s_numberDefinitionMap.insert(name,def);
|
|
}
|
|
kdDebug(7107) << "end .aln" << endl;
|
|
break;
|
|
}
|
|
case REQ_shift: // groff(7) "SHIFT parameter"
|
|
{
|
|
c+=j;
|
|
h=c;
|
|
while (*h && *h!='\n' && isdigit(*h) ) ++h;
|
|
const char tempchar = *h;
|
|
*h = 0;
|
|
const TQCString number = c;
|
|
*h = tempchar;
|
|
c = skip_till_newline( h );
|
|
unsigned int result = 1; // Numbers of shifts to do
|
|
if ( !number.isEmpty() )
|
|
{
|
|
bool ok = false;
|
|
result = number.toUInt(&ok);
|
|
if ( !ok || result < 1 )
|
|
result = 1;
|
|
}
|
|
for ( unsigned int num = 0; num < result; ++num )
|
|
{
|
|
if ( !s_argumentList.isEmpty() )
|
|
s_argumentList.pop_front();
|
|
}
|
|
break;
|
|
}
|
|
case REQ_while: // groff(7) "WHILE loop"
|
|
{
|
|
request_while( c, j, mandoc_command );
|
|
break;
|
|
}
|
|
case REQ_do: // groff(7) "DO command"
|
|
{
|
|
// HACK: we just replace do by a \n and a .
|
|
*c = '\n';
|
|
c++;
|
|
*c = '.';
|
|
// The . will be treated as next character
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
if (mandoc_command &&
|
|
((isupper(*c) && islower(*(c+1)))
|
|
|| (islower(*c) && isupper(*(c+1)))) )
|
|
{
|
|
/* Let through any mdoc(7) commands that haven't
|
|
* been delt with.
|
|
* I don't want to miss anything out of the text.
|
|
*/
|
|
char buf[4] = { c[0], c[1], ' ', 0 };
|
|
out_html(buf); /* Print the command (it might just be text). */
|
|
c=c+j;
|
|
trans_char(c,'"','\a');
|
|
if (*c=='\n') c++;
|
|
out_html(set_font("R"));
|
|
c=scan_troff(c, 1, NULL);
|
|
out_html(NEWLINE);
|
|
if (fillout)
|
|
curpos++;
|
|
else
|
|
curpos=0;
|
|
}
|
|
else
|
|
c=skip_till_newline(c);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (fillout)
|
|
{
|
|
out_html(NEWLINE);
|
|
curpos++;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
static int contained_tab=0;
|
|
static bool mandoc_line=false; /* Signals whether to look for embedded mandoc
|
|
* commands.
|
|
*/
|
|
|
|
static char *scan_troff(char *c, bool san, char **result)
|
|
{ /* san : stop at newline */
|
|
char *h;
|
|
char intbuff[NULL_TERMINATED(MED_STR_MAX)];
|
|
int ibp=0;
|
|
#define FLUSHIBP if (ibp) { intbuff[ibp]=0; out_html(intbuff); ibp=0; }
|
|
char *exbuffer;
|
|
int exbuffpos, exbuffmax, exnewline_for_fun;
|
|
bool exscaninbuff;
|
|
int usenbsp=0;
|
|
|
|
exbuffer=buffer;
|
|
exbuffpos=buffpos;
|
|
exbuffmax=buffmax;
|
|
exnewline_for_fun=newline_for_fun;
|
|
exscaninbuff=scaninbuff;
|
|
newline_for_fun=0;
|
|
if (result) {
|
|
if (*result) {
|
|
buffer=*result;
|
|
buffpos=tqstrlen(buffer);
|
|
buffmax=buffpos;
|
|
} else {
|
|
buffer = stralloc(LARGE_STR_MAX);
|
|
buffpos=0;
|
|
buffmax=LARGE_STR_MAX;
|
|
}
|
|
scaninbuff=true;
|
|
}
|
|
h=c; // ### FIXME below are too many tests that may go before the posiiton of c
|
|
/* start scanning */
|
|
|
|
// ### VERIFY: a dot must be at first position, we cannot add newlines or it would allow spaces before a dot
|
|
while (*h == ' ')
|
|
{
|
|
#if 1
|
|
++h;
|
|
#else
|
|
*h++ = '\n';
|
|
#endif
|
|
}
|
|
|
|
while (h && *h && (!san || newline_for_fun || *h!='\n')) {
|
|
|
|
if (*h==escapesym) {
|
|
h++;
|
|
FLUSHIBP;
|
|
h = scan_escape(h);
|
|
} else if (*h==controlsym && h[-1]=='\n') {
|
|
h++;
|
|
FLUSHIBP;
|
|
h = scan_request(h);
|
|
if (h && san && h[-1]=='\n') h--;
|
|
} else if (mandoc_line
|
|
&& ((*(h-1)) && (isspace(*(h-1)) || (*(h-1))=='\n'))
|
|
&& *(h) && isupper(*(h))
|
|
&& *(h+1) && islower(*(h+1))
|
|
&& *(h+2) && isspace(*(h+2))) {
|
|
// mdoc(7) embedded command eg ".It Fl Ar arg1 Fl Ar arg2"
|
|
FLUSHIBP;
|
|
h = scan_request(h);
|
|
if (san && h[-1]=='\n') h--;
|
|
} else if (*h==nobreaksym && h[-1]=='\n') {
|
|
h++;
|
|
FLUSHIBP;
|
|
h = scan_request(h);
|
|
if (san && h[-1]=='\n') h--;
|
|
} else {
|
|
/* int mx; */
|
|
if (still_dd && isalnum(*h) && h[-1]=='\n') {
|
|
/* sometimes a .HP request is not followed by a .br request */
|
|
FLUSHIBP;
|
|
out_html("<DD>");
|
|
curpos=0;
|
|
still_dd=false;
|
|
}
|
|
switch (*h) {
|
|
case '&':
|
|
intbuff[ibp++]='&';
|
|
intbuff[ibp++]='a';
|
|
intbuff[ibp++]='m';
|
|
intbuff[ibp++]='p';
|
|
intbuff[ibp++]=';';
|
|
curpos++;
|
|
break;
|
|
case '<':
|
|
intbuff[ibp++]='&';
|
|
intbuff[ibp++]='l';
|
|
intbuff[ibp++]='t';
|
|
intbuff[ibp++]=';';
|
|
curpos++;
|
|
break;
|
|
case '>':
|
|
intbuff[ibp++]='&';
|
|
intbuff[ibp++]='g';
|
|
intbuff[ibp++]='t';
|
|
intbuff[ibp++]=';';
|
|
curpos++;
|
|
break;
|
|
case '"':
|
|
intbuff[ibp++]='&';
|
|
intbuff[ibp++]='q';
|
|
intbuff[ibp++]='u';
|
|
intbuff[ibp++]='o';
|
|
intbuff[ibp++]='t';
|
|
intbuff[ibp++]=';';
|
|
curpos++;
|
|
break;
|
|
case '\n':
|
|
if (h != c && h[-1]=='\n' && fillout) {
|
|
intbuff[ibp++]='<';
|
|
intbuff[ibp++]='P';
|
|
intbuff[ibp++]='>';
|
|
}
|
|
if (contained_tab && fillout) {
|
|
intbuff[ibp++]='<';
|
|
intbuff[ibp++]='B';
|
|
intbuff[ibp++]='R';
|
|
intbuff[ibp++]='>';
|
|
}
|
|
contained_tab=0;
|
|
curpos=0;
|
|
usenbsp=0;
|
|
intbuff[ibp++]='\n';
|
|
break;
|
|
case '\t':
|
|
{
|
|
int curtab=0;
|
|
contained_tab=1;
|
|
FLUSHIBP;
|
|
/* like a typewriter, not like TeX */
|
|
tabstops[19]=curpos+1;
|
|
while (curtab<maxtstop && tabstops[curtab]<=curpos)
|
|
curtab++;
|
|
if (curtab<maxtstop) {
|
|
if (!fillout) {
|
|
while (curpos<tabstops[curtab]) {
|
|
intbuff[ibp++]=' ';
|
|
if (ibp>480) { FLUSHIBP; }
|
|
curpos++;
|
|
}
|
|
} else {
|
|
out_html("<TT>");
|
|
while (curpos<tabstops[curtab]) {
|
|
out_html(" ");
|
|
curpos++;
|
|
}
|
|
out_html("</TT>");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
if (*h==' ' && (h[-1]=='\n' || usenbsp)) {
|
|
FLUSHIBP;
|
|
if (!usenbsp && fillout) {
|
|
out_html("<BR>");
|
|
curpos=0;
|
|
}
|
|
usenbsp=fillout;
|
|
if (usenbsp) out_html(" "); else intbuff[ibp++]=' ';
|
|
} else if (*h>31 && *h<127) intbuff[ibp++]=*h;
|
|
else if (((unsigned char)(*h))>127) {
|
|
intbuff[ibp++]=*h;
|
|
}
|
|
curpos++;
|
|
break;
|
|
}
|
|
if (ibp > (MED_STR_MAX - 20)) FLUSHIBP;
|
|
h++;
|
|
}
|
|
}
|
|
FLUSHIBP;
|
|
if (buffer) buffer[buffpos]='\0';
|
|
if (san && h && *h) h++;
|
|
newline_for_fun=exnewline_for_fun;
|
|
if (result) {
|
|
*result = buffer;
|
|
buffer=exbuffer;
|
|
buffpos=exbuffpos;
|
|
buffmax=exbuffmax;
|
|
scaninbuff=exscaninbuff;
|
|
}
|
|
|
|
return h;
|
|
}
|
|
|
|
|
|
static char *scan_troff_mandoc(char *c, bool san, char **result)
|
|
{
|
|
char *ret;
|
|
char *end = c;
|
|
bool oldval = mandoc_line;
|
|
mandoc_line = true;
|
|
while (*end && *end != '\n') {
|
|
end++;
|
|
}
|
|
|
|
if (end > c + 2
|
|
&& ispunct(*(end - 1))
|
|
&& isspace(*(end - 2)) && *(end - 2) != '\n') {
|
|
/* Don't format lonely punctuation E.g. in "xyz ," format
|
|
* the xyz and then append the comma removing the space.
|
|
*/
|
|
*(end - 2) = '\n';
|
|
ret = scan_troff(c, san, result);
|
|
*(end - 2) = *(end - 1);
|
|
*(end - 1) = ' ';
|
|
}
|
|
else {
|
|
ret = scan_troff(c, san, result);
|
|
}
|
|
mandoc_line = oldval;
|
|
return ret;
|
|
}
|
|
|
|
// Entry point
|
|
void scan_man_page(const char *man_page)
|
|
{
|
|
if (!man_page)
|
|
return;
|
|
|
|
kdDebug(7107) << "Start scanning man page" << endl;
|
|
|
|
// ## Do more init
|
|
// Unlike man2html, we actually call this several times, hence the need to
|
|
// properly cleanup all those static vars
|
|
s_ifelseval.clear();
|
|
|
|
s_characterDefinitionMap.clear();
|
|
InitCharacterDefinitions();
|
|
|
|
s_stringDefinitionMap.clear();
|
|
InitStringDefinitions();
|
|
|
|
s_numberDefinitionMap.clear();
|
|
InitNumberDefinitions();
|
|
|
|
s_argumentList.clear();
|
|
|
|
section = 0;
|
|
|
|
s_dollarZero = ""; // No macro called yet!
|
|
|
|
output_possible = false;
|
|
int strLength = tqstrlen(man_page);
|
|
char *buf = new char[strLength + 2];
|
|
qstrcpy(buf+1, man_page);
|
|
buf[0] = '\n';
|
|
|
|
kdDebug(7107) << "Parse man page" << endl;
|
|
|
|
scan_troff(buf+1,0,NULL);
|
|
|
|
kdDebug(7107) << "Man page parsed!" << endl;
|
|
|
|
while (itemdepth || dl_set[itemdepth]) {
|
|
out_html("</DL>\n");
|
|
if (dl_set[itemdepth]) dl_set[itemdepth]=0;
|
|
else if (itemdepth > 0) itemdepth--;
|
|
}
|
|
|
|
out_html(set_font("R"));
|
|
out_html(change_to_size(0));
|
|
if (!fillout) {
|
|
fillout=1;
|
|
out_html("</PRE>");
|
|
}
|
|
out_html(NEWLINE);
|
|
|
|
if (section) {
|
|
output_real("<div style=\"margin-left: 2cm\">\n");
|
|
section = 0;
|
|
}
|
|
|
|
if (output_possible) {
|
|
output_real("</div>\n");
|
|
output_real("<div class=\"bannerBottom\" style=\"background-image: url(");
|
|
output_real(cssPath);
|
|
output_real("/bottom-middle.png); background-repeat: x-repeat; width: 100%; height: 100px; bottom:0pt;\">\n");
|
|
output_real("<div class=\"bannerBottomLeft\">\n");
|
|
output_real("<img src=\"");
|
|
output_real(cssPath);
|
|
output_real("/bottom-left.png\" style=\"margin: 0pt;\" alt=\"Bottom left of the banner\">\n");
|
|
output_real("</div>\n");
|
|
output_real("<div class=\"bannerBottomRight\">\n");
|
|
output_real("<img src=\"");
|
|
output_real(cssPath);
|
|
output_real("/bottom-right.png\" style=\"margin: 0pt\" alt=\"Bottom right of the banner\">\n");
|
|
output_real("</div>\n");
|
|
output_real("</div>\n");
|
|
|
|
output_real("</BODY>\n</HTML>\n");
|
|
}
|
|
delete [] buf;
|
|
|
|
// Release memory
|
|
s_characterDefinitionMap.clear();
|
|
s_stringDefinitionMap.clear();
|
|
s_numberDefinitionMap.clear();
|
|
s_argumentList.clear();
|
|
|
|
// reinit static variables for reuse
|
|
delete [] buffer;
|
|
buffer = 0;
|
|
|
|
escapesym='\\';
|
|
nobreaksym='\'';
|
|
controlsym='.';
|
|
fieldsym=0;
|
|
padsym=0;
|
|
|
|
buffpos=0;
|
|
buffmax=0;
|
|
scaninbuff=false;
|
|
itemdepth=0;
|
|
for (int i = 0; i < 20; i++)
|
|
dl_set[i] = 0;
|
|
still_dd=false;
|
|
for (int i = 0; i < 12; i++)
|
|
tabstops[i] = (i+1)*8;
|
|
maxtstop=12;
|
|
curpos=0;
|
|
|
|
mandoc_name_count = 0;
|
|
}
|
|
|
|
#ifdef SIMPLE_MAN2HTML
|
|
void output_real(const char *insert)
|
|
{
|
|
cout << insert;
|
|
}
|
|
|
|
char *read_man_page(const char *filename)
|
|
{
|
|
int man_pipe = 0;
|
|
char *man_buf = NULL;
|
|
|
|
FILE *man_stream = NULL;
|
|
struct stat stbuf;
|
|
size_t buf_size;
|
|
if (stat(filename, &stbuf) == -1) {
|
|
std::cerr << "read_man_page: can't find " << filename << endl;
|
|
return NULL;
|
|
}
|
|
if (!S_ISREG(stbuf.st_mode)) {
|
|
std::cerr << "read_man_page: no file " << filename << endl;
|
|
return NULL;
|
|
}
|
|
buf_size = stbuf.st_size;
|
|
man_buf = stralloc(buf_size+5);
|
|
man_pipe = 0;
|
|
man_stream = fopen(filename, "r");
|
|
if (man_stream) {
|
|
man_buf[0] = '\n';
|
|
if (fread(man_buf+1, 1, buf_size, man_stream) == buf_size) {
|
|
man_buf[buf_size] = '\n';
|
|
man_buf[buf_size + 1] = man_buf[buf_size + 2] = '\0';
|
|
}
|
|
else {
|
|
man_buf = NULL;
|
|
}
|
|
fclose(man_stream);
|
|
}
|
|
return man_buf;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
htmlPath = ".";
|
|
cssPath = ".";
|
|
if (argc < 2) {
|
|
std::cerr << "call: " << argv[0] << " <filename>\n";
|
|
return 1;
|
|
}
|
|
if (chdir(argv[1])) {
|
|
char *buf = read_man_page(argv[1]);
|
|
if (buf) {
|
|
scan_man_page(buf);
|
|
delete [] buf;
|
|
}
|
|
} else {
|
|
DIR *dir = opendir(".");
|
|
struct dirent *ent;
|
|
while ((ent = readdir(dir)) != NULL) {
|
|
cerr << "converting " << ent->d_name << endl;
|
|
char *buf = read_man_page(ent->d_name);
|
|
if (buf) {
|
|
scan_man_page(buf);
|
|
delete [] buf;
|
|
}
|
|
}
|
|
closedir(dir);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
#endif
|