Commit 2a28ab58 2a28ab58762f9ae9cd00ca3e467ee6f90f241201 by Sergey Poznyakoff

Rewrite parseopt help routines using wordwrap streams

* include/mailutils/stream.h (MU_IOCTL_WORDWRAP_SET_NEXT_MARGIN)
(MU_IOCTL_WORDWRAP_GET_OFFSET): New opcodes.
* libmailutils/stream/wordwrap.c (_wordwrap_flush_line): Replace
whitespace-only lines with single newline on output.
(_wordwrap_flush): Fix condition.
(set_margin): Bugfix.
(_wordwrap_ctl): Handle new opcodes.

* include/mailutils/cli.h (mu_version_func): Change signature.
* include/mailutils/opt.h (mu_parseopt) <po_help_hook>
<po_version_hook>: Change signature.
(mu_parseopt_fmt_text): Remove.
(mu_option_describe_options, mu_program_help)
(mu_program_usage): Change signature.
(mu_program_version): New prototype.
* libmailutils/cli/cli.c (mu_version_func): Take mu_stream_t as
2nd argument.  Use mu_stream_printf for output.
(extra_help_hook): Likewise.
* libmailutils/opt/help.c: Rewrite using wordwrap streams.
* libmailutils/opt/opt.c (fn_help, fn_usage, fn_version): Update.
* libmailutils/tests/parseopt.c (version_hook): Write to mu_stream_t.

