2013-08-05 18:39:49 +04:00
|
|
|
#include <stdlib.h>
|
2023-12-20 22:35:05 +03:00
|
|
|
#include <stdbool.h>
|
2013-08-05 18:39:49 +04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <ctype.h>
|
2018-02-16 15:42:10 +03:00
|
|
|
#include <string.h>
|
2013-08-05 18:39:49 +04:00
|
|
|
|
2023-12-14 16:13:53 +03:00
|
|
|
#include "../cmdline.h"
|
2013-08-05 18:39:49 +04:00
|
|
|
|
2023-12-20 22:35:05 +03:00
|
|
|
static char* resize(char** buffer, size_t* size, size_t increment)
|
2013-08-05 18:39:49 +04:00
|
|
|
{
|
2023-12-20 22:35:05 +03:00
|
|
|
const size_t nsize = *size + increment;
|
|
|
|
char* tmp = realloc(*buffer, nsize);
|
|
|
|
if (!tmp)
|
2019-10-04 15:49:30 +03:00
|
|
|
{
|
2024-08-26 16:39:33 +03:00
|
|
|
(void)fprintf(stderr,
|
|
|
|
"Could not reallocate string buffer from %" PRIuz " to %" PRIuz " bytes.\n",
|
|
|
|
*size, nsize);
|
2023-12-20 22:35:05 +03:00
|
|
|
free(*buffer);
|
2024-04-12 10:01:12 +03:00
|
|
|
return NULL;
|
2019-10-04 15:49:30 +03:00
|
|
|
}
|
2023-12-20 22:35:05 +03:00
|
|
|
memset(&tmp[*size], '\0', increment);
|
|
|
|
*size = nsize;
|
|
|
|
*buffer = tmp;
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char* append(char** buffer, size_t* size, const char* str)
|
|
|
|
{
|
|
|
|
const size_t len = strnlen(*buffer, *size);
|
|
|
|
const size_t add = strlen(str);
|
|
|
|
const size_t required = len + add + 1;
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2023-12-20 22:35:05 +03:00
|
|
|
if (required > *size)
|
2013-08-05 18:39:49 +04:00
|
|
|
{
|
2023-12-20 22:35:05 +03:00
|
|
|
if (!resize(buffer, size, required - *size))
|
|
|
|
return NULL;
|
2013-08-05 18:39:49 +04:00
|
|
|
}
|
2024-09-19 00:53:44 +03:00
|
|
|
strncpy(&(*buffer)[len], str, add);
|
2023-12-20 22:35:05 +03:00
|
|
|
return *buffer;
|
|
|
|
}
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2023-12-20 22:35:05 +03:00
|
|
|
static LPSTR tr_esc_str(LPCSTR arg, bool format)
|
|
|
|
{
|
2024-01-23 18:49:54 +03:00
|
|
|
const char* str = NULL;
|
2023-12-20 22:35:05 +03:00
|
|
|
LPSTR tmp = NULL;
|
|
|
|
size_t ds = 0;
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2023-12-20 22:35:05 +03:00
|
|
|
if (NULL == arg)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
const size_t s = strlen(arg) + 1;
|
|
|
|
if (!resize(&tmp, &ds, s))
|
|
|
|
exit(-2);
|
|
|
|
|
|
|
|
for (size_t x = 0; x < s; x++)
|
2013-08-05 18:39:49 +04:00
|
|
|
{
|
2023-12-20 22:35:05 +03:00
|
|
|
char data[2] = { 0 };
|
2018-08-22 14:13:37 +03:00
|
|
|
switch (arg[x])
|
2013-08-05 18:39:49 +04:00
|
|
|
{
|
2024-10-09 18:08:00 +03:00
|
|
|
case '-':
|
|
|
|
str = "\\-";
|
|
|
|
if (!append(&tmp, &ds, str))
|
|
|
|
exit(-3);
|
|
|
|
break;
|
|
|
|
|
2013-08-05 18:39:49 +04:00
|
|
|
case '<':
|
2017-09-25 12:37:43 +03:00
|
|
|
if (format)
|
2024-10-09 18:08:00 +03:00
|
|
|
str = "\\fI";
|
2018-08-22 14:13:37 +03:00
|
|
|
else
|
2024-10-09 18:08:00 +03:00
|
|
|
str = "<";
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2023-12-20 22:35:05 +03:00
|
|
|
if (!append(&tmp, &ds, str))
|
|
|
|
exit(-3);
|
2013-08-05 18:39:49 +04:00
|
|
|
break;
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2013-08-05 18:39:49 +04:00
|
|
|
case '>':
|
2017-09-25 12:37:43 +03:00
|
|
|
if (format)
|
2024-10-09 18:08:00 +03:00
|
|
|
str = "\\fR";
|
2018-08-22 14:13:37 +03:00
|
|
|
else
|
2024-10-09 18:08:00 +03:00
|
|
|
str = ">";
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2023-12-20 22:35:05 +03:00
|
|
|
if (!append(&tmp, &ds, str))
|
|
|
|
exit(-4);
|
2013-08-05 18:39:49 +04:00
|
|
|
break;
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2013-08-05 18:39:49 +04:00
|
|
|
case '\'':
|
2024-10-09 18:08:00 +03:00
|
|
|
str = "\\*(Aq";
|
|
|
|
if (!append(&tmp, &ds, str))
|
|
|
|
exit(-4);
|
2013-08-05 18:39:49 +04:00
|
|
|
break;
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2024-10-09 18:08:00 +03:00
|
|
|
case '.':
|
|
|
|
if (!append(&tmp, &ds, "\\&."))
|
2023-12-20 22:35:05 +03:00
|
|
|
exit(-6);
|
|
|
|
break;
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2023-12-20 22:35:05 +03:00
|
|
|
case '\r':
|
|
|
|
case '\n':
|
2024-10-09 18:08:00 +03:00
|
|
|
if (!append(&tmp, &ds, "\n.br\n"))
|
2023-12-20 22:35:05 +03:00
|
|
|
exit(-7);
|
2013-08-05 18:39:49 +04:00
|
|
|
break;
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2013-08-05 18:39:49 +04:00
|
|
|
default:
|
2023-12-20 22:35:05 +03:00
|
|
|
data[0] = arg[x];
|
|
|
|
if (!append(&tmp, &ds, data))
|
|
|
|
exit(-8);
|
2013-08-05 18:39:49 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2013-08-05 18:39:49 +04:00
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
2018-08-22 14:13:37 +03:00
|
|
|
int main(int argc, char* argv[])
|
2013-08-05 18:39:49 +04:00
|
|
|
{
|
2021-10-06 10:47:30 +03:00
|
|
|
size_t elements = sizeof(global_cmd_args) / sizeof(global_cmd_args[0]);
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2024-10-09 18:08:00 +03:00
|
|
|
if (argc != 2)
|
|
|
|
{
|
|
|
|
(void)fprintf(stderr, "Usage: %s <output file name>\n", argv[0]);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* fname = argv[1];
|
|
|
|
|
|
|
|
(void)fprintf(stdout, "Generating manpage file '%s'\n", fname);
|
2023-12-20 22:35:05 +03:00
|
|
|
FILE* fp = fopen(fname, "w");
|
2018-08-22 14:13:37 +03:00
|
|
|
if (NULL == fp)
|
2013-08-05 18:39:49 +04:00
|
|
|
{
|
2024-08-26 16:39:33 +03:00
|
|
|
(void)fprintf(stderr, "Could not open '%s' for writing.\n", fname);
|
2018-02-16 15:42:10 +03:00
|
|
|
return -1;
|
2013-08-05 18:39:49 +04:00
|
|
|
}
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2013-08-05 18:39:49 +04:00
|
|
|
/* The tag used as header in the manpage */
|
2024-10-09 18:08:00 +03:00
|
|
|
(void)fprintf(fp, ".SH \"OPTIONS\"\n");
|
2018-08-22 14:13:37 +03:00
|
|
|
|
|
|
|
if (elements < 2)
|
2013-08-06 12:23:43 +04:00
|
|
|
{
|
2024-08-26 16:39:33 +03:00
|
|
|
(void)fprintf(stderr, "The argument array 'args' is empty, writing an empty file.\n");
|
2013-08-06 12:23:43 +04:00
|
|
|
elements = 1;
|
|
|
|
}
|
2017-09-25 12:33:03 +03:00
|
|
|
|
2023-12-20 22:35:05 +03:00
|
|
|
for (size_t x = 0; x < elements - 1; x++)
|
2018-08-22 14:13:37 +03:00
|
|
|
{
|
2021-10-06 10:47:30 +03:00
|
|
|
const COMMAND_LINE_ARGUMENT_A* arg = &global_cmd_args[x];
|
2023-12-20 22:35:05 +03:00
|
|
|
char* name = tr_esc_str(arg->Name, FALSE);
|
|
|
|
char* alias = tr_esc_str(arg->Alias, FALSE);
|
2018-08-22 14:13:37 +03:00
|
|
|
char* format = tr_esc_str(arg->Format, TRUE);
|
2023-12-20 22:35:05 +03:00
|
|
|
char* text = tr_esc_str(arg->Text, FALSE);
|
2016-06-15 23:39:03 +03:00
|
|
|
|
2024-10-09 18:08:00 +03:00
|
|
|
(void)fprintf(fp, ".PP\n");
|
|
|
|
bool first = true;
|
2017-09-25 13:41:40 +03:00
|
|
|
do
|
2017-09-25 12:37:43 +03:00
|
|
|
{
|
2024-10-09 18:08:00 +03:00
|
|
|
(void)fprintf(fp, "%s\\fB", first ? "" : ", ");
|
|
|
|
first = false;
|
2017-09-25 13:41:40 +03:00
|
|
|
if (arg->Flags == COMMAND_LINE_VALUE_BOOL)
|
2024-10-09 18:08:00 +03:00
|
|
|
(void)fprintf(fp, "%s", arg->Default ? "\\-" : "+");
|
2017-09-25 13:41:40 +03:00
|
|
|
else
|
2024-08-26 16:39:33 +03:00
|
|
|
(void)fprintf(fp, "/");
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2024-10-09 18:08:00 +03:00
|
|
|
(void)fprintf(fp, "%s\\fR", name);
|
2017-09-25 12:37:43 +03:00
|
|
|
|
2017-09-25 13:41:40 +03:00
|
|
|
if (format)
|
|
|
|
{
|
|
|
|
if (arg->Flags == COMMAND_LINE_VALUE_OPTIONAL)
|
2024-08-26 16:39:33 +03:00
|
|
|
(void)fprintf(fp, "[");
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2024-08-26 16:39:33 +03:00
|
|
|
(void)fprintf(fp, ":%s", format);
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2017-09-25 13:41:40 +03:00
|
|
|
if (arg->Flags == COMMAND_LINE_VALUE_OPTIONAL)
|
2024-08-26 16:39:33 +03:00
|
|
|
(void)fprintf(fp, "]");
|
2017-09-25 13:41:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (alias == name)
|
|
|
|
break;
|
|
|
|
|
2018-08-22 14:13:37 +03:00
|
|
|
free(name);
|
2017-09-25 13:41:40 +03:00
|
|
|
name = alias;
|
2019-11-06 17:24:51 +03:00
|
|
|
} while (alias);
|
2024-10-09 18:08:00 +03:00
|
|
|
(void)fprintf(fp, "\n");
|
2016-06-15 23:39:03 +03:00
|
|
|
|
2017-09-25 12:37:43 +03:00
|
|
|
if (text)
|
2016-06-15 23:39:03 +03:00
|
|
|
{
|
2024-10-09 18:08:00 +03:00
|
|
|
(void)fprintf(fp, ".RS 4\n");
|
|
|
|
const int hasText = text && (strnlen(text, 2) > 0);
|
|
|
|
if (hasText)
|
2024-08-26 16:39:33 +03:00
|
|
|
(void)fprintf(fp, "%s", text);
|
2017-09-25 12:37:43 +03:00
|
|
|
|
2020-03-19 00:00:06 +03:00
|
|
|
if (arg->Flags & COMMAND_LINE_VALUE_BOOL &&
|
|
|
|
(!arg->Default || arg->Default == BoolValueTrue))
|
2024-10-09 18:08:00 +03:00
|
|
|
(void)fprintf(fp, " (default:%s)\n", arg->Default ? "on" : "off");
|
2017-09-25 12:37:43 +03:00
|
|
|
else if (arg->Default)
|
|
|
|
{
|
2023-12-20 22:35:05 +03:00
|
|
|
char* value = tr_esc_str(arg->Default, FALSE);
|
2024-10-09 18:08:00 +03:00
|
|
|
(void)fprintf(fp, " (default:%s)\n", value);
|
2018-08-22 14:13:37 +03:00
|
|
|
free(value);
|
2016-06-15 23:39:03 +03:00
|
|
|
}
|
2024-10-09 18:08:00 +03:00
|
|
|
else if (hasText)
|
|
|
|
(void)fprintf(fp, "\n");
|
2016-06-15 23:39:03 +03:00
|
|
|
}
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2024-10-09 18:08:00 +03:00
|
|
|
(void)fprintf(fp, ".RE\n");
|
|
|
|
|
2017-09-25 12:33:03 +03:00
|
|
|
free(name);
|
|
|
|
free(format);
|
|
|
|
free(text);
|
2013-08-05 18:39:49 +04:00
|
|
|
}
|
2018-08-22 14:13:37 +03:00
|
|
|
|
2024-08-26 16:39:33 +03:00
|
|
|
(void)fclose(fp);
|
2023-12-20 22:35:05 +03:00
|
|
|
|
2024-08-26 16:39:33 +03:00
|
|
|
(void)fprintf(stdout, "successfully generated '%s'\n", fname);
|
2018-02-16 15:42:10 +03:00
|
|
|
return 0;
|
2013-08-05 18:39:49 +04:00
|
|
|
}
|