FreeRDP
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 
9 static 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 
27 static 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 
42 static 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 
140 int 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;
254 fail:
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 }