ctl.c
1 #include "internal.h" 2 3 #pragma mark Definitions 4 #define CTL_OUTPUT_WIDTH (80) 5 #define CTL_OUTPUT_OPTARG_INDENT (32) 6 #define CTL_OUTPUT_OPTARG_OVERFLOW (CTL_OUTPUT_OPTARG_INDENT - 4) 7 #define SUBCOMMAND_LINKER_SET "__subcommands" 8 9 #define OS_SUBCOMMAND_OPTIONS_FOREACH(_osco_i, _osc, _which, _i) \ 10 while (((_osco_i) = &osc->osc_ ## _which[(_i)]) && \ 11 ((_i) += 1, 1) && \ 12 !((_osco_i)->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_TERMINATOR)) 13 14 #pragma mark Types 15 OS_ENUM(os_subcommand_option_spec_fmt, uint64_t, 16 OS_SUBCOMMAND_OPTION_SPEC_SHORT, 17 OS_SUBCOMMAND_OPTION_SPEC_LONG, 18 OS_SUBCOMMAND_OPTION_SPEC_COMBINED, 19 ); 20 21 #pragma mark Forward Declarations 22 static void _print_header( 23 FILE *f, 24 const char *hdr, 25 bool *already_done); 26 static const os_subcommand_t *_os_subcommand_find( 27 const char *name); 28 static void _os_subcommand_print_usage( 29 const os_subcommand_t *osc, 30 FILE *f); 31 static void _os_subcommand_print_help_line( 32 const os_subcommand_t *osc, 33 FILE *f); 34 static void _print_subcommand_list( 35 const os_subcommand_t *osc, 36 FILE *f); 37 38 #pragma mark Module Globals 39 static const os_subcommand_t __help_cmd; 40 static const os_subcommand_t *_help_cmd = &__help_cmd; 41 42 static const os_subcommand_t __main_cmd; 43 static const os_subcommand_t *_main_cmd = &__main_cmd; 44 static const os_subcommand_t *_internal_main_cmd = &__main_cmd; 45 46 static struct ttysize __ttys = { 47 .ts_lines = 24, 48 .ts_cols = CTL_OUTPUT_WIDTH, 49 }; 50 51 static const struct ttysize *_ttys = &__ttys; 52 53 #pragma mark Module Private 54 static void 55 _init_column_count(void) 56 { 57 const char *columns_env = NULL; 58 char *end = NULL; 59 struct ttysize ttys = { 60 .ts_lines = 24, 61 .ts_cols = CTL_OUTPUT_WIDTH, 62 }; 63 int ret = -1; 64 65 columns_env = getenv("COLUMNS"); 66 if (columns_env) { 67 unsigned short cols = -1; 68 69 cols = strtoul(columns_env, &end, 0); 70 if (end != columns_env && end[0] != 0) { 71 ttys.ts_lines = cols; 72 } 73 } else { 74 ret = ioctl(0, TIOCGSIZE, &ttys); 75 if (ret) { 76 ttys.ts_lines = 24; 77 ttys.ts_cols = CTL_OUTPUT_WIDTH; 78 } 79 } 80 81 __ttys = ttys; 82 } 83 84 static void 85 _stoupper(char *str) 86 { 87 size_t i = 0; 88 size_t len = strlen(str); 89 90 for (i = 0; i < len; i++) { 91 char *cp = &str[i]; 92 *cp = ___toupper(*cp); 93 } 94 } 95 96 #pragma mark Main Subcommand 97 static int _main_invoke(const os_subcommand_t *osc, 98 int argc, 99 const char *argv[]); 100 101 static const os_subcommand_option_t _main_positional[] = { 102 [0] = { 103 .osco_version = OS_SUBCOMMAND_OPTION_VERSION, 104 .osco_flags = 0, 105 .osco_option = NULL, 106 .osco_argument_usage = "subcommand", 107 .osco_argument_human = "The subcommand to invoke", 108 }, 109 OS_SUBCOMMAND_OPTION_TERMINATOR, 110 }; 111 112 static const os_subcommand_t __main_cmd = { 113 .osc_version = OS_SUBCOMMAND_VERSION, 114 .osc_flags = OS_SUBCOMMAND_FLAG_MAIN, 115 .osc_name = "_main", 116 .osc_desc = "main command", 117 .osc_optstring = NULL, 118 .osc_options = NULL, 119 .osc_required = NULL, 120 .osc_optional = NULL, 121 .osc_positional = _main_positional, 122 .osc_invoke = &_main_invoke, 123 }; 124 125 static int 126 _main_invoke(const os_subcommand_t *osc, int argc, const char *argv[]) 127 { 128 return 0; 129 } 130 131 #pragma mark Help Subcommand 132 static int _help_invoke(const os_subcommand_t *osc, 133 int argc, 134 const char *argv[]); 135 136 static const os_subcommand_option_t _help_positional[] = { 137 [0] = { 138 .osco_version = OS_SUBCOMMAND_OPTION_VERSION, 139 .osco_flags = 0, 140 .osco_option = NULL, 141 .osco_argument_usage = "SUBCOMMAND", 142 .osco_argument_human = "The subcommand to query for help", 143 }, 144 OS_SUBCOMMAND_OPTION_TERMINATOR, 145 }; 146 147 static const os_subcommand_t __help_cmd = { 148 .osc_version = OS_SUBCOMMAND_VERSION, 149 .osc_flags = 0, 150 .osc_name = "help", 151 .osc_desc = "prints helpful information", 152 .osc_optstring = NULL, 153 .osc_options = NULL, 154 .osc_required = NULL, 155 .osc_optional = NULL, 156 .osc_positional = _help_positional, 157 .osc_invoke = &_help_invoke, 158 }; 159 160 static int 161 _help_invoke(const os_subcommand_t *osc, int argc, const char *argv[]) 162 { 163 int xit = -1; 164 const char *cmdname = NULL; 165 const os_subcommand_t *target = NULL; 166 FILE *f = stdout; 167 168 if (argc > 1) { 169 cmdname = argv[1]; 170 } 171 172 // Print usage information for the requested subcommand. 173 target = _os_subcommand_find(cmdname); 174 if (!target) { 175 // If it's a bogus subcommand, just print top-level usage. 176 fprintf(stderr, "unrecognized subcommand: %s\n", cmdname); 177 target = _main_cmd; 178 xit = EX_UNAVAILABLE; 179 } else { 180 xit = 0; 181 } 182 183 if (xit) { 184 f = stderr; 185 } 186 187 _os_subcommand_print_usage(target, f); 188 189 if (target == _main_cmd) { 190 _print_subcommand_list(_help_cmd, f); 191 } 192 193 return xit; 194 } 195 196 #pragma mark Utilities 197 static void 198 _print_header(FILE *f, const char *hdr, bool *already_done) 199 { 200 if (already_done && *already_done) { 201 return; 202 } 203 204 crfprintf_np(f, ""); 205 crfprintf_np(f, "%s:", hdr); 206 crfprintf_np(f, ""); 207 208 if (already_done) { 209 *already_done = true; 210 } 211 } 212 213 #pragma mark Module Routines 214 static char * 215 _os_subcommand_copy_option_spec_short(const os_subcommand_t *osc, 216 const os_subcommand_option_t *osco) 217 { 218 const struct option *opt = osco->osco_option; 219 char optbuff[64] = ""; 220 char argbuff[64] = ""; 221 char *final = NULL; 222 int ret = -1; 223 224 if (opt) { 225 snprintf(optbuff, sizeof(optbuff), "-%c", opt->val); 226 227 switch (opt->has_arg) { 228 case no_argument: 229 break; 230 case optional_argument: 231 snprintf(argbuff, sizeof(argbuff), "[%s]", 232 osco->osco_argument_usage); 233 break; 234 case required_argument: 235 snprintf(argbuff, sizeof(argbuff), "<%s>", 236 osco->osco_argument_usage); 237 break; 238 default: 239 __builtin_unreachable(); 240 } 241 242 if (!(osco->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_ENUM)) { 243 _stoupper(argbuff); 244 } 245 } else { 246 snprintf(optbuff, sizeof(optbuff), "%s", osco->osco_argument_usage); 247 if (!(osco->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_ENUM)) { 248 _stoupper(optbuff); 249 } 250 } 251 252 ret = asprintf(&final, "%s%s", optbuff, argbuff); 253 if (ret < 0) { 254 os_assert_zero(ret); 255 } 256 257 return final; 258 } 259 260 static char * 261 _os_subcommand_copy_option_spec_long(const os_subcommand_t *osc, 262 const os_subcommand_option_t *osco) 263 { 264 const struct option *opt = osco->osco_option; 265 char optbuff[64] = ""; 266 char argbuff[64] = ""; 267 char *final = NULL; 268 int ret = -1; 269 270 if (opt) { 271 snprintf(optbuff, sizeof(optbuff), "--%s", opt->name); 272 273 switch (opt->has_arg) { 274 case no_argument: 275 break; 276 case optional_argument: 277 snprintf(argbuff, sizeof(argbuff), "[=%s]", 278 osco->osco_argument_usage); 279 break; 280 case required_argument: 281 snprintf(argbuff, sizeof(argbuff), "=<%s>", 282 osco->osco_argument_usage); 283 break; 284 default: 285 __builtin_unreachable(); 286 } 287 288 if (!(osco->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_ENUM)) { 289 _stoupper(argbuff); 290 } 291 } else { 292 snprintf(optbuff, sizeof(optbuff), "%s", osco->osco_argument_usage); 293 if (!(osco->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_ENUM)) { 294 _stoupper(optbuff); 295 } 296 } 297 298 ret = asprintf(&final, "%s%s", optbuff, argbuff); 299 if (ret < 0) { 300 os_assert_zero(ret); 301 } 302 303 return final; 304 } 305 306 static char * 307 _os_subcommand_copy_option_spec(const os_subcommand_t *osc, 308 const os_subcommand_option_t *osco, os_subcommand_option_spec_fmt_t fmt) 309 { 310 int ret = -1; 311 char *spec = NULL; 312 char *__os_free spec_old = NULL; 313 314 switch (fmt) { 315 case OS_SUBCOMMAND_OPTION_SPEC_SHORT: 316 _os_subcommand_copy_option_spec_short(osc, osco); 317 break; 318 case OS_SUBCOMMAND_OPTION_SPEC_LONG: 319 _os_subcommand_copy_option_spec_long(osc, osco); 320 break; 321 case OS_SUBCOMMAND_OPTION_SPEC_COMBINED: 322 spec = _os_subcommand_copy_option_spec_long(osc, osco); 323 if (osco->osco_option) { 324 spec_old = spec; 325 326 ret = asprintf(&spec, "-%c | %s", osco->osco_option->val, spec); 327 if (ret < 0) { 328 os_assert_zero(ret); 329 } 330 } 331 332 break; 333 default: 334 __builtin_unreachable(); 335 } 336 337 return spec; 338 } 339 340 static char * 341 _os_subcommand_copy_usage_line(const os_subcommand_t *osc) 342 { 343 char *usage_line = NULL; 344 size_t i = 0; 345 const os_subcommand_option_t *osco_i = NULL; 346 const char *optional_spec = ""; 347 char subcmd_name[64]; 348 int ret = -1; 349 350 // The usage line does not enumerate all possible optional options, just the 351 // required options. If there are optional options, then display that but 352 // otherwise leave them to be described by more extensive usage information. 353 if (osc->osc_optional) { 354 optional_spec = " [options]"; 355 } 356 357 if (osc == _main_cmd) { 358 strlcpy(subcmd_name, "", sizeof(subcmd_name)); 359 } else { 360 snprintf(subcmd_name, sizeof(subcmd_name), " %s", osc->osc_name); 361 } 362 363 ret = asprintf(&usage_line, "%s%s%s", 364 getprogname(), subcmd_name, optional_spec); 365 if (ret < 0) { 366 os_assert_zero(ret); 367 } 368 369 i = 0; 370 OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, required, i) { 371 char *__os_free usage_line_old = NULL; 372 char *__os_free osco_spec = NULL; 373 374 usage_line_old = usage_line; 375 376 osco_spec = _os_subcommand_copy_option_spec_long(osc, osco_i); 377 ret = asprintf(&usage_line, "%s %s", usage_line, osco_spec); 378 if (ret < 0) { 379 os_assert_zero(ret); 380 } 381 } 382 383 i = 0; 384 OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, positional, i) { 385 char *__os_free usage_line_old = NULL; 386 char *__os_free osco_spec = NULL; 387 const char *braces[] = { 388 "<", 389 ">", 390 }; 391 392 if (osco_i->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL_POS) { 393 braces[0] = "["; 394 braces[1] = "]"; 395 } 396 397 usage_line_old = usage_line; 398 399 osco_spec = _os_subcommand_copy_option_spec_long(osc, osco_i); 400 ret = asprintf(&usage_line, "%s %s%s%s", 401 usage_line, braces[0], osco_spec, braces[1]); 402 if (ret < 0) { 403 os_assert_zero(ret); 404 } 405 } 406 407 if (osc == _main_cmd && osc != _internal_main_cmd) { 408 // Always include the positional subcommand when printing usage for the 409 // main subcommand. We do not expect it to be specified in a user- 410 // provided main subcommand. 411 const os_subcommand_option_t *subopt = &_main_positional[0]; 412 char *__os_free usage_line_old = NULL; 413 char *__os_free osco_spec = NULL; 414 415 usage_line_old = usage_line; 416 417 osco_spec = _os_subcommand_copy_option_spec_long(osc, subopt); 418 ret = asprintf(&usage_line, "%s <%s>", usage_line, osco_spec); 419 if (ret < 0) { 420 os_assert_zero(ret); 421 } 422 } 423 424 return usage_line; 425 } 426 427 static void 428 _os_subcommand_print_option_usage(const os_subcommand_t *osc, 429 const os_subcommand_option_t *osco, FILE *f) 430 { 431 char *__os_free opt_spec = NULL; 432 ssize_t initpad = -CTL_OUTPUT_OPTARG_INDENT; 433 434 opt_spec = _os_subcommand_copy_option_spec(osc, osco, 435 OS_SUBCOMMAND_OPTION_SPEC_COMBINED); 436 fprintf(f, " %-24s ", opt_spec); 437 438 // If the usage specifier is long, start the description on the next line. 439 if (strlen(opt_spec) >= CTL_OUTPUT_OPTARG_OVERFLOW) { 440 initpad = CTL_OUTPUT_OPTARG_INDENT; 441 crfprintf_np(f, ""); 442 } 443 444 wfprintf_np(f, initpad, CTL_OUTPUT_OPTARG_INDENT, _ttys->ts_cols, "%s", 445 osco->osco_argument_human); 446 } 447 448 static void 449 _os_subcommand_print_help_line(const os_subcommand_t *osc, FILE *f) 450 { 451 ssize_t initpad = -CTL_OUTPUT_OPTARG_INDENT; 452 453 fprintf(f, " %-24s ", osc->osc_name); 454 455 // If the usage specifier is long, start the description on the next line. 456 if (strlen(osc->osc_name) >= CTL_OUTPUT_OPTARG_OVERFLOW) { 457 initpad = CTL_OUTPUT_OPTARG_INDENT; 458 crfprintf_np(f, ""); 459 } 460 461 wfprintf_np(f, initpad, CTL_OUTPUT_OPTARG_INDENT, _ttys->ts_cols, "%s", 462 osc->osc_desc); 463 } 464 465 static void 466 _os_subcommand_print_usage(const os_subcommand_t *osc, FILE *f) 467 { 468 size_t i = 0; 469 const os_subcommand_option_t *osco_i = NULL; 470 char *__os_free usage_line = NULL; 471 bool header_printed = false; 472 473 usage_line = _os_subcommand_copy_usage_line(osc); 474 475 wfprintf_np(f, 0, 4, _ttys->ts_cols, "USAGE:"); 476 crfprintf_np(f, ""); 477 wfprintf_np(f, 4, 4, _ttys->ts_cols, "%s", usage_line); 478 479 if (osc->osc_long_desc) { 480 // The long description gets printed in its own paragraph. 481 _print_header(f, "DESCRIPTION", NULL); 482 wfprintf_np(f, 4, 4, _ttys->ts_cols, "%s", osc->osc_long_desc); 483 } else if (osc->osc_desc) { 484 // The short description gets printed on the same line. 485 crfprintf_np(f, ""); 486 wfprintf_np(f, 0, 4, _ttys->ts_cols, "DESCRIPTION: %s", 487 osc->osc_desc); 488 } 489 490 if (osc->osc_required || osc->osc_positional || osc == _main_cmd) { 491 i = 0; 492 OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, required, i) { 493 _print_header(f, "REQUIRED", &header_printed); 494 _os_subcommand_print_option_usage(osc, osco_i, f); 495 } 496 497 i = 0; 498 OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, positional, i) { 499 _print_header(f, "REQUIRED", &header_printed); 500 501 if (osco_i->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL_POS) { 502 continue; 503 } 504 505 _os_subcommand_print_option_usage(osc, osco_i, f); 506 } 507 508 if (osc == _main_cmd && osc != _internal_main_cmd) { 509 // We do not expect the user's main command to specify that a 510 // subcommand must follow, so always defer to ours. 511 _print_header(f, "REQUIRED", &header_printed); 512 _os_subcommand_print_option_usage(osc, &_main_positional[0], f); 513 } 514 } 515 516 header_printed = false; 517 518 if (osc->osc_optional || osc->osc_positional) { 519 i = 0; 520 OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, optional, i) { 521 _print_header(f, "OPTIONAL", &header_printed); 522 _os_subcommand_print_option_usage(osc, osco_i, f); 523 } 524 525 i = 0; 526 OS_SUBCOMMAND_OPTIONS_FOREACH(osco_i, osc, positional, i) { 527 if (osco_i->osco_flags & OS_SUBCOMMAND_OPTION_FLAG_OPTIONAL_POS) { 528 _print_header(f, "OPTIONAL", &header_printed); 529 _os_subcommand_print_option_usage(osc, osco_i, f); 530 } 531 } 532 } 533 } 534 535 static const os_subcommand_t * 536 _os_subcommand_find(const char *name) 537 { 538 const os_subcommand_t **oscip = NULL; 539 540 if (!name) { 541 return _main_cmd; 542 } 543 544 if (strcmp(_help_cmd->osc_name, name) == 0) { 545 return &__help_cmd; 546 } 547 548 LINKER_SET_FOREACH(oscip, const os_subcommand_t **, SUBCOMMAND_LINKER_SET) { 549 const os_subcommand_t *osci = *oscip; 550 551 if (osci->osc_flags & OS_SUBCOMMAND_FLAG_MAIN) { 552 // The main subcommand cannot be invoked directly. 553 continue; 554 } 555 556 if (strcmp(osci->osc_name, name) == 0) { 557 return osci; 558 } 559 } 560 561 return NULL; 562 } 563 564 static int 565 _os_subcommand_be_helpful(const os_subcommand_t *osc, 566 int argc, const char *argv[]) 567 { 568 int res = 0; 569 570 if (osc->osc_flags & OS_SUBCOMMAND_FLAG_HELPFUL) { 571 if (argc == 1) { 572 _os_subcommand_print_usage(osc, stdout); 573 res = 1; 574 goto __out; 575 } 576 } 577 578 if (osc->osc_flags & OS_SUBCOMMAND_FLAG_HELPFUL_FIRST_OPTION) { 579 if (argc == 2 && (strcmp(argv[1], "help") == 0 || 580 strcmp(argv[1], "-h") == 0 || 581 strcmp(argv[1], "-help") == 0 || 582 strcmp(argv[1], "--help") == 0)) { 583 _os_subcommand_print_usage(osc, stdout); 584 res = 1; 585 goto __out; 586 } 587 } 588 589 __out: 590 return res; 591 } 592 593 static void 594 _print_subcommand_list(const os_subcommand_t *osc, FILE *f) 595 { 596 const os_subcommand_t **oscip = NULL; 597 bool header_printed = false; 598 599 LINKER_SET_FOREACH(oscip, const os_subcommand_t **, 600 SUBCOMMAND_LINKER_SET) { 601 const os_subcommand_t *osci = *oscip; 602 603 _print_header(f, "SUBCOMMANDS", &header_printed); 604 605 if ((osci->osc_flags & OS_SUBCOMMAND_FLAG_MAIN) || 606 (osci->osc_flags & OS_SUBCOMMAND_FLAG_HIDDEN)) { 607 continue; 608 } 609 610 _os_subcommand_print_help_line(osci, f); 611 } 612 613 // Print the help subcommand last. 614 _os_subcommand_print_help_line(osc, f); 615 } 616 617 #pragma mark API 618 int 619 os_subcommand_main(int argc, const char *argv[], 620 os_subcommand_main_flags_t flags) 621 { 622 int xit = -1; 623 const char *cmdname = NULL; 624 const os_subcommand_t *osc = NULL; 625 const os_subcommand_t **oscip = NULL; 626 627 _init_column_count(); 628 629 // Find the main subcommand if any exists. Otherwise we'll just use our pre- 630 // canned main subcommand. 631 LINKER_SET_FOREACH(oscip, const os_subcommand_t **, SUBCOMMAND_LINKER_SET) { 632 osc = *oscip; 633 if (osc->osc_flags & OS_SUBCOMMAND_FLAG_MAIN) { 634 _main_cmd = osc; 635 break; 636 } 637 } 638 639 osc = NULL; 640 641 // See if we just need to print help for the main command. 642 if (_os_subcommand_be_helpful(_main_cmd, argc, argv)) { 643 _print_subcommand_list(_help_cmd, stdout); 644 xit = 0; 645 goto __out; 646 } 647 648 // Invoke the main subcommand to snarf any global options. Our default 649 // implementation does nothing and just returns 0. 650 xit = _main_cmd->osc_invoke(_main_cmd, argc, argv); 651 if (xit) { 652 goto __out; 653 } 654 655 // Advance argument pointer and make the subcommand argv[0]. 656 argc -= optind; 657 argv += optind; 658 cmdname = argv[0]; 659 660 if (argc < 1) { 661 os_subcommand_fprintf(NULL, stderr, "please provide a subcommand"); 662 xit = EX_USAGE; 663 goto __out; 664 } 665 666 osc = _os_subcommand_find(cmdname); 667 if (osc) { 668 if (osc->osc_flags & OS_SUBCOMMAND_FLAG_REQUIRE_ROOT) { 669 if (geteuid()) { 670 os_subcommand_fprintf(osc, stderr, 671 "subcommand requires root: %s", 672 cmdname); 673 xit = EX_NOPERM; 674 goto __out; 675 } 676 } 677 678 if (osc->osc_flags & OS_SUBCOMMAND_FLAG_TTYONLY) { 679 if (!isatty(STDOUT_FILENO) || !isatty(STDIN_FILENO)) { 680 os_subcommand_fprintf(osc, stderr, 681 "subcommand requires a tty: %s", 682 cmdname); 683 xit = EX_UNAVAILABLE; 684 goto __out; 685 } 686 } 687 688 if (_os_subcommand_be_helpful(osc, argc, argv)) { 689 xit = 0; 690 goto __out; 691 } 692 693 xit = osc->osc_invoke(osc, argc, argv); 694 } else { 695 os_subcommand_fprintf(NULL, stderr, "unknown subcommand: %s", cmdname); 696 xit = EX_USAGE; 697 } 698 699 __out: 700 if (xit == EX_USAGE) { 701 if (!osc) { 702 // If we couldn't find the subcommand, then print the list of known 703 // subcommands. 704 _print_subcommand_list(_help_cmd, stderr); 705 } else { 706 _os_subcommand_print_usage(osc, stderr); 707 } 708 } 709 710 return xit; 711 } 712 713 void 714 os_subcommand_fprintf(const os_subcommand_t *osc, FILE *f, 715 const char *fmt, ...) 716 { 717 va_list ap; 718 719 va_start(ap, fmt); 720 vcrfprintf_np(f, fmt, ap); 721 va_end(ap); 722 } 723 724 void 725 os_subcommand_vfprintf(const os_subcommand_t *osc, FILE *f, 726 const char *fmt, va_list ap) 727 { 728 if (!osc || (osc->osc_flags & OS_SUBCOMMAND_FLAG_MAIN)) { 729 fprintf(f, "%s: ", getprogname()); 730 } else { 731 fprintf(f, "%s-%s: ", getprogname(), osc->osc_name); 732 } 733 734 vcrfprintf_np(f, fmt, ap); 735 }