FreeRDP
Loading...
Searching...
No Matches
generate_argument_manpage.c
1#include <stdlib.h>
2#include <stdbool.h>
3#include <stdio.h>
4#include <ctype.h>
5#include <string.h>
6
7#include "../cmdline.h"
8
9static char* resize(char** buffer, size_t* size, size_t increment)
10{
11 const size_t nsize = *size + increment;
12 char* tmp = realloc(*buffer, nsize);
13 if (!tmp)
14 {
15 (void)fprintf(stderr,
16 "Could not reallocate string buffer from %" PRIuz " to %" PRIuz " bytes.\n",
17 *size, nsize);
18 free(*buffer);
19 return NULL;
20 }
21 memset(&tmp[*size], '\0', increment);
22 *size = nsize;
23 *buffer = tmp;
24 return tmp;
25}
26
27static char* append(char** buffer, size_t* size, const char* str)
28{
29 const size_t len = strnlen(*buffer, *size);
30 const size_t add = strlen(str);
31 const size_t required = len + add + 1;
32
33 if (required > *size)
34 {
35 if (!resize(buffer, size, required - *size))
36 return NULL;
37 }
38 strncpy(&(*buffer)[len], str, add);
39 return *buffer;
40}
41
42static LPSTR tr_esc_str(LPCSTR arg, bool format, int* failed)
43{
44 const char* str = NULL;
45 LPSTR tmp = NULL;
46 size_t ds = 0;
47
48 assert(failed);
49
50 if (NULL == arg)
51 return NULL;
52
53 const size_t s = strlen(arg) + 1;
54 if (!resize(&tmp, &ds, s))
55 {
56 *failed = -2;
57 return NULL;
58 }
59
60 for (size_t x = 0; x < s; x++)
61 {
62 char data[2] = { 0 };
63 switch (arg[x])
64 {
65 case '-':
66 str = "\\-";
67 if (!append(&tmp, &ds, str))
68 {
69 *failed = -3;
70 return NULL;
71 }
72 break;
73
74 case '<':
75 if (format)
76 str = "\\fI";
77 else
78 str = "<";
79
80 if (!append(&tmp, &ds, str))
81 {
82 *failed = -4;
83 return NULL;
84 }
85 break;
86
87 case '>':
88 if (format)
89 str = "\\fR";
90 else
91 str = ">";
92
93 if (!append(&tmp, &ds, str))
94 {
95 *failed = -5;
96 return NULL;
97 }
98 break;
99
100 case '\'':
101 str = "\\*(Aq";
102 if (!append(&tmp, &ds, str))
103 {
104 *failed = -6;
105 return NULL;
106 }
107 break;
108
109 case '.':
110 if (!append(&tmp, &ds, "\\&."))
111 {
112 *failed = -7;
113 return NULL;
114 }
115 break;
116
117 case '\r':
118 case '\n':
119 if (!append(&tmp, &ds, "\n.br\n"))
120 {
121 *failed = -8;
122 return NULL;
123 }
124 break;
125
126 default:
127 data[0] = arg[x];
128 if (!append(&tmp, &ds, data))
129 {
130 *failed = -9;
131 return NULL;
132 }
133 break;
134 }
135 }
136
137 return tmp;
138}
139
140int main(int argc, char* argv[])
141{
142 int rc = -3;
143 size_t elements = sizeof(global_cmd_args) / sizeof(global_cmd_args[0]);
144
145 if (argc != 2)
146 {
147 (void)fprintf(stderr, "Usage: %s <output file name>\n", argv[0]);
148 return -1;
149 }
150
151 const char* fname = argv[1];
152
153 (void)fprintf(stdout, "Generating manpage file '%s'\n", fname);
154 FILE* fp = fopen(fname, "w");
155 if (NULL == fp)
156 {
157 (void)fprintf(stderr, "Could not open '%s' for writing.\n", fname);
158 return -1;
159 }
160
161 /* The tag used as header in the manpage */
162 (void)fprintf(fp, ".SH \"OPTIONS\"\n");
163
164 if (elements < 2)
165 {
166 (void)fprintf(stderr, "The argument array 'args' is empty, writing an empty file.\n");
167 elements = 1;
168 }
169
170 for (size_t x = 0; x < elements - 1; x++)
171 {
172 int failed = 0;
173 const COMMAND_LINE_ARGUMENT_A* arg = &global_cmd_args[x];
174 char* name = tr_esc_str(arg->Name, FALSE, &failed);
175 char* alias = tr_esc_str(arg->Alias, FALSE, &failed);
176 char* format = tr_esc_str(arg->Format, TRUE, &failed);
177 char* text = tr_esc_str(arg->Text, FALSE, &failed);
178
179 if (failed != 0)
180 {
181 free(name);
182 free(alias);
183 free(format);
184 free(text);
185 rc = failed;
186 goto fail;
187 }
188
189 (void)fprintf(fp, ".PP\n");
190 bool first = true;
191 do
192 {
193 (void)fprintf(fp, "%s\\fB", first ? "" : ", ");
194 first = false;
195 if (arg->Flags == COMMAND_LINE_VALUE_BOOL)
196 (void)fprintf(fp, "%s", arg->Default ? "\\-" : "+");
197 else
198 (void)fprintf(fp, "/");
199
200 (void)fprintf(fp, "%s\\fR", name);
201
202 if (format)
203 {
204 if (arg->Flags == COMMAND_LINE_VALUE_OPTIONAL)
205 (void)fprintf(fp, "[");
206
207 (void)fprintf(fp, ":%s", format);
208
209 if (arg->Flags == COMMAND_LINE_VALUE_OPTIONAL)
210 (void)fprintf(fp, "]");
211 }
212
213 if (alias == name)
214 break;
215
216 free(name);
217 name = alias;
218 } while (alias);
219 (void)fprintf(fp, "\n");
220
221 if (text)
222 {
223 (void)fprintf(fp, ".RS 4\n");
224 const int hasText = text && (strnlen(text, 2) > 0);
225 if (hasText)
226 (void)fprintf(fp, "%s", text);
227
228 if (arg->Flags & COMMAND_LINE_VALUE_BOOL &&
229 (!arg->Default || arg->Default == BoolValueTrue))
230 (void)fprintf(fp, " (default:%s)\n", arg->Default ? "on" : "off");
231 else if (arg->Default)
232 {
233 char* value = tr_esc_str(arg->Default, FALSE, &failed);
234 if (failed != 0)
235 {
236 rc = failed;
237 goto fail;
238 }
239 (void)fprintf(fp, " (default:%s)\n", value);
240 free(value);
241 }
242 else if (hasText)
243 (void)fprintf(fp, "\n");
244 }
245
246 (void)fprintf(fp, ".RE\n");
247
248 free(name);
249 free(format);
250 free(text);
251 }
252
253 rc = 0;
254fail:
255 (void)fclose(fp);
256
257 if (rc == 0)
258 (void)fprintf(stdout, "successfully generated '%s'\n", fname);
259 else
260 (void)fprintf(stdout, "failed to generate '%s'\n", fname);
261 return rc;
262}