* libmailutils/tests/parseopt_help00.at: Fix expected output.
* libmailutils/tests/parseopt_help01.at: Likewise.
* libmailutils/tests/parseopt_help02.at: Likewise.
* libmailutils/tests/parseopt_help03.at: Likewise.
* libmailutils/tests/parseopt_help04.at: Likewise.
* libmailutils/tests/parseopt_help05.at: Likewise.
* libmailutils/tests/parseopt_help06.at: Likewise.
* libmailutils/tests/parseopt_help07.at: Likewise.
* libmailutils/tests/parseopt_help08.at: Likewise.
* libmailutils/tests/parseopt_help09.at: Likewise.
* libmailutils/tests/parseopt_help10.at: Likewise.
* libmailutils/tests/parseopt_help11.at: Likewise.
1 parent 06c13b34
...@@ -50,7 +50,7 @@ struct mu_cli_setup ...@@ -50,7 +50,7 @@ struct mu_cli_setup
50 errors */ 50 errors */
51 }; 51 };
52 52
53 void mu_version_func (struct mu_parseopt *po, FILE *stream); 53 void mu_version_func (struct mu_parseopt *po, mu_stream_t stream);
54 void mu_cli (int argc, char **argv, struct mu_cli_setup *setup, 54 void mu_cli (int argc, char **argv, struct mu_cli_setup *setup,
55 char **capa, void *data, 55 char **capa, void *data,
56 int *ret_argc, char ***ret_argv); 56 int *ret_argc, char ***ret_argv);
......
...@@ -131,9 +131,8 @@ struct mu_parseopt ...@@ -131,9 +131,8 @@ struct mu_parseopt
131 char const *po_package_url; 131 char const *po_package_url;
132 char const *po_extra_info; 132 char const *po_extra_info;
133 133
134 /* FIXME: should these take mu_stream_t ?*/ 134 void (*po_help_hook) (struct mu_parseopt *po, mu_stream_t stream);
135 void (*po_help_hook) (struct mu_parseopt *po, FILE *stream); 135 void (*po_version_hook) (struct mu_parseopt *po, mu_stream_t stream);
136 void (*po_version_hook) (struct mu_parseopt *po, FILE *stream);
137 136
138 /* Output data */ 137 /* Output data */
139 int po_ind; /* Index of the next option */ 138 int po_ind; /* Index of the next option */
...@@ -169,11 +168,12 @@ int mu_parseopt_apply (struct mu_parseopt *p); ...@@ -169,11 +168,12 @@ int mu_parseopt_apply (struct mu_parseopt *p);
169 void mu_parseopt_free (struct mu_parseopt *p); 168 void mu_parseopt_free (struct mu_parseopt *p);
170 169
171 unsigned mu_parseopt_getcolumn (const char *name); 170 unsigned mu_parseopt_getcolumn (const char *name);
172 void mu_parseopt_fmt_text (const char *text, size_t col);
173 171
174 void mu_option_describe_options (struct mu_option **optbuf, size_t optcnt); 172 void mu_option_describe_options (mu_stream_t str,
175 void mu_program_help (struct mu_parseopt *p); 173 struct mu_option **optbuf, size_t optcnt);
176 void mu_program_usage (struct mu_parseopt *p); 174 void mu_program_help (struct mu_parseopt *p, mu_stream_t str);
175 void mu_program_usage (struct mu_parseopt *p, mu_stream_t str);
176 void mu_program_version (struct mu_parseopt *po, mu_stream_t str);
177 177
178 void mu_option_set_value (struct mu_parseopt *po, struct mu_option *opt, 178 void mu_option_set_value (struct mu_parseopt *po, struct mu_option *opt,
179 char const *arg); 179 char const *arg);
......
...@@ -208,10 +208,12 @@ enum mu_buffer_type ...@@ -208,10 +208,12 @@ enum mu_buffer_type
208 ((n) == MU_TRANSPORT_INPUT || (n) == MU_TRANSPORT_OUTPUT) 208 ((n) == MU_TRANSPORT_INPUT || (n) == MU_TRANSPORT_OUTPUT)
209 209
210 /* Word wrapper streams */ 210 /* Word wrapper streams */
211 #define MU_IOCTL_WORDWRAP_GET_MARGIN 0 211 #define MU_IOCTL_WORDWRAP_GET_MARGIN 0
212 #define MU_IOCTL_WORDWRAP_SET_MARGIN 1 212 #define MU_IOCTL_WORDWRAP_SET_MARGIN 1
213 #define MU_IOCTL_WORDWRAP_MOVE_MARGIN 2 213 #define MU_IOCTL_WORDWRAP_MOVE_MARGIN 2
214 214 #define MU_IOCTL_WORDWRAP_SET_NEXT_MARGIN 3
215 #define MU_IOCTL_WORDWRAP_GET_OFFSET 4
216
215 struct mu_nullstream_pattern 217 struct mu_nullstream_pattern
216 { 218 {
217 char *pattern; 219 char *pattern;
......
...@@ -53,40 +53,37 @@ const char mu_version_copyright[] = ...@@ -53,40 +53,37 @@ const char mu_version_copyright[] =
53 "Copyright %s 2007-2016 Free Software Foundation, inc."; 53 "Copyright %s 2007-2016 Free Software Foundation, inc.";
54 54
55 void 55 void
56 mu_version_func (struct mu_parseopt *po, FILE *stream) 56 mu_version_func (struct mu_parseopt *po, mu_stream_t stream)
57 { 57 {
58 #ifdef GIT_DESCRIBE 58 #ifdef GIT_DESCRIBE
59 fprintf (stream, "%s (%s) %s [%s]\n", 59 mu_stream_printf (stream, "%s (%s) %s [%s]\n",
60 mu_program_name, PACKAGE_NAME, PACKAGE_VERSION, GIT_DESCRIBE); 60 mu_program_name, PACKAGE_NAME, PACKAGE_VERSION,
61 GIT_DESCRIBE);
61 #else 62 #else
62 fprintf (stream, "%s (%s) %s\n", mu_program_name, 63 mu_stream_printf (stream, "%s (%s) %s\n", mu_program_name,
63 PACKAGE_NAME, PACKAGE_VERSION); 64 PACKAGE_NAME, PACKAGE_VERSION);
64 #endif 65 #endif
65 /* TRANSLATORS: Translate "(C)" to the copyright symbol 66 /* TRANSLATORS: Translate "(C)" to the copyright symbol
66 (C-in-a-circle), if this symbol is available in the user's 67 (C-in-a-circle), if this symbol is available in the user's
67 locale. Otherwise, do not translate "(C)"; leave it as-is. */ 68 locale. Otherwise, do not translate "(C)"; leave it as-is. */
68 fprintf (stream, mu_version_copyright, _("(C)")); 69 mu_stream_printf (stream, mu_version_copyright, _("(C)"));
69 fputs (_("\ 70 mu_stream_printf (stream, _("\
70 \n\ 71 \n\
71 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\n\ 72 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\n\
72 There is NO WARRANTY, to the extent permitted by law.\n\ 73 There is NO WARRANTY, to the extent permitted by law.\n\
73 \n\ 74 \n\
74 "), 75 "));
75 stream);
76 } 76 }
77 77
78 static char gnu_general_help_url[] = 78 static char gnu_general_help_url[] =
79 N_("General help using GNU software: <http://www.gnu.org/gethelp/>"); 79 N_("General help using GNU software: <http://www.gnu.org/gethelp/>");
80 80
81 static void 81 static void
82 extra_help_hook (struct mu_parseopt *po, FILE *stream) 82 extra_help_hook (struct mu_parseopt *po, mu_stream_t stream)
83 { 83 {
84 struct mu_cfg_parse_hints *hints = po->po_data; 84 struct mu_cfg_parse_hints *hints = po->po_data;
85 struct mu_cli_setup *setup = hints->data; 85 struct mu_cli_setup *setup = hints->data;
86 char *extra_doc = _(setup->prog_extra_doc); 86 mu_stream_printf (stream, "%s\n", _(setup->prog_extra_doc));
87 /* FIXME: mu_parseopt help output should get FILE * argument */
88 mu_parseopt_fmt_text (extra_doc, 0);
89 fputc ('\n', stdout);
90 } 87 }
91 88
92 static void 89 static void
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
27 #include <mailutils/cctype.h> 27 #include <mailutils/cctype.h>
28 #include <mailutils/nls.h> 28 #include <mailutils/nls.h>
29 #include <mailutils/wordsplit.h> 29 #include <mailutils/wordsplit.h>
30 #include <mailutils/stream.h>
30 31
31 unsigned short_opt_col = 2; 32 unsigned short_opt_col = 2;
32 unsigned long_opt_col = 6; 33 unsigned long_opt_col = 6;
...@@ -184,91 +185,69 @@ init_usage_vars (struct mu_parseopt *po) ...@@ -184,91 +185,69 @@ init_usage_vars (struct mu_parseopt *po)
184 185
185 mu_wordsplit_free (&ws); 186 mu_wordsplit_free (&ws);
186 } 187 }
187 188
188 static int 189 static void
189 indent (int start, int col) 190 set_margin (mu_stream_t str, unsigned margin)
190 { 191 {
191 for (; start < col; start++) 192 mu_stream_ioctl (str, MU_IOCTL_WORDWRAPSTREAM,
192 putchar (' '); 193 MU_IOCTL_WORDWRAP_SET_MARGIN,
193 return start; 194 &margin);
194 } 195 }
195
196 static void 196 static void
197 print_option_descr (const char *descr, size_t lmargin, size_t rmargin) 197 set_next_margin (mu_stream_t str, unsigned margin)
198 { 198 {
199 while (*descr) 199 mu_stream_ioctl (str, MU_IOCTL_WORDWRAPSTREAM,
200 { 200 MU_IOCTL_WORDWRAP_SET_NEXT_MARGIN,
201 size_t s = 0; 201 &margin);
202 size_t i;
203 size_t width = rmargin - lmargin;
204
205 for (i = 0; ; i++)
206 {
207 if (descr[i] == 0 || descr[i] == ' ' || descr[i] == '\t')
208 {
209 if (i > width)
210 break;
211 s = i;
212 if (descr[i] == 0)
213 break;
214 }
215 else if (descr[i] == '\n')
216 {
217 s = i;
218 break;
219 }
220 }
221 fwrite (descr, 1, s, stdout);
222 fputc ('\n', stdout);
223 descr += s;
224 if (*descr)
225 {
226 indent (0, lmargin);
227 descr++;
228 }
229 }
230 } 202 }
231 203 static void
232 void 204 get_offset (mu_stream_t str, unsigned *offset)
233 mu_parseopt_fmt_text (const char *text, size_t col)
234 { 205 {
235 print_option_descr (text, col, rmargin); 206 mu_stream_ioctl (str, MU_IOCTL_WORDWRAPSTREAM,
207 MU_IOCTL_WORDWRAP_GET_OFFSET,
208 offset);
209 }
210 static void
211 move_margin (mu_stream_t str, int margin)
212 {
213 mu_stream_ioctl (str, MU_IOCTL_WORDWRAPSTREAM,
214 MU_IOCTL_WORDWRAP_MOVE_MARGIN,
215 &margin);
236 } 216 }
237
238 217
239 static size_t 218 static void
240 print_opt_arg (struct mu_option *opt, int delim) 219 print_opt_arg (mu_stream_t str, struct mu_option *opt, int delim)
241 { 220 {
242 int w = 0;
243 if (opt->opt_flags & MU_OPTION_ARG_OPTIONAL) 221 if (opt->opt_flags & MU_OPTION_ARG_OPTIONAL)
244 { 222 {
245 if (delim == '=') 223 if (delim == '=')
246 w = printf ("[=%s]", gettext (opt->opt_arg)); 224 mu_stream_printf (str, "[=%s]", gettext (opt->opt_arg));
247 else 225 else
248 w = printf ("[%s]", gettext (opt->opt_arg)); 226 mu_stream_printf (str, "[%s]", gettext (opt->opt_arg));
249 } 227 }
250 else 228 else
251 w = printf ("%c%s", delim, gettext (opt->opt_arg)); 229 mu_stream_printf (str, "%c%s", delim, gettext (opt->opt_arg));
252 return w;
253 } 230 }
254 231
255 static size_t 232 static size_t
256 print_option (struct mu_option **optbuf, size_t optcnt, size_t num, 233 print_option (mu_stream_t str,
234 struct mu_option **optbuf, size_t optcnt, size_t num,
257 int *argsused) 235 int *argsused)
258 { 236 {
259 struct mu_option *opt = optbuf[num]; 237 struct mu_option *opt = optbuf[num];
260 size_t next, i; 238 size_t next, i;
261 int delim; 239 int delim;
262 int w; 240 int first_option = 1;
241 int first_long_option = 1;
263 242
264 if (MU_OPTION_IS_GROUP_HEADER (opt)) 243 if (MU_OPTION_IS_GROUP_HEADER (opt))
265 { 244 {
266 if (num) 245 if (num)
267 putchar ('\n'); 246 mu_stream_printf (str, "\n");
268 if (opt->opt_doc[0]) 247 if (opt->opt_doc[0])
269 { 248 {
270 indent (0, header_col); 249 set_margin (str, header_col);
271 print_option_descr (gettext (opt->opt_doc), header_col, rmargin); 250 mu_stream_printf (str, "%s", gettext (opt->opt_doc));
272 } 251 }
273 return num + 1; 252 return num + 1;
274 } 253 }
...@@ -281,22 +260,19 @@ print_option (struct mu_option **optbuf, size_t optcnt, size_t num, ...@@ -281,22 +260,19 @@ print_option (struct mu_option **optbuf, size_t optcnt, size_t num,
281 if (opt->opt_flags & MU_OPTION_HIDDEN) 260 if (opt->opt_flags & MU_OPTION_HIDDEN)
282 return next; 261 return next;
283 262
284 w = 0; 263 set_margin (str, short_opt_col);
285 for (i = num; i < next; i++) 264 for (i = num; i < next; i++)
286 { 265 {
287 if (MU_OPTION_IS_VALID_SHORT_OPTION (optbuf[i])) 266 if (MU_OPTION_IS_VALID_SHORT_OPTION (optbuf[i]))
288 { 267 {
289 if (w == 0) 268 if (first_option)
290 { 269 first_option = 0;
291 indent (0, short_opt_col);
292 w = short_opt_col;
293 }
294 else 270 else
295 w += printf (", "); 271 mu_stream_printf (str, ", ");
296 w += printf ("-%c", optbuf[i]->opt_short); 272 mu_stream_printf (str, "-%c", optbuf[i]->opt_short);
297 delim = ' '; 273 delim = ' ';
298 if (opt->opt_arg && dup_args) 274 if (opt->opt_arg && dup_args)
299 w += print_opt_arg (opt, delim); 275 print_opt_arg (str, opt, delim);
300 } 276 }
301 } 277 }
302 278
...@@ -304,19 +280,24 @@ print_option (struct mu_option **optbuf, size_t optcnt, size_t num, ...@@ -304,19 +280,24 @@ print_option (struct mu_option **optbuf, size_t optcnt, size_t num,
304 { 280 {
305 if (MU_OPTION_IS_VALID_LONG_OPTION (optbuf[i])) 281 if (MU_OPTION_IS_VALID_LONG_OPTION (optbuf[i]))
306 { 282 {
307 if (w == 0) 283 if (first_option)
284 first_option = 0;
285 else
286 mu_stream_printf (str, ", ");
287
288 if (first_long_option)
308 { 289 {
309 indent (0, short_opt_col); 290 unsigned off;
310 w = short_opt_col; 291 get_offset (str, &off);
292 if (off < long_opt_col)
293 set_margin (str, long_opt_col);
294 first_long_option = 0;
311 } 295 }
312 else 296
313 w += printf (", "); 297 mu_stream_printf (str, "--%s", optbuf[i]->opt_long);
314 if (i == num)
315 w = indent (w, long_opt_col);
316 w += printf ("--%s", optbuf[i]->opt_long);
317 delim = '='; 298 delim = '=';
318 if (opt->opt_arg && dup_args) 299 if (opt->opt_arg && dup_args)
319 w += print_opt_arg (opt, delim); 300 print_opt_arg (str, opt, delim);
320 } 301 }
321 } 302 }
322 303
...@@ -324,70 +305,87 @@ print_option (struct mu_option **optbuf, size_t optcnt, size_t num, ...@@ -324,70 +305,87 @@ print_option (struct mu_option **optbuf, size_t optcnt, size_t num,
324 { 305 {
325 *argsused = 1; 306 *argsused = 1;
326 if (!dup_args) 307 if (!dup_args)
327 w += print_opt_arg (opt, delim); 308 print_opt_arg (str, opt, delim);
328 }
329 if (w >= opt_doc_col)
330 {
331 putchar ('\n');
332 w = 0;
333 } 309 }
334 indent (w, opt_doc_col); 310
335 print_option_descr (gettext (opt->opt_doc), opt_doc_col, rmargin); 311 set_margin (str, opt_doc_col);
312 mu_stream_printf (str, "%s\n", gettext (opt->opt_doc));
336 313
337 return next; 314 return next;
338 } 315 }
339 316
340 void 317 void
341 mu_option_describe_options (struct mu_option **optbuf, size_t optcnt) 318 mu_option_describe_options (mu_stream_t str,
319 struct mu_option **optbuf, size_t optcnt)
342 { 320 {
343 unsigned i; 321 unsigned i;
344 int argsused = 0; 322 int argsused = 0;
345 323
346 for (i = 0; i < optcnt; ) 324 for (i = 0; i < optcnt; )
347 i = print_option (optbuf, optcnt, i, &argsused); 325 i = print_option (str, optbuf, optcnt, i, &argsused);
348 putchar ('\n'); 326 mu_stream_printf (str, "\n");
349 327
350 if (argsused && dup_args_note) 328 if (argsused && dup_args_note)
351 { 329 {
352 print_option_descr (_("Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options."), 0, rmargin); 330 set_margin (str, 0);
353 putchar ('\n'); 331 mu_stream_printf (str, "%s\n\n",
332 _("Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options."));
354 } 333 }
355 } 334 }
356 335
357 void 336 void
358 mu_program_help (struct mu_parseopt *po) 337 mu_program_help (struct mu_parseopt *po, mu_stream_t outstr)
359 { 338 {
339 mu_stream_t str;
340 int rc;
341
360 init_usage_vars (po); 342 init_usage_vars (po);
361 343
362 printf ("%s", _("Usage:")); 344 rc = mu_wordwrap_stream_create (&str, outstr, 0, rmargin);
345 if (rc)
346 {
347 abort ();//FIXME
348 }
349
350 mu_stream_printf (str, "%s", _("Usage:"));
363 if (po->po_prog_name) 351 if (po->po_prog_name)
364 printf (" %s", po->po_prog_name); 352 mu_stream_printf (str, " %s ", po->po_prog_name);
365 printf (" [%s]...", _("OPTION")); 353 move_margin (str, 0);
354 mu_stream_printf (str, "[%s]...", _("OPTION"));
366 if (po->po_prog_args) 355 if (po->po_prog_args)
367 printf (" %s", gettext (po->po_prog_args)); 356 mu_stream_printf (str, " %s", gettext (po->po_prog_args));
368 putchar ('\n'); 357 mu_stream_printf (str, "\n");
369 358
370 if (po->po_prog_doc) 359 if (po->po_prog_doc)
371 print_option_descr (gettext (po->po_prog_doc), 0, rmargin); 360 {
372 putchar ('\n'); 361 set_margin (str, 0);
373 362 mu_stream_printf (str, "%s\n", gettext (po->po_prog_doc));
374 mu_option_describe_options (po->po_optv, po->po_optc); 363 }
364 mu_stream_printf (str, "\n");
365
366 mu_option_describe_options (str, po->po_optv, po->po_optc);
375 367
376 if (po->po_help_hook) 368 if (po->po_help_hook)
377 po->po_help_hook (po, stdout); 369 {
378 370 po->po_help_hook (po, str);
371 mu_stream_printf (str, "\n");
372 }
373
374 set_margin (str, 0);
379 if (po->po_bug_address) 375 if (po->po_bug_address)
380 /* TRANSLATORS: The placeholder indicates the bug-reporting address 376 /* TRANSLATORS: The placeholder indicates the bug-reporting address
381 for this package. Please add _another line_ saying 377 for this package. Please add _another line_ saying
382 "Report translation bugs to <...>\n" with the address for translation 378 "Report translation bugs to <...>\n" with the address for translation
383 bugs (typically your translation team's web or email address). */ 379 bugs (typically your translation team's web or email address). */
384 printf (_("Report bugs to <%s>.\n"), po->po_bug_address); 380 mu_stream_printf (str, _("Report bugs to <%s>.\n"), po->po_bug_address);
385 381
386 if (po->po_package_name && po->po_package_url) 382 if (po->po_package_name && po->po_package_url)
387 printf (_("%s home page: <%s>\n"), 383 mu_stream_printf (str, _("%s home page: <%s>\n"),
388 po->po_package_name, po->po_package_url); 384 po->po_package_name, po->po_package_url);
389 if (po->po_flags & MU_PARSEOPT_EXTRA_INFO) 385 if (po->po_flags & MU_PARSEOPT_EXTRA_INFO)
390 print_option_descr (_(po->po_extra_info), 0, rmargin); 386 mu_stream_printf (str, "%s\n", _(po->po_extra_info));
387
388 mu_stream_destroy (&str);
391 } 389 }
392 390
393 static struct mu_option **option_tab; 391 static struct mu_option **option_tab;
...@@ -426,41 +424,32 @@ cmpidx_long (const void *a, const void *b) ...@@ -426,41 +424,32 @@ cmpidx_long (const void *a, const void *b)
426 } 424 }
427 425
428 void 426 void
429 mu_program_usage (struct mu_parseopt *po) 427 mu_program_usage (struct mu_parseopt *po, mu_stream_t outstr)
430 { 428 {
429 int rc;
431 unsigned i; 430 unsigned i;
432 unsigned n;
433 char buf[rmargin+1];
434 unsigned *idxbuf; 431 unsigned *idxbuf;
435 unsigned nidx; 432 unsigned nidx;
436 433
437 struct mu_option **optbuf = po->po_optv; 434 struct mu_option **optbuf = po->po_optv;
438 size_t optcnt = po->po_optc; 435 size_t optcnt = po->po_optc;
439 436
440 #define FLUSH \ 437 mu_stream_t str;
441 do \
442 { \
443 buf[n] = 0; \
444 printf ("%s\n", buf); \
445 n = usage_indent; \
446 if (n) memset (buf, ' ', n); \
447 } \
448 while (0)
449 #define ADDC(c) \
450 do \
451 { \
452 if (n == rmargin) FLUSH; \
453 buf[n++] = c; \
454 } \
455 while (0)
456 438
457 init_usage_vars (po); 439 init_usage_vars (po);
458 440
441 rc = mu_wordwrap_stream_create (&str, outstr, 0, rmargin);
442 if (rc)
443 {
444 abort ();//FIXME
445 }
446
459 option_tab = optbuf; 447 option_tab = optbuf;
460 448
461 idxbuf = mu_calloc (optcnt, sizeof (idxbuf[0])); 449 idxbuf = mu_calloc (optcnt, sizeof (idxbuf[0]));
462 450
463 n = snprintf (buf, sizeof buf, "%s %s ", _("Usage:"), po->po_prog_name); 451 mu_stream_printf (str, "%s %s ", _("Usage:"), po->po_prog_name);
452 set_next_margin (str, usage_indent);
464 453
465 /* Print a list of short options without arguments. */ 454 /* Print a list of short options without arguments. */
466 for (i = nidx = 0; i < optcnt; i++) 455 for (i = nidx = 0; i < optcnt; i++)
...@@ -470,14 +459,12 @@ mu_program_usage (struct mu_parseopt *po) ...@@ -470,14 +459,12 @@ mu_program_usage (struct mu_parseopt *po)
470 if (nidx) 459 if (nidx)
471 { 460 {
472 qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_short); 461 qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_short);
473 462 mu_stream_printf (str, "[-");
474 ADDC ('[');
475 ADDC ('-');
476 for (i = 0; i < nidx; i++) 463 for (i = 0; i < nidx; i++)
477 { 464 {
478 ADDC (optbuf[idxbuf[i]]->opt_short); 465 mu_stream_printf (str, "%c", optbuf[idxbuf[i]]->opt_short);
479 } 466 }
480 ADDC (']'); 467 mu_stream_printf (str, "%c", ']');
481 } 468 }
482 469
483 /* Print a list of short options with arguments. */ 470 /* Print a list of short options with arguments. */
...@@ -495,19 +482,10 @@ mu_program_usage (struct mu_parseopt *po) ...@@ -495,19 +482,10 @@ mu_program_usage (struct mu_parseopt *po)
495 { 482 {
496 struct mu_option *opt = optbuf[idxbuf[i]]; 483 struct mu_option *opt = optbuf[idxbuf[i]];
497 const char *arg = gettext (opt->opt_arg); 484 const char *arg = gettext (opt->opt_arg);
498 size_t len = 5 + strlen (arg) + 1; 485 if (opt->opt_flags & MU_OPTION_ARG_OPTIONAL)
499 486 mu_stream_printf (str, " [-%c[%s]]", opt->opt_short, arg);
500 if (n + len >= rmargin)
501 FLUSH;
502 else 487 else
503 buf[n++] = ' '; 488 mu_stream_printf (str, " [-%c %s]", opt->opt_short, arg);
504 buf[n++] = '[';
505 buf[n++] = '-';
506 buf[n++] = opt->opt_short;
507 buf[n++] = ' ';
508 strcpy (&buf[n], arg);
509 n += strlen (arg);
510 buf[n++] = ']';
511 } 489 }
512 } 490 }
513 491
...@@ -526,52 +504,41 @@ mu_program_usage (struct mu_parseopt *po) ...@@ -526,52 +504,41 @@ mu_program_usage (struct mu_parseopt *po)
526 { 504 {
527 struct mu_option *opt = optbuf[idxbuf[i]]; 505 struct mu_option *opt = optbuf[idxbuf[i]];
528 const char *arg = opt->opt_arg ? gettext (opt->opt_arg) : NULL; 506 const char *arg = opt->opt_arg ? gettext (opt->opt_arg) : NULL;
529 size_t len = 5 + strlen (opt->opt_long) 507
530 + (arg ? 1 + strlen (arg) : 0); 508 mu_stream_printf (str, " [--%s", opt->opt_long);
531 if (n + len >= rmargin)
532 FLUSH;
533 else
534 buf[n++] = ' ';
535 buf[n++] = '[';
536 buf[n++] = '-';
537 buf[n++] = '-';
538 strcpy (&buf[n], opt->opt_long);
539 n += strlen (opt->opt_long);
540 if (opt->opt_arg) 509 if (opt->opt_arg)
541 { 510 {
542 buf[n++] = '='; 511 if (opt->opt_flags & MU_OPTION_ARG_OPTIONAL)
543 strcpy (&buf[n], arg); 512 mu_stream_printf (str, "[=%s]", arg);
544 n += strlen (arg); 513 else
514 mu_stream_printf (str, "=%s", arg);
545 } 515 }
546 buf[n++] = ']'; 516 mu_stream_printf (str, "%c", ']');
547 } 517 }
548 } 518 }
549 519
550 if (po->po_flags & MU_PARSEOPT_PROG_ARGS) 520 if (po->po_flags & MU_PARSEOPT_PROG_ARGS)
551 { 521 mu_stream_printf (str, " %s", _(po->po_prog_args));
552 char const *p = po->po_prog_args;
553
554 if (n + 1 >= rmargin)
555 FLUSH;
556 buf[n++] = ' ';
557
558 while (*p)
559 {
560 size_t len = strcspn (p, " \t\n");
561 if (len == 0)
562 len = 1;
563 if (n + len >= rmargin)
564 FLUSH;
565 else
566 {
567 memcpy (buf + n, p, len);
568 p += len;
569 n += len;
570 }
571 }
572 }
573 522
574 FLUSH; 523 mu_stream_destroy (&str);
524
575 free (idxbuf); 525 free (idxbuf);
576 } 526 }
577 527
528 void
529 mu_program_version (struct mu_parseopt *po, mu_stream_t outstr)
530 {
531 int rc;
532 mu_stream_t str;
533
534 init_usage_vars (po);
535
536 rc = mu_wordwrap_stream_create (&str, outstr, 0, rmargin);
537 if (rc)
538 {
539 abort ();//FIXME
540 }
541 po->po_version_hook (po, str);
542
543 mu_stream_destroy (&str);
544 }
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
24 #include <mailutils/opt.h> 24 #include <mailutils/opt.h>
25 #include <mailutils/nls.h> 25 #include <mailutils/nls.h>
26 #include <mailutils/errno.h> 26 #include <mailutils/errno.h>
27 #include <mailutils/stdstream.h>
27 28
28 #define EXIT_SUCCESS 0 29 #define EXIT_SUCCESS 0
29 #define EXIT_ERROR 1 30 #define EXIT_ERROR 1
...@@ -78,7 +79,7 @@ sort_group (struct mu_option **optbuf, size_t start) ...@@ -78,7 +79,7 @@ sort_group (struct mu_option **optbuf, size_t start)
78 static void 79 static void
79 fn_help (struct mu_parseopt *po, struct mu_option *opt, char const *unused) 80 fn_help (struct mu_parseopt *po, struct mu_option *opt, char const *unused)
80 { 81 {
81 mu_program_help (po); 82 mu_program_help (po, mu_strout);
82 exit (EXIT_SUCCESS); 83 exit (EXIT_SUCCESS);
83 } 84 }
84 85
...@@ -86,14 +87,14 @@ fn_help (struct mu_parseopt *po, struct mu_option *opt, char const *unused) ...@@ -86,14 +87,14 @@ fn_help (struct mu_parseopt *po, struct mu_option *opt, char const *unused)
86 static void 87 static void
87 fn_usage (struct mu_parseopt *po, struct mu_option *opt, char const *unused) 88 fn_usage (struct mu_parseopt *po, struct mu_option *opt, char const *unused)
88 { 89 {
89 mu_program_usage (po); 90 mu_program_usage (po, mu_strout);
90 exit (EXIT_SUCCESS); 91 exit (EXIT_SUCCESS);
91 } 92 }
92 93
93 static void 94 static void
94 fn_version (struct mu_parseopt *po, struct mu_option *opt, char const *unused) 95 fn_version (struct mu_parseopt *po, struct mu_option *opt, char const *unused)
95 { 96 {
96 po->po_version_hook (po, stdout); 97 mu_program_version (po, mu_strout);
97 exit (EXIT_SUCCESS); 98 exit (EXIT_SUCCESS);
98 } 99 }
99 100
......
...@@ -88,7 +88,7 @@ _wordwrap_flush_line (struct mu_wordwrap_stream *str, int lookahead) ...@@ -88,7 +88,7 @@ _wordwrap_flush_line (struct mu_wordwrap_stream *str, int lookahead)
88 } 88 }
89 } 89 }
90 90
91 while (length > 0 && mu_isblank (str->buffer[length - 1])) 91 while (length > 0 && mu_isspace (str->buffer[length - 1]))
92 length--; 92 length--;
93 93
94 if (length == 0 || str->buffer[length - 1] != '\n') 94 if (length == 0 || str->buffer[length - 1] != '\n')
...@@ -147,7 +147,7 @@ static int ...@@ -147,7 +147,7 @@ static int
147 _wordwrap_flush (mu_stream_t stream) 147 _wordwrap_flush (mu_stream_t stream)
148 { 148 {
149 struct mu_wordwrap_stream *str = (struct mu_wordwrap_stream *)stream; 149 struct mu_wordwrap_stream *str = (struct mu_wordwrap_stream *)stream;
150 if (str->offset) 150 if (str->offset > str->left_margin)
151 _wordwrap_flush_line (str, 0); 151 _wordwrap_flush_line (str, 0);
152 return mu_stream_flush (str->transport); 152 return mu_stream_flush (str->transport);
153 } 153 }
...@@ -177,18 +177,19 @@ set_margin (mu_stream_t stream, unsigned lmargin, int off) ...@@ -177,18 +177,19 @@ set_margin (mu_stream_t stream, unsigned lmargin, int off)
177 177
178 if (lmargin >= str->right_margin) 178 if (lmargin >= str->right_margin)
179 return EINVAL; 179 return EINVAL;
180 if (lmargin < str->offset) 180
181 str->left_margin = lmargin;
182 if (lmargin < str->offset ||
183 (str->offset > 0 && str->buffer[str->offset - 1] == '\n'))
181 { 184 {
182 str->left_margin = lmargin;
183 _wordwrap_flush (stream); 185 _wordwrap_flush (stream);
184 } 186 }
185 else if (lmargin > str->offset) 187 else if (lmargin > str->offset)
186 { 188 {
187 memset (str->buffer + str->offset, ' ', 189 memset (str->buffer + str->offset, ' ', lmargin - str->offset);
188 lmargin - str->offset);
189 str->left_margin = lmargin;
190 str->offset = lmargin; 190 str->offset = lmargin;
191 } 191 }
192
192 return 0; 193 return 0;
193 } 194 }
194 195
...@@ -217,12 +218,30 @@ _wordwrap_ctl (mu_stream_t stream, int code, int opcode, void *arg) ...@@ -217,12 +218,30 @@ _wordwrap_ctl (mu_stream_t stream, int code, int opcode, void *arg)
217 else 218 else
218 return set_margin (stream, *(unsigned*)arg, 0); 219 return set_margin (stream, *(unsigned*)arg, 0);
219 220
221 case MU_IOCTL_WORDWRAP_SET_NEXT_MARGIN:
222 if (!arg)
223 return EINVAL;
224 else
225 {
226 unsigned marg = *(unsigned*)arg;
227 if (marg >= str->right_margin)
228 return EINVAL;
229 str->left_margin = marg;
230 }
231 break;
232
220 case MU_IOCTL_WORDWRAP_MOVE_MARGIN: 233 case MU_IOCTL_WORDWRAP_MOVE_MARGIN:
221 if (!arg) 234 if (!arg)
222 return EINVAL; 235 return EINVAL;
223 else 236 else
224 return set_margin (stream, str->offset, *(int*)arg); 237 return set_margin (stream, str->offset, *(int*)arg);
225 238
239 case MU_IOCTL_WORDWRAP_GET_OFFSET:
240 if (!arg)
241 return EINVAL;
242 *(unsigned*)arg = str->offset;
243 break;
244
226 default: 245 default:
227 return EINVAL; 246 return EINVAL;
228 } 247 }
......
...@@ -64,9 +64,9 @@ struct mu_option group_b[] = { ...@@ -64,9 +64,9 @@ struct mu_option group_b[] = {
64 struct mu_option *optv[] = { group_a, group_b, NULL }; 64 struct mu_option *optv[] = { group_a, group_b, NULL };
65 65
66 static void 66 static void
67 version_hook (struct mu_parseopt *po, FILE *fp) 67 version_hook (struct mu_parseopt *po, mu_stream_t str)
68 { 68 {
69 fputs ("version hook called\n", fp); 69 mu_stream_printf (str, "version hook called\n");
70 } 70 }
71 71
72 #define S(s) ((s)?(s):"(null)") 72 #define S(s) ((s)?(s):"(null)")
......
...@@ -30,8 +30,8 @@ parseopt --help ...@@ -30,8 +30,8 @@ parseopt --help
30 -x short-only option 30 -x short-only option
31 31
32 Group B 32 Group B
33 -F, --find=VALUE find VALUE
34 -d, -v, --debug, --verbose another option 33 -d, -v, --debug, --verbose another option
34 -F, --find=VALUE find VALUE
35 -j, --jobs=N sets numeric value 35 -j, --jobs=N sets numeric value
36 36
37 -?, --help give this help list 37 -?, --help give this help list
......
...@@ -21,8 +21,8 @@ PARSEOPT_DEFAULT ...@@ -21,8 +21,8 @@ PARSEOPT_DEFAULT
21 parseopt --usage 21 parseopt --usage
22 ], 22 ],
23 [0], 23 [0],
24 [[Usage: parseopt [-advx?] [-f FILE] [-F VALUE] [-j N] [-o FILE] [--all] 24 [[Usage: parseopt [-advx?] [-f FILE] [-F VALUE] [-j N] [-o[FILE]] [--all]
25 [--debug] [--file=FILE] [--find=VALUE] [--help] [--jobs=N] 25 [--debug] [--file=FILE] [--find=VALUE] [--help] [--jobs=N]
26 [--optional=FILE] [--usage] [--verbose] 26 [--optional[=FILE]] [--usage] [--verbose]
27 ]]) 27 ]])
28 AT_CLEANUP 28 AT_CLEANUP
......
...@@ -30,8 +30,8 @@ MU_PARSEOPT_PROG_NAME=newname parseopt --help ...@@ -30,8 +30,8 @@ MU_PARSEOPT_PROG_NAME=newname parseopt --help
30 -x short-only option 30 -x short-only option
31 31
32 Group B 32 Group B
33 -F, --find=VALUE find VALUE
34 -d, -v, --debug, --verbose another option 33 -d, -v, --debug, --verbose another option
34 -F, --find=VALUE find VALUE
35 -j, --jobs=N sets numeric value 35 -j, --jobs=N sets numeric value
36 36
37 -?, --help give this help list 37 -?, --help give this help list
......
...@@ -31,8 +31,8 @@ Tests option parsing ...@@ -31,8 +31,8 @@ Tests option parsing
31 -x short-only option 31 -x short-only option
32 32
33 Group B 33 Group B
34 -F, --find=VALUE find VALUE
35 -d, -v, --debug, --verbose another option 34 -d, -v, --debug, --verbose another option
35 -F, --find=VALUE find VALUE
36 -j, --jobs=N sets numeric value 36 -j, --jobs=N sets numeric value
37 37
38 -?, --help give this help list 38 -?, --help give this help list
......
...@@ -30,8 +30,8 @@ MU_PARSEOPT_PROG_ARGS="SOME MORE ARGS" parseopt --help ...@@ -30,8 +30,8 @@ MU_PARSEOPT_PROG_ARGS="SOME MORE ARGS" parseopt --help
30 -x short-only option 30 -x short-only option
31 31
32 Group B 32 Group B
33 -F, --find=VALUE find VALUE
34 -d, -v, --debug, --verbose another option 33 -d, -v, --debug, --verbose another option
34 -F, --find=VALUE find VALUE
35 -j, --jobs=N sets numeric value 35 -j, --jobs=N sets numeric value
36 36
37 -?, --help give this help list 37 -?, --help give this help list
......
...@@ -30,8 +30,8 @@ MU_PARSEOPT_BUG_ADDRESS='gray@gnu.org' parseopt --help ...@@ -30,8 +30,8 @@ MU_PARSEOPT_BUG_ADDRESS='gray@gnu.org' parseopt --help
30 -x short-only option 30 -x short-only option
31 31
32 Group B 32 Group B
33 -F, --find=VALUE find VALUE
34 -d, -v, --debug, --verbose another option 33 -d, -v, --debug, --verbose another option
34 -F, --find=VALUE find VALUE
35 -j, --jobs=N sets numeric value 35 -j, --jobs=N sets numeric value
36 36
37 -?, --help give this help list 37 -?, --help give this help list
......
...@@ -30,8 +30,8 @@ MU_PARSEOPT_PACKAGE_NAME='GNU Mailutils' MU_PARSEOPT_PACKAGE_URL='http://mailuti ...@@ -30,8 +30,8 @@ MU_PARSEOPT_PACKAGE_NAME='GNU Mailutils' MU_PARSEOPT_PACKAGE_URL='http://mailuti
30 -x short-only option 30 -x short-only option
31 31
32 Group B 32 Group B
33 -F, --find=VALUE find VALUE
34 -d, -v, --debug, --verbose another option 33 -d, -v, --debug, --verbose another option
34 -F, --find=VALUE find VALUE
35 -j, --jobs=N sets numeric value 35 -j, --jobs=N sets numeric value
36 36
37 -?, --help give this help list 37 -?, --help give this help list
......
...@@ -37,8 +37,8 @@ Tests option parsing ...@@ -37,8 +37,8 @@ Tests option parsing
37 -x short-only option 37 -x short-only option
38 38
39 Group B 39 Group B
40 -F, --find=VALUE find VALUE
41 -d, -v, --debug, --verbose another option 40 -d, -v, --debug, --verbose another option
41 -F, --find=VALUE find VALUE
42 -j, --jobs=N sets numeric value 42 -j, --jobs=N sets numeric value
43 43
44 -?, --help give this help list 44 -?, --help give this help list
......
...@@ -31,8 +31,8 @@ ARGP_HELP_FMT=dup-args,no-dup-args-note,short-opt-col=1,opt-doc-col=32,header-co ...@@ -31,8 +31,8 @@ ARGP_HELP_FMT=dup-args,no-dup-args-note,short-opt-col=1,opt-doc-col=32,header-co
31 -x short-only option 31 -x short-only option
32 32
33 Group B 33 Group B
34 -F VALUE, --find=VALUE find VALUE
35 -d, -v, --debug, --verbose another option 34 -d, -v, --debug, --verbose another option
35 -F VALUE, --find=VALUE find VALUE
36 -j N, --jobs=N sets numeric value 36 -j N, --jobs=N sets numeric value
37 37
38 -?, --help give this help list 38 -?, --help give this help list
......
...@@ -23,7 +23,7 @@ ARGP_HELP_FMT=rmargin=62,usage-indent=1\ ...@@ -23,7 +23,7 @@ ARGP_HELP_FMT=rmargin=62,usage-indent=1\
23 ], 23 ],
24 [0], 24 [0],
25 [[Usage: parseopt [-advx?] [-f FILE] [-F VALUE] [-j N] 25 [[Usage: parseopt [-advx?] [-f FILE] [-F VALUE] [-j N]
26 [-o FILE] [--all] [--debug] [--file=FILE] [--find=VALUE] 26 [-o[FILE]] [--all] [--debug] [--file=FILE] [--find=VALUE]
27 [--help] [--jobs=N] [--optional=FILE] [--usage] [--verbose] 27 [--help] [--jobs=N] [--optional[=FILE]] [--usage] [--verbose]
28 ]]) 28 ]])
29 AT_CLEANUP 29 AT_CLEANUP
......
...@@ -21,8 +21,8 @@ PARSEOPT_DEFAULT ...@@ -21,8 +21,8 @@ PARSEOPT_DEFAULT
21 MU_PARSEOPT_VERSION_HOOK=1 parseopt --usage 21 MU_PARSEOPT_VERSION_HOOK=1 parseopt --usage
22 ], 22 ],
23 [0], 23 [0],
24 [[Usage: parseopt [-advVx?] [-f FILE] [-F VALUE] [-j N] [-o FILE] [--all] 24 [[Usage: parseopt [-advVx?] [-f FILE] [-F VALUE] [-j N] [-o[FILE]] [--all]
25 [--debug] [--file=FILE] [--find=VALUE] [--help] [--jobs=N] 25 [--debug] [--file=FILE] [--find=VALUE] [--help] [--jobs=N]
26 [--optional=FILE] [--usage] [--verbose] [--version] 26 [--optional[=FILE]] [--usage] [--verbose] [--version]
27 ]]) 27 ]])
28 AT_CLEANUP 28 AT_CLEANUP
......
...@@ -30,8 +30,8 @@ MU_PARSEOPT_VERSION_HOOK=1 parseopt --help ...@@ -30,8 +30,8 @@ MU_PARSEOPT_VERSION_HOOK=1 parseopt --help
30 -x short-only option 30 -x short-only option
31 31
32 Group B 32 Group B
33 -F, --find=VALUE find VALUE
34 -d, -v, --debug, --verbose another option 33 -d, -v, --debug, --verbose another option
34 -F, --find=VALUE find VALUE
35 -j, --jobs=N sets numeric value 35 -j, --jobs=N sets numeric value
36 36
37 -?, --help give this help list 37 -?, --help give this help list
......