/** set_color builtin */ static int builtin_set_color(parser_t &parser, wchar_t **argv) { /** Variables used for parsing the argument list */ const struct woption long_options[] = { { L"background", required_argument, 0, 'b'}, { L"help", no_argument, 0, 'h' }, { L"bold", no_argument, 0, 'o' }, { L"underline", no_argument, 0, 'u' }, { L"version", no_argument, 0, 'v' }, { L"print-colors", no_argument, 0, 'c' }, { 0, 0, 0, 0 } }; const wchar_t *short_options = L"b:hvocu"; int argc = builtin_count_args(argv); /* Some code passes variables to set_color that don't exist, like $fish_user_whatever. As a hack, quietly return failure. */ if (argc <= 1) { return EXIT_FAILURE; } const wchar_t *bgcolor = NULL; bool bold = false, underline=false; int errret; /* Parse options to obtain the requested operation and the modifiers */ woptind = 0; while (1) { int c = wgetopt_long(argc, argv, short_options, long_options, 0); if (c == -1) { break; } switch (c) { case 0: break; case 'b': bgcolor = woptarg; break; case 'h': builtin_print_help(parser, argv[0], stdout_buffer); return STATUS_BUILTIN_OK; case 'o': bold = true; break; case 'u': underline = true; break; case 'c': print_colors(); return STATUS_BUILTIN_OK; case '?': return STATUS_BUILTIN_ERROR; } } /* Remaining arguments are foreground color */ std::vector<rgb_color_t> fgcolors; for (; woptind < argc; woptind++) { rgb_color_t fg = rgb_color_t(argv[woptind]); if (fg.is_none() || fg.is_ignore()) { append_format(stderr_buffer, _(L"%ls: Unknown color '%ls'\n"), argv[0], argv[woptind]); return STATUS_BUILTIN_ERROR; } fgcolors.push_back(fg); } if (fgcolors.empty() && bgcolor == NULL && !bold && !underline) { append_format(stderr_buffer, _(L"%ls: Expected an argument\n"), argv[0]); return STATUS_BUILTIN_ERROR; } // #1323: We may have multiple foreground colors. Choose the best one. // If we had no foreground coor, we'll get none(); if we have at least one we expect not-none const rgb_color_t fg = best_color(fgcolors, output_get_color_support()); assert(fgcolors.empty() || !(fg.is_none() || fg.is_ignore())); const rgb_color_t bg = rgb_color_t(bgcolor ? bgcolor : L""); if (bgcolor && (bg.is_none() || bg.is_ignore())) { append_format(stderr_buffer, _(L"%ls: Unknown color '%ls'\n"), argv[0], bgcolor); return STATUS_BUILTIN_ERROR; } /* Make sure that the term exists */ if (cur_term == NULL && setupterm(0, STDOUT_FILENO, &errret) == ERR) { append_format(stderr_buffer, _(L"%ls: Could not set up terminal\n"), argv[0]); return STATUS_BUILTIN_ERROR; } /* Test if we have at least basic support for setting fonts, colors and related bits - otherwise just give up... */ if (! exit_attribute_mode) { return STATUS_BUILTIN_ERROR; } /* Save old output function so we can restore it */ int (* const saved_writer_func)(char) = output_get_writer(); /* Set our output function, which writes to a std::string */ builtin_set_color_output.clear(); output_set_writer(set_color_builtin_outputter); if (bold) { if (enter_bold_mode) writembs(tparm(enter_bold_mode)); } if (underline) { if (enter_underline_mode) writembs(enter_underline_mode); } if (bgcolor != NULL) { if (bg.is_normal()) { write_color(rgb_color_t::black(), false /* not is_fg */); writembs(tparm(exit_attribute_mode)); } } if (! fg.is_none()) { if (fg.is_normal() || fg.is_reset()) { write_color(rgb_color_t::black(), true /* is_fg */); writembs(tparm(exit_attribute_mode)); } else { write_color(fg, true /* is_fg */); } } if (bgcolor != NULL) { if (! bg.is_normal() && ! bg.is_reset()) { write_color(bg, false /* not is_fg */); } } /* Restore saved writer function */ output_set_writer(saved_writer_func); /* Output the collected string */ stdout_buffer.append(str2wcstring(builtin_set_color_output)); builtin_set_color_output.clear(); return STATUS_BUILTIN_OK; }
/// set_color builtin. int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) { // By the time this is called we should have initialized the curses subsystem. assert(curses_initialized); // Hack in missing italics and dim capabilities omitted from MacOS xterm-256color terminfo // Helps Terminal.app/iTerm #if __APPLE__ const auto term_prog = parser.vars().get(L"TERM_PROGRAM"); if (!term_prog.missing_or_empty() && (term_prog->as_string() == L"Apple_Terminal" || term_prog->as_string() == L"iTerm.app")) { const auto term = parser.vars().get(L"TERM"); if (!term.missing_or_empty() && (term->as_string() == L"xterm-256color")) { enter_italics_mode = sitm_esc; exit_italics_mode = ritm_esc; enter_dim_mode = dim_esc; } } #endif // Variables used for parsing the argument list. wchar_t *cmd = argv[0]; int argc = builtin_count_args(argv); // Some code passes variables to set_color that don't exist, like $fish_user_whatever. As a // hack, quietly return failure. if (argc <= 1) { return EXIT_FAILURE; } const wchar_t *bgcolor = NULL; bool bold = false, underline = false, italics = false, dim = false, reverse = false; // Parse options to obtain the requested operation and the modifiers. int opt; wgetopter_t w; while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) { switch (opt) { case 'b': { bgcolor = w.woptarg; break; } case 'h': { builtin_print_help(parser, streams, argv[0], streams.out); return STATUS_CMD_OK; } case 'o': { bold = true; break; } case 'i': { italics = true; break; } case 'd': { dim = true; break; } case 'r': { reverse = true; break; } case 'u': { underline = true; break; } case 'c': { print_colors(streams); return STATUS_CMD_OK; } case ':': { builtin_missing_argument(parser, streams, cmd, argv[w.woptind - 1]); return STATUS_INVALID_ARGS; } case '?': { return STATUS_INVALID_ARGS; } default: { DIE("unexpected retval from wgetopt_long"); break; } } } // Remaining arguments are foreground color. std::vector<rgb_color_t> fgcolors; for (; w.woptind < argc; w.woptind++) { rgb_color_t fg = rgb_color_t(argv[w.woptind]); if (fg.is_none()) { streams.err.append_format(_(L"%ls: Unknown color '%ls'\n"), argv[0], argv[w.woptind]); return STATUS_INVALID_ARGS; } fgcolors.push_back(fg); } if (fgcolors.empty() && bgcolor == NULL && !bold && !underline && !italics && !dim && !reverse) { streams.err.append_format(_(L"%ls: Expected an argument\n"), argv[0]); return STATUS_INVALID_ARGS; } // #1323: We may have multiple foreground colors. Choose the best one. If we had no foreground // color, we'll get none(); if we have at least one we expect not-none. const rgb_color_t fg = best_color(fgcolors, output_get_color_support()); assert(fgcolors.empty() || !fg.is_none()); const rgb_color_t bg = rgb_color_t(bgcolor ? bgcolor : L""); if (bgcolor && bg.is_none()) { streams.err.append_format(_(L"%ls: Unknown color '%ls'\n"), argv[0], bgcolor); return STATUS_INVALID_ARGS; } // Test if we have at least basic support for setting fonts, colors and related bits - otherwise // just give up... if (cur_term == NULL || !exit_attribute_mode) { return STATUS_CMD_ERROR; } outputter_t outp; if (bold && enter_bold_mode) { writembs_nofail(outp, tparm((char *)enter_bold_mode)); } if (underline && enter_underline_mode) { writembs_nofail(outp, enter_underline_mode); } if (italics && enter_italics_mode) { writembs_nofail(outp, enter_italics_mode); } if (dim && enter_dim_mode) { writembs_nofail(outp, enter_dim_mode); } if (reverse && enter_reverse_mode) { writembs_nofail(outp, enter_reverse_mode); } else if (reverse && enter_standout_mode) { writembs_nofail(outp, enter_standout_mode); } if (bgcolor != NULL && bg.is_normal()) { writembs_nofail(outp, tparm((char *)exit_attribute_mode)); } if (!fg.is_none()) { if (fg.is_normal() || fg.is_reset()) { writembs_nofail(outp, tparm((char *)exit_attribute_mode)); } else { if (!outp.write_color(fg, true /* is_fg */)) { // We need to do *something* or the lack of any output messes up // when the cartesian product here would make "foo" disappear: // $ echo (set_color foo)bar outp.set_color(rgb_color_t::reset(), rgb_color_t::none()); } } } if (bgcolor != NULL && !bg.is_normal() && !bg.is_reset()) { outp.write_color(bg, false /* not is_fg */); } // Output the collected string. streams.out.append(str2wcstring(outp.contents())); return STATUS_CMD_OK; }