2021-09-08 18:16:06 -04:00
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Render Help
|
|
|
|
***********************************************************************************************************************************/
|
|
|
|
#include "build.auto.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include <libxml/parser.h>
|
|
|
|
#include <libxml/tree.h>
|
|
|
|
|
|
|
|
#include "common/compress/bz2/compress.h"
|
|
|
|
#include "common/io/bufferRead.h"
|
|
|
|
#include "common/io/bufferWrite.h"
|
|
|
|
#include "common/log.h"
|
|
|
|
#include "common/type/pack.h"
|
|
|
|
|
|
|
|
#include "build/common/render.h"
|
|
|
|
#include "build/config/parse.h"
|
|
|
|
#include "build/help/parse.h"
|
|
|
|
|
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Render xml as text
|
|
|
|
***********************************************************************************************************************************/
|
|
|
|
static String *
|
|
|
|
bldHlpRenderReplace(const String *const string, const char *const replace, const char *const with)
|
|
|
|
{
|
|
|
|
String *result = strNew();
|
|
|
|
|
|
|
|
StringList *const stringList = strLstNewSplitZ(string, replace);
|
|
|
|
|
|
|
|
for (unsigned int stringIdx = 0; stringIdx < strLstSize(stringList); stringIdx++)
|
|
|
|
{
|
|
|
|
if (stringIdx != 0)
|
|
|
|
strCatZ(result, with);
|
|
|
|
|
|
|
|
strCat(result, strLstGet(stringList, stringIdx));
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static String *
|
|
|
|
bldHlpRenderXmlNode(const xmlNodePtr xml)
|
|
|
|
{
|
|
|
|
String *const result = strNew();
|
|
|
|
|
|
|
|
for (xmlNodePtr currentNode = xml->children; currentNode != NULL; currentNode = currentNode->next)
|
|
|
|
{
|
|
|
|
const String *const name = STR((char *)currentNode->name);
|
|
|
|
|
|
|
|
if (currentNode->type == XML_ELEMENT_NODE)
|
|
|
|
{
|
|
|
|
if (strEq(name, STRDEF("admonition")))
|
|
|
|
{
|
|
|
|
strCatZ(result, "NOTE: ");
|
|
|
|
strCat(result, bldHlpRenderXmlNode(currentNode));
|
|
|
|
strCatZ(result, "\n\n");
|
|
|
|
}
|
|
|
|
else if (strEq(name, STRDEF("backrest")))
|
|
|
|
strCatZ(result, "pgBackRest");
|
|
|
|
else if (strEq(name, STRDEF("list")))
|
|
|
|
{
|
|
|
|
strCat(result, bldHlpRenderXmlNode(currentNode));
|
|
|
|
strCatChr(result, '\n');
|
|
|
|
}
|
|
|
|
else if (strEq(name, STRDEF("list-item")))
|
|
|
|
{
|
|
|
|
strCatZ(result, "* ");
|
|
|
|
strCat(result, bldHlpRenderXmlNode(currentNode));
|
|
|
|
strCatChr(result, '\n');
|
|
|
|
}
|
|
|
|
else if (strEq(name, STRDEF("p")))
|
|
|
|
{
|
|
|
|
strCat(result, bldHlpRenderXmlNode(currentNode));
|
|
|
|
strCatZ(result, "\n\n");
|
|
|
|
}
|
|
|
|
else if (strEq(name, STRDEF("postgres")))
|
|
|
|
strCatZ(result, "PostgreSQL");
|
|
|
|
else if (
|
|
|
|
strEq(name, STRDEF("id")) || strEq(name, STRDEF("br-option")) || strEq(name, STRDEF("cmd")) ||
|
|
|
|
strEq(name, STRDEF("link")) || strEq(name, STRDEF("setting")) || strEq(name, STRDEF("pg-setting")) ||
|
|
|
|
strEq(name, STRDEF("code")) || strEq(name, STRDEF("i")) || strEq(name, STRDEF("file")) ||
|
|
|
|
strEq(name, STRDEF("path")) || strEq(name, STRDEF("b")) || strEq(name, STRDEF("host")) ||
|
|
|
|
strEq(name, STRDEF("exe")) || strEq(name, STRDEF("proper")))
|
|
|
|
{
|
|
|
|
strCat(result, bldHlpRenderXmlNode(currentNode));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
THROW_FMT(FormatError, "unknown tag '%s'", strZ(name));
|
|
|
|
}
|
2021-09-11 16:07:59 -04:00
|
|
|
else if (currentNode->type == XML_TEXT_NODE)
|
2021-09-08 18:16:06 -04:00
|
|
|
{
|
|
|
|
xmlChar *content = xmlNodeGetContent(currentNode);
|
|
|
|
String *text = strNewZ((char *)content);
|
|
|
|
xmlFree(content);
|
|
|
|
|
|
|
|
if (strchr(strZ(text), '\n') != NULL)
|
|
|
|
{
|
|
|
|
if (!strEmpty(strTrim(strDup(text))))
|
|
|
|
THROW_FMT(FormatError, "text '%s' is invalid", strZ(text));
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
text = bldHlpRenderReplace(text, "{[dash]}", "-");
|
|
|
|
|
|
|
|
strCat(result, text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static String *
|
|
|
|
bldHlpRenderXml(const XmlNode *const xml)
|
|
|
|
{
|
|
|
|
return strTrim(bldHlpRenderXmlNode(*(const xmlNodePtr *)xml));
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Render help to a pack
|
|
|
|
***********************************************************************************************************************************/
|
|
|
|
static PackWrite *
|
|
|
|
bldHlpRenderHelpAutoCPack(const BldCfg bldCfg, const BldHlp bldHlp)
|
|
|
|
{
|
|
|
|
PackWrite *const pack = pckWriteNewBuf(bufNew(65 * 1024));
|
|
|
|
|
|
|
|
// Command help
|
|
|
|
// -----------------------------------------------------------------------------------------------------------------------------
|
|
|
|
pckWriteArrayBeginP(pack);
|
|
|
|
|
|
|
|
for (unsigned int cmdIdx = 0; cmdIdx < lstSize(bldCfg.cmdList); cmdIdx++)
|
|
|
|
{
|
|
|
|
const BldCfgCommand *const cmd = lstGet(bldCfg.cmdList, cmdIdx);
|
|
|
|
const BldHlpCommand *const cmdHlp = lstFind(bldHlp.cmdList, &cmd->name);
|
|
|
|
CHECK(cmdHlp != NULL);
|
|
|
|
|
|
|
|
pckWriteBoolP(pack, cmd->internal);
|
|
|
|
pckWriteStrP(pack, bldHlpRenderXml(cmdHlp->summary));
|
|
|
|
pckWriteStrP(pack, bldHlpRenderXml(cmdHlp->description));
|
|
|
|
}
|
|
|
|
|
|
|
|
pckWriteArrayEndP(pack);
|
|
|
|
|
|
|
|
// Option help
|
|
|
|
// -----------------------------------------------------------------------------------------------------------------------------
|
|
|
|
pckWriteArrayBeginP(pack);
|
|
|
|
|
|
|
|
for (unsigned int optIdx = 0; optIdx < lstSize(bldCfg.optList); optIdx++)
|
|
|
|
{
|
|
|
|
const BldCfgOption *const opt = lstGet(bldCfg.optList, optIdx);
|
|
|
|
const BldHlpOption *const optHlp = lstFind(bldHlp.optList, &opt->name);
|
|
|
|
|
|
|
|
// Internal
|
|
|
|
pckWriteBoolP(pack, opt->internal);
|
|
|
|
|
|
|
|
// Section
|
|
|
|
if (optHlp != NULL)
|
|
|
|
pckWriteStrP(pack, optHlp->section);
|
|
|
|
else
|
|
|
|
pckWriteNullP(pack);
|
|
|
|
|
|
|
|
// Summary
|
|
|
|
if (optHlp != NULL)
|
|
|
|
pckWriteStrP(pack, bldHlpRenderXml(optHlp->summary));
|
|
|
|
else
|
|
|
|
pckWriteNullP(pack);
|
|
|
|
|
|
|
|
// Description
|
|
|
|
if (optHlp != NULL)
|
|
|
|
pckWriteStrP(pack, bldHlpRenderXml(optHlp->description));
|
|
|
|
else
|
|
|
|
pckWriteNullP(pack);
|
|
|
|
|
|
|
|
// Deprecations
|
|
|
|
StringList *const deprecateList = strLstNew();
|
|
|
|
|
|
|
|
if (opt->deprecateList != NULL)
|
|
|
|
{
|
|
|
|
for (unsigned int deprecateIdx = 0; deprecateIdx < lstSize(opt->deprecateList); deprecateIdx++)
|
|
|
|
{
|
|
|
|
const BldCfgOptionDeprecate *const deprecate = lstGet(opt->deprecateList, deprecateIdx);
|
|
|
|
|
|
|
|
if (!strEq(deprecate->name, opt->name))
|
|
|
|
strLstAdd(deprecateList, deprecate->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strLstEmpty(deprecateList))
|
|
|
|
{
|
|
|
|
pckWriteArrayBeginP(pack);
|
|
|
|
|
|
|
|
for (unsigned int deprecateIdx = 0; deprecateIdx < strLstSize(deprecateList); deprecateIdx++)
|
|
|
|
pckWriteStrP(pack, strLstGet(deprecateList, deprecateIdx));
|
|
|
|
|
|
|
|
pckWriteArrayEndP(pack);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
pckWriteNullP(pack);
|
|
|
|
|
|
|
|
// Command overrides
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
for (unsigned int cmdIdx = 0; cmdIdx < lstSize(bldCfg.cmdList); cmdIdx++)
|
|
|
|
{
|
|
|
|
const BldCfgCommand *const cmd = lstGet(bldCfg.cmdList, cmdIdx);
|
|
|
|
const BldCfgCommand *const optCmd = lstFind(opt->cmdList, &cmd->name);
|
|
|
|
|
|
|
|
if (optCmd != NULL)
|
|
|
|
{
|
|
|
|
const BldHlpCommand *const cmdHlp = lstFind(bldHlp.cmdList, &cmd->name);
|
|
|
|
CHECK(cmdHlp != NULL);
|
|
|
|
const BldHlpOption *const cmdOptHlp = cmdHlp->optList != NULL ? lstFind(cmdHlp->optList, &opt->name) : NULL;
|
|
|
|
|
|
|
|
if (opt->internal != optCmd->internal || cmdOptHlp != NULL)
|
|
|
|
{
|
|
|
|
if (!found)
|
|
|
|
{
|
|
|
|
pckWriteArrayBeginP(pack);
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
pckWriteObjBeginP(pack, .id = cmdIdx + 1);
|
|
|
|
|
|
|
|
if (opt->internal != optCmd->internal)
|
|
|
|
pckWriteBoolP(pack, optCmd->internal);
|
|
|
|
else
|
|
|
|
pckWriteNullP(pack);
|
|
|
|
|
|
|
|
if (cmdOptHlp != NULL)
|
|
|
|
{
|
|
|
|
pckWriteStrP(pack, bldHlpRenderXml(cmdOptHlp->summary));
|
|
|
|
pckWriteStrP(pack, bldHlpRenderXml(cmdOptHlp->description));
|
|
|
|
}
|
|
|
|
|
|
|
|
pckWriteObjEndP(pack);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found)
|
|
|
|
pckWriteArrayEndP(pack);
|
|
|
|
else
|
|
|
|
pckWriteNullP(pack);
|
|
|
|
}
|
|
|
|
|
|
|
|
pckWriteArrayEndP(pack);
|
|
|
|
pckWriteEnd(pack);
|
|
|
|
|
|
|
|
return pack;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Compress pack to a buffer
|
|
|
|
***********************************************************************************************************************************/
|
|
|
|
static Buffer *
|
|
|
|
bldHlpRenderHelpAutoCCmp(const BldCfg bldCfg, const BldHlp bldHlp)
|
|
|
|
{
|
|
|
|
// Get pack buffer
|
|
|
|
const Buffer *const packBuf = pckWriteBuf(bldHlpRenderHelpAutoCPack(bldCfg, bldHlp));
|
|
|
|
Buffer *const result = bufNew(bufSize(packBuf));
|
|
|
|
|
|
|
|
// Open source/destination
|
|
|
|
IoRead *const source = ioBufferReadNew(packBuf);
|
|
|
|
IoWrite *const destination = ioBufferWriteNew(result);
|
|
|
|
|
|
|
|
ioFilterGroupAdd(ioWriteFilterGroup(destination), bz2CompressNew(9));
|
|
|
|
|
|
|
|
ioReadOpen(source);
|
|
|
|
ioWriteOpen(destination);
|
|
|
|
|
|
|
|
// Copy data from source to destination
|
|
|
|
Buffer *read = bufNew(bufUsed(packBuf) + 1);
|
|
|
|
|
|
|
|
ioRead(source, read);
|
|
|
|
ASSERT(ioReadEof(source));
|
|
|
|
|
|
|
|
ioWrite(destination, read);
|
|
|
|
ioWriteClose(destination);
|
|
|
|
|
|
|
|
// Return compressed buffer
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************************************************************************************
|
|
|
|
Output buffer to a file as a byte array
|
|
|
|
***********************************************************************************************************************************/
|
|
|
|
static void
|
|
|
|
bldHlpRenderHelpAutoC(const Storage *const storageRepo, const BldCfg bldCfg, const BldHlp bldHlp)
|
|
|
|
{
|
|
|
|
// Convert pack to bytes
|
|
|
|
String *const help = strNewFmt(
|
|
|
|
"%s"
|
|
|
|
"static const unsigned char helpData[] =\n"
|
|
|
|
"{\n",
|
|
|
|
strZ(bldHeader("help", "Help Data")));
|
|
|
|
|
|
|
|
const Buffer *const buffer = bldHlpRenderHelpAutoCCmp(bldCfg, bldHlp);
|
|
|
|
bool first = true;
|
|
|
|
size_t lineSize = 0;
|
|
|
|
char byteZ[4];
|
|
|
|
|
|
|
|
for (unsigned int bufferIdx = 0; bufferIdx < bufUsed(buffer); bufferIdx++)
|
|
|
|
{
|
|
|
|
snprintf(byteZ, sizeof(byteZ), "%u", bufPtrConst(buffer)[bufferIdx]);
|
|
|
|
|
|
|
|
if (strlen(byteZ) + 1 + (first ? 0 : 1) + lineSize > 128)
|
|
|
|
{
|
|
|
|
strCatChr(help, '\n');
|
|
|
|
first = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (first)
|
|
|
|
{
|
|
|
|
strCatFmt(help, " ");
|
|
|
|
lineSize = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
strCatFmt(help, "%s%s,", first ? "" : " ", byteZ);
|
|
|
|
|
|
|
|
lineSize += strlen(byteZ) + 1 + (first ? 0 : 1);
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
strCatZ(help, "\n};\n");
|
|
|
|
|
|
|
|
// Write to storage
|
|
|
|
bldPut(storageRepo, "command/help/help.auto.c", BUFSTR(help));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**********************************************************************************************************************************/
|
|
|
|
void
|
|
|
|
bldHlpRender(const Storage *const storageRepo, const BldCfg bldCfg, const BldHlp bldHlp)
|
|
|
|
{
|
|
|
|
bldHlpRenderHelpAutoC(storageRepo, bldCfg, bldHlp);
|
|
|
|
}
|