diff --git a/kioslave/man/man2html.cpp b/kioslave/man/man2html.cpp
index 9045ece8c8..e21877de82 100644
--- a/kioslave/man/man2html.cpp
+++ b/kioslave/man/man2html.cpp
@@ -1,6141 +1,6138 @@
/*
This file is part of the KDE libraries
Copyright (C) 2000 Stephan Kulow
Copyright (C) 2005 Nicolas GOUTTE
Copyright (C) 2011 Martin Koller
... and others (see SVN history)
*/
// 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
/*
* man2html-linux-1.0/1.1
* This version modified for Redhat/Caldera linux - March 1996.
* Michael Hamilton .
*
* 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 .
*
* 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
** ^^^^
** 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
\n");
else
out_html("\n");
if (!oldfillout) out_html("
");
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';
+ bool oldSkipEscape = skip_escape;
+ skip_escape = true; // evaluating an expression shall not print it
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 || qstrncmp(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 || qstrncmp(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:
{
kDebug(7107) << "Unknown operator " << char(oper);
}
}
oper = 0;
}
}
if (*c == ')') c++;
}
*result = value;
+
+ skip_escape = oldSkipEscape;
+
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++;
}
}
//---------------------------------------------------------------------
// parse 1 line (or a line which stretches multiple lines by \(enter) )
// return all arguments starting at \p c in \p args
// returns the pointer to the next char where scanning should continue
// (which is the char after the ending \n)
// argPointers .. a list of pointers to the startchars of each arg pointing into the string given with c
void getArguments(/* const */ char *&c, QList &args, QList *argPointers = 0)
{
args.clear();
if ( argPointers )
argPointers->clear();
QByteArray arg;
arg.reserve(30); // reduce num of reallocs
bool inString = false;
bool inArgument = false;
for (; *c && (*c != '\n'); c++)
{
if ( *c == '"' )
{
if ( !inString )
{
inString = true; // start of quoted argument
}
else
{
// according to http://heirloom.sourceforge.net/doctools/troff.pdf chapter 7.3
// two consecutive quotes inside a string is one quote char
if ( *(c+1) == '"' )
{
arg += '"';
c++;
}
else // end of quoted argument
{
args.append(arg);
arg.clear();
inString = false;
inArgument = false;
}
}
}
else if ( *c == ' ' )
{
if ( inString )
{
arg += *c;
if ( !inArgument ) // argument not yet found (leading spaces)
{
inArgument = true;
if ( argPointers )
argPointers->append(c);
}
}
else if ( inArgument )
{
// end of previous argument
args.append(arg);
arg.clear();
inArgument = false;
}
}
else if ( (*c == escapesym) && (*(c+1) == ' ') )
{
// special handling \ shall be kept as is
arg += *c++;
arg += *c;
if ( !inArgument ) // argument not yet found (leading spaces)
{
inArgument = true;
if ( argPointers )
argPointers->append(c);
}
}
else if ( (*c == escapesym) && (*(c+1) == '\n') )
{
c++;
}
else if ( (*c == escapesym) && (*(c+1) == '"') ) // start of comment; skip rest of line
{
if ( inArgument )
{
// end of previous argument
args.append(arg);
arg.clear();
inArgument = false;
}
// skip rest of line
while ( *c && (*c != '\n') ) c++;
break;
}
else if ( *c != ' ' )
{
arg += *c;
if ( !inArgument ) // argument not yet found (leading spaces)
{
inArgument = true;
if ( argPointers )
argPointers->append(c);
}
}
}
if ( inArgument )
{
// end of previous argument
args.append(arg);
}
if ( *c ) c++;
#if 0
for (int i = 0; i < args.count(); i++)
{
qWarning("ARG:%d >>>%s<<<", i, args[i].data());
}
#endif
}
//---------------------------------------------------------------------
static const char * const 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 nroff and troff",
"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", "SPARC Architecture Manual",
"KR", "The C Programming Language",
NULL, NULL
};
static const char *lookup_abbrev(const 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 * const 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++;
else if (*c == '\0')
break;
}
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: .continue
kDebug(7107) << "Entering .while";
c += j;
char* newline = skip_till_newline(c);
const char oldchar = *newline;
*newline = 0;
// We store the full .while stuff into a QByteArray as if it would be a macro
const QByteArray macro = c ;
kDebug(7107) << "'Macro' of .while" << BYTEARRAY(macro);
// 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
break_the_while_loop = false;
while (result && !break_the_while_loop)
{
// Unlike for a normal macro, we have the condition at start, so we do not need to prepend extra bytes
char* liveloop = qstrdup(macro.data());
kDebug(7107) << "Scanning .while condition";
kDebug(7101) << "Loop macro " << liveloop;
char* end_expression = scan_expression(liveloop, &result);
kDebug(7101) << "After " << end_expression;
if (result)
{
kDebug(7107) << "New .while iteration";
// 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;
}
break_the_while_loop = false;
//
s_whileloop = oldwhileloop;
kDebug(7107) << "Ending .while";
}
//---------------------------------------------------------------------
// Processing mixed fonts requests 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++;
QList args;
getArguments(c, args);
for (int i = 0; i < args.count(); i++)
{
if (mode || inFMode)
{
out_html(" ");
curpos++;
}
out_html(set_font((i&1) ? font2 : font1));
scan_troff(args[i].data(), 1, NULL);
}
out_html(set_font("R"));
if (mode)
{
out_html(" ]");
curpos++;
}
out_html(NEWLINE);
if (!fillout)
curpos = 0;
else
curpos++;
}
//---------------------------------------------------------------------
// &%(#@ c programs !!!
//static int ifelseval=0;
// If/else can be nested!
static QStack 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 QByteArray 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 QByteArray name = c;
*h = tempchar;
if (name.isEmpty())
{
kDebug(7107) << "EXCEPTION: identifier empty!";
}
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)
int i = 0;
bool mode = false;
char *h = 0;
char *sl;
QList args;
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] == '$')
{
kDebug(7107) << "Found .\\$";
c = skip_till_newline(c); // ### TODO
}
else
{
// the result of the escape expansion must be parsed again
c++;
QByteArray cstr;
c = scan_escape_direct(c, cstr);
for (; *c && (*c != '\n'); c++) cstr += *c;
if ( cstr.length() )
scan_request(cstr.data());
}
}
else
{
int nlen = 0;
QByteArray 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] == '\t') j++;
/* search macro database of self-defined macros */
QMap::const_iterator it = s_stringDefinitionMap.constFind(macroName);
// ### HACK: e.g. nmap, smb.conf redefine SH, SS to increase the font, etc. for non-TTY output
// Ignore those to make the HTML result look better
if ( (macroName != "SH") && (macroName != "SS") &&
it != s_stringDefinitionMap.constEnd() )
{
kDebug(7107) << "CALLING MACRO: " << BYTEARRAY(macroName);
const QByteArray oldDollarZero = s_dollarZero; // Previous value of $0
s_dollarZero = macroName;
c += j;
getArguments(c, args);
for (i = 0; i < args.count(); i++)
{
char *h = 0;
if (mandoc_command)
scan_troff_mandoc(args[i].data(), 1, &h);
else
scan_troff(args[i].data(), 1, &h);
args[i] = h;
delete [] h;
}
if (!(*it).m_output.isEmpty())
{
//kDebug(7107) << "Macro content is: "<< BYTEARRAY( (*it).m_output );
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
qstrncpy(work + 1, (*it).m_output.data(), length + 1);
const QList oldArgumentList(s_argumentList);
s_argumentList.clear();
for (i = 0; i < args.count(); i++)
s_argumentList.push_back(args[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;
}
s_dollarZero = oldDollarZero;
kDebug(7107) << "ENDING MACRO: " << BYTEARRAY(macroName);
}
else
{
kDebug(7107) << "REQUEST: " << BYTEARRAY(macroName);
switch (RequestNum request = RequestHash::getRequest(macroName, macroName.length()))
{
case REQ_ab: // groff(7) "ABort"
{
h = c + j;
while (*h && *h != '\n') h++;
*h = '\0';
if (scaninbuff && buffpos)
{
buffer[buffpos] = '\0';
kDebug(7107) << "ABORT: " << buffer;
}
// ### TODO find a way to display it to the user
kDebug(7107) << "Aborting: .ab " << (c + j);
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"
{
kDebug(7107) << "Start .di";
c += j;
if (*c == '\n')
{
++c;
break;
}
const QByteArray name(scan_identifier(c));
while (*c && *c != '\n') c++;
c++;
h = c;
while (*c && qstrncmp(c, ".di", 3)) while (*c && *c++ != '\n');
*c = '\0';
char* result = 0;
scan_troff(h, 0, &result);
QMap::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);
kDebug(7107) << "end .di";
break;
}
case REQ_ds: // groff(7) "Define String variable"
mode = true;
case REQ_as: // groff (7) "Append String variable"
{
kDebug(7107) << "start .ds/.as";
int oldcurpos = curpos;
c += j;
const QByteArray name(scan_identifier(c));
if (name.isEmpty())
break;
// an initial " is removed to allow leading space
while (*c && isspace(*c)) c++;
if (*c == '"') c++;
single_escape = true;
curpos = 0;
char* result = 0;
c = scan_troff(c, 1, &result);
QMap::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;
kDebug(7107) << "end .ds/.as";
break;
}
case REQ_br: // groff(7) "line BReak"
{
if (still_dd)
out_html("
"); // ### VERIFY (does not look like generating good HTML)
else
out_html(" \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("
\n");
while (i && *c)
{
char *line = NULL;
c = scan_troff(c, 1, &line);
if (line && qstrncmp(line, " ", 4))
{
out_html(line);
out_html(" \n");
delete [] line; // ### FIXME: memory leak!
i--;
}
}
out_html("
\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("
\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 QByteArray 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 */
// fallthrough
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 && qstrncmp(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("
\n");
}
curpos = 0;
fillout = 0;
c = skip_till_newline(c);
break;
}
case REQ_ps: // groff(7) "previous Point Size"
{
c += j;
getArguments(c, args);
if ( args.count() == 0 )
out_html(change_to_size('0'));
else
{
char *h = args[0].data();
j = 0;
i = 0;
if (*h == '-')
{
j = -1;
h++;
}
else if (*h == '+')
j = 1;
h++;
scan_expression(h, &i);
if (!j)
{
j = 1;
if (i > 5) i = i - 10;
}
out_html(change_to_size(i*j));
}
break;
}
case REQ_sp: // groff(7) "SKip one line"
{
c += j;
if (fillout)
out_html("
");
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)
{
kDebug(7107) << "Unable to open or read file: .so " << (h);
out_html("
"
"man2html: unable to open or read file.\n");
out_html(h);
out_html("
\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("\n");
if (dl_set[itemdepth]) dl_set[itemdepth]=0;
else itemdepth--;
}*/
out_html(" \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 += j;
getArguments(c, args);
if ( args.count() )
kDebug(7107) << ".tm " << args[0];
break;
}
case REQ_B: // man(7) "Bold"
mode = 1;
case REQ_I: // man(7) "Italic"
{
/* parse one line in a certain font */
c += j;
getArguments(c, args);
out_html(set_font(mode ? "B" : "I"));
for (int i = 0; i < args.count(); i++)
{
scan_troff(args[i].data(), 1, 0);
out_html(" ");
}
out_html(set_font("R"));
if (fillout)
curpos++;
else
{
out_html(NEWLINE);
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++;
getArguments(c, args);
for (i = 0; i < args.count(); i++)
{
// ### FIXME In theory, only a single punctuation character is recognized as punctuation
if ( is_mdoc_punctuation(args[i][0]) )
out_html(set_font("R"));
else
out_html(set_font("B"));
scan_troff(args[i].data(), 1, NULL);
out_html(" ");
}
// In the mdoc synopsis, there are automatical line breaks (### TODO: before or after?)
if (mandoc_synopsis)
out_html(" ");
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++;
getArguments(c, args);
if ( args.count() )
{
for (i = 0; i < args.count(); i++)
{
if (i)
out_html(set_font("I"));
else
out_html(set_font("B"));
scan_troff(args[i].data(), 1, NULL);
out_html(set_font("R"));
if (i == 0)
{
out_html(" (");
}
else if (i < args.count() - 1)
out_html(", ");
}
out_html(")");
}
out_html(set_font("R"));
if (mandoc_synopsis)
out_html(" ");
out_html(NEWLINE);
if (!fillout)
curpos = 0;
else
curpos++;
break;
}
case REQ_Fo: // mdoc(7) "Function definition Opening"
{
char* font[2] = {(char*)"B", (char*)"R" };
c += j;
if (*c == '\n') c++;
char *eol = strchr(c, '\n');
char *semicolon = strchr(c, ';');
if ((semicolon != 0) && (semicolon < eol)) *semicolon = ' ';
getArguments(c, args);
// Normally a .Fo has only one parameter
for (i = 0; i < args.count(); i++)
{
out_html(set_font(font[i&1]));
scan_troff(args[i].data(), 1, NULL);
if (i == 0)
{
out_html(" (");
}
// ### TODO What should happen if there is more than one argument
// else if (i 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] = {(char*)"B", (char*)"R" };
out_html(set_font(font[i&1]));
out_html(")");
out_html(set_font("R"));
if (mandoc_synopsis)
out_html(" ");
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] = {(char*)"B", (char*)"R" };
c += j;
if (*c == '\n') c++;
getArguments(c, args);
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 < args.count(); i++)
scan_troff(args[i].data(), 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 : [ ab ] */
out_html(set_font("R"));
out_html("[");
curpos++;
request_mixed_fonts(c, j, "B", "I", true, false);
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"
{
c += j;
getArguments(c, args);
if (!dl_set[itemdepth])
{
out_html("
\n");
dl_set[itemdepth] = 1;
}
out_html("
");
if ( args.count() )
scan_troff(args[0].data(), 1, NULL);
out_html("
\n
");
listItemStack.push("DD");
curpos = 0;
break;
}
case REQ_TP: // man(7) "hanging Tag Paragraph"
{
if (!dl_set[itemdepth])
{
out_html("
");
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("
");
listItemStack.push("DD");
}
curpos = 0;
break;
}
case REQ_IX: // Indexing term (printed on standard error)
{
c = skip_till_newline(c); // ignore
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("
\n");
dl_set[itemdepth] = 0;
}
else
if (fillout) out_html(" ");
if (fillout)
out_html(" \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_set[itemdepth] = 1;
}
out_html("
\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"
{
c += j;
getArguments(c, args);
j = 1;
if (args.count() > 0) scan_expression(args[0].data(), &j);
if (j >= 0)
{
itemdepth++;
dl_set[itemdepth] = 0;
out_html("
");
listItemStack.push("DD");
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("
");
out_html("
\n");
itemdepth--;
}
c = skip_till_newline(c);
curpos = 0;
break;
}
case REQ_SB: // man(7) "Small; Bold"
{
out_html(set_font("B"));
out_html("");
c = scan_troff(c + j, 1, NULL);
out_html("");
out_html(set_font("R"));
break;
}
case REQ_SM: // man(7) "SMall"
{
c = c + j;
if (*c == '\n') c++;
out_html("");
c = scan_troff(c, 1, NULL);
out_html("");
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("
\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("
");
}
trans_char(c, '"', '\a');
if (section)
{
out_html("\n");
section = 0;
}
if (mode)
out_html("\n
\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: // Table Start tbl(1)
{
c = scan_table(c);
break;
}
case REQ_Dt: /* mdoc(7) */
mandoc_command = true;
case REQ_TH: // man(7) "Title Header"
{
if (!output_possible)
{
c += j;
getArguments(c, args);
output_possible = true;
out_html(DOCTYPE"\n\n");
out_html("\n");
out_html("");
if ( args.count() )
{
// work around the problem that in a title no HTML tags are allowed
// but args[0] can have formatting escapes, e.g. to switch a font
// which results in a HTML tag added to the output
char *result = 0;
scan_troff(args[0].data(), 0, &result);
char *p = result;
QByteArray title;
while ( *p )
{
if ( *p == '<' ) // tag begin -> skip whole tag
{
for (p++; *p && (*p != '>'); p++) ;
if ( *p ) p++;
}
if ( *p )
title += *p++;
}
ignore_links = true;
title += '\n'; // needed so that out_html flushes buffer and ignore_links works
out_html(title);
ignore_links = false;
delete [] result;
}
out_html(" Manpage\n");
// KDE defaults.
out_html("\n");
// Output our custom stylesheet.
out_html("\n");
// Some elements need background images, but this
// could not be included in the stylesheet,
// include it now.
out_html("\n\n"
);
out_html("\n");
out_html("\n\n");
out_html("\n\n");
out_html("
\n");
out_html("
\n");
out_html(" ");
if ( args.count() )
scan_troff(args[0].data(), 0, NULL);
out_html(" - KDE Man Page Viewer");
out_html("
\n");
out_html("
\n");
out_html("
");
if ( args.count() )
scan_troff(args[0].data(), 0, NULL);
out_html("
\n");
if (args.count() > 1)
{
out_html("Section: ");
if ( !mandoc_command && (args.count() > 4) )
scan_troff(args[4].data(), 0, NULL);
else
out_html(section_name(args[1].data()));
out_html(" (");
scan_troff(args[1].data(), 0, NULL);
out_html(")\n");
}
else
{
out_html("Section not specified");
}
}
else
{
kWarning(7107) << ".TH found but output not possible" ;
c = skip_till_newline(c);
}
curpos = 0;
break;
}
case REQ_TX: // mdoc(7)
{
c += j;
getArguments(c, args);
out_html(set_font("I"));
const char *c2 = lookup_abbrev(args[0]);
curpos += qstrlen(c2);
out_html(c2);
out_html(set_font("R"));
if (args.count() > 1)
out_html(args[1]);
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 */
{
kDebug(7107) << "start .rm/.rn";
c += j;
const QByteArray name(scan_identifier(c));
if (name.isEmpty())
{
kDebug(7107) << "EXCEPTION: empty origin string to remove/rename";
break;
}
QByteArray name2;
if (!mode)
{
while (*c && isspace(*c) && *c != '\n') ++c;
name2 = scan_identifier(c);
if (name2.isEmpty())
{
kDebug(7107) << "EXCEPTION: empty destination string to rename";
break;
}
}
c = skip_till_newline(c);
QMap::iterator it = s_stringDefinitionMap.find(name);
if (it == s_stringDefinitionMap.end())
{
kDebug(7107) << "EXCEPTION: cannot find string to rename or remove: " << BYTEARRAY(name);
}
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);
}
}
kDebug(7107) << "end .rm/.rn";
break;
}
case REQ_nx:
case REQ_in: // groff(7) "INdent"
{
/* .in +-N : Indent */
c = skip_till_newline(c);
break;
}
case REQ_nr: // groff(7) "Number Register"
{
kDebug(7107) << "start .nr";
c += j;
const QByteArray name(scan_identifier(c));
if (name.isEmpty())
{
kDebug(7107) << "EXCEPTION: empty name for register variable";
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);
QMap ::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;
}
kDebug(7107) << "end .nr";
break;
}
case REQ_am: // groff(7) "Append Macro"
/* .am xx yy : append to a macro. */
/* define or handle as .ig yy */
mode = true;
// fallthrough
case REQ_de: // groff(7) "DEfine macro"
case REQ_de1: // groff(7) "DEfine macro"
{
/* .de xx yy : define or redefine macro xx; end at .yy (..) */
/* define or handle as .ig yy */
kDebug(7107) << "Start .am/.de";
c += j;
getArguments(c, args);
if ( args.count() == 0 )
break;
const QByteArray name(args[0]);
QByteArray endmacro;
if (args.count() == 1)
endmacro = "..";
else
endmacro = "." + args[1]; // krazy:exclude=doublequote_chars
sl = c;
while (*c && qstrncmp(c, endmacro, endmacro.length()))
c = skip_till_newline(c);
QByteArray macro;
while (sl != c)
{
if (sl[0] == '\\' && sl[1] == '\\')
{
macro += '\\';
sl++;
}
else
macro += *sl;
sl++;
}
QMap::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.endsWith('\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);
kDebug(7107) << "End .am/.de";
break;
}
case REQ_Bl: // mdoc(7) "Begin List"
{
- char list_options[NULL_TERMINATED(MED_STR_MAX)];
+ QByteArray list_options;
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);
+ list_options = QByteArray(c, nl - c);
}
- if (strstr(list_options, "-bullet"))
+ if ( list_options.contains("-bullet") )
{
/* HTML Unnumbered List */
dl_set[itemdepth] = BL_BULLET_LIST;
out_html("
\n");
}
- else if (strstr(list_options, "-enum"))
+ else if ( list_options.contains("-enum") )
{
/* HTML Ordered List */
dl_set[itemdepth] = BL_ENUM_LIST;
out_html("\n");
}
else
{
/* HTML Descriptive List */
dl_set[itemdepth] = BL_DESC_LIST;
out_html("
\n");
}
curpos = 0;
c = skip_till_newline(c);
break;
}
case REQ_El: // mdoc(7) "End List"
{
checkListStack();
c = c + j;
if (dl_set[itemdepth] & BL_DESC_LIST)
out_html("
\n");
else if (dl_set[itemdepth] & BL_BULLET_LIST)
out_html("
\n");
else if (dl_set[itemdepth] & BL_ENUM_LIST)
out_html("\n");
dl_set[itemdepth] = 0;
if (itemdepth > 0) itemdepth--;
if ( !fillout )
out_html(NEWLINE);
curpos = 0;
c = skip_till_newline(c);
break;
}
case REQ_It: // mdoc(7) "list ITem"
{
checkListStack();
c = c + j;
//if (qstrncmp(c, "Xo", 2) == 0 && isspace(*(c + 2)))
//c = skip_till_newline(c);
if (dl_set[itemdepth] & BL_DESC_LIST)
{
out_html("
");
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("
\n");
section = 0;
}
if (output_possible)
{
// The output is buggy wrt to how divs are handled. Fixing it would
// require closing divs before other block-level elements are output,
// and I do not feel like going to find them all.
output_real("
\n");
output_real("\n\n");
output_real("\n\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)
{
std::cout << insert;
}
+//---------------------------------------------------------------------
+
char *read_man_page(const char *filename)
{
- char *man_buf = NULL;
+ QFile f(QFile::decodeName(filename));
- FILE *man_stream = NULL;
- struct stat stbuf;
- size_t buf_size;
- if (stat(filename, &stbuf) == -1)
- {
- std::cerr << "read_man_page: can not find " << filename << std::endl;
- return NULL;
- }
- if (!S_ISREG(stbuf.st_mode))
+ if ( !f.open(QIODevice::ReadOnly) )
{
- std::cerr << "read_man_page: no file " << filename << std::endl;
- return NULL;
+ std::cerr << "read_man_page: can not open " << filename << std::endl;
+ return 0;
}
- buf_size = stbuf.st_size;
- man_buf = new char[buf_size + 5];
- 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
- {
- delete [] man_buf;
- man_buf = NULL;
- }
- fclose(man_stream);
- }
- return man_buf;
+
+ QByteArray array = f.readAll();
+
+ // as we do not know in which encoding the man source is, try to automatically
+ // detect it and always return it as UTF-8
+ KEncodingProber encodingProber;
+ encodingProber.feed(array);
+ kDebug(7107) << "auto-detect encoding for" << filename << "guess=" << encodingProber.encoding()
+ << "confidence=" << encodingProber.confidence();
+ QString out = QTextCodec::codecForName(encodingProber.encoding())->toUnicode(array);
+ array = out.toUtf8();
+
+ const int len = array.size();
+ char *buf = new char[len + 4];
+ memmove(buf + 1, array.data(), len);
+ buf[0] = buf[len+1] = '\n'; // Start and end with an end of line
+ buf[len+2] = buf[len+3] = '\0'; // Two NUL characters at end
+
+ return buf;
}
+//---------------------------------------------------------------------
+
#ifndef KIO_MAN_TEST
int main(int argc, char **argv)
{
cssPath = "."; // krazy:exclude=doublequote_chars
if (argc < 2)
{
std::cerr << "call: " << argv[0] << " \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)
{
std::cerr << "converting " << ent->d_name << std::endl;
char *buf = read_man_page(ent->d_name);
if (buf)
{
scan_man_page(buf);
delete [] buf;
}
}
closedir(dir);
}
return 0;
}
#endif
#endif
// kate: indent-mode cstyle; space-indent on; indent-width 2; replace-tabs on;