| /* Routines for handling XML generic OS data provided by target. | |
| Copyright (C) 2008-2025 Free Software Foundation, Inc. | |
| This file is part of GDB. | |
| This program is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
| #include "target.h" | |
| #include "xml-support.h" | |
| #include "osdata.h" | |
| #include "ui-out.h" | |
| #include "cli/cli-cmds.h" | |
| #if !defined(HAVE_LIBEXPAT) | |
| std::unique_ptr<osdata> | |
| osdata_parse (const char *xml) | |
| { | |
| static int have_warned; | |
| if (!have_warned) | |
| { | |
| have_warned = 1; | |
| warning (_("Can not parse XML OS data; XML support was disabled " | |
| "at compile time")); | |
| } | |
| return NULL; | |
| } | |
| #else /* HAVE_LIBEXPAT */ | |
| /* Internal parsing data passed to all XML callbacks. */ | |
| struct osdata_parsing_data | |
| { | |
| std::unique_ptr<struct osdata> osdata; | |
| std::string property_name; | |
| }; | |
| /* Handle the start of a <osdata> element. */ | |
| static void | |
| osdata_start_osdata (struct gdb_xml_parser *parser, | |
| const struct gdb_xml_element *element, | |
| void *user_data, | |
| std::vector<gdb_xml_value> &attributes) | |
| { | |
| struct osdata_parsing_data *data = (struct osdata_parsing_data *) user_data; | |
| if (data->osdata != NULL) | |
| gdb_xml_error (parser, _("Seen more than on osdata element")); | |
| char *type = (char *) xml_find_attribute (attributes, "type")->value.get (); | |
| data->osdata = std::make_unique<osdata> (std::string (type)); | |
| } | |
| /* Handle the start of a <item> element. */ | |
| static void | |
| osdata_start_item (struct gdb_xml_parser *parser, | |
| const struct gdb_xml_element *element, | |
| void *user_data, | |
| std::vector<gdb_xml_value> &attributes) | |
| { | |
| struct osdata_parsing_data *data = (struct osdata_parsing_data *) user_data; | |
| data->osdata->items.emplace_back (); | |
| } | |
| /* Handle the start of a <column> element. */ | |
| static void | |
| osdata_start_column (struct gdb_xml_parser *parser, | |
| const struct gdb_xml_element *element, | |
| void *user_data, | |
| std::vector<gdb_xml_value> &attributes) | |
| { | |
| struct osdata_parsing_data *data = (struct osdata_parsing_data *) user_data; | |
| const char *name | |
| = (const char *) xml_find_attribute (attributes, "name")->value.get (); | |
| data->property_name.assign (name); | |
| } | |
| /* Handle the end of a <column> element. */ | |
| static void | |
| osdata_end_column (struct gdb_xml_parser *parser, | |
| const struct gdb_xml_element *element, | |
| void *user_data, const char *body_text) | |
| { | |
| osdata_parsing_data *data = (struct osdata_parsing_data *) user_data; | |
| struct osdata *osdata = data->osdata.get (); | |
| osdata_item &item = osdata->items.back (); | |
| item.columns.emplace_back (std::move (data->property_name), | |
| std::string (body_text)); | |
| } | |
| /* The allowed elements and attributes for OS data object. | |
| The root element is a <osdata>. */ | |
| const struct gdb_xml_attribute column_attributes[] = { | |
| { "name", GDB_XML_AF_NONE, NULL, NULL }, | |
| { NULL, GDB_XML_AF_NONE, NULL, NULL } | |
| }; | |
| const struct gdb_xml_element item_children[] = { | |
| { "column", column_attributes, NULL, | |
| GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL, | |
| osdata_start_column, osdata_end_column }, | |
| { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } | |
| }; | |
| const struct gdb_xml_attribute osdata_attributes[] = { | |
| { "type", GDB_XML_AF_NONE, NULL, NULL }, | |
| { NULL, GDB_XML_AF_NONE, NULL, NULL } | |
| }; | |
| const struct gdb_xml_element osdata_children[] = { | |
| { "item", NULL, item_children, | |
| GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL, | |
| osdata_start_item, NULL }, | |
| { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } | |
| }; | |
| const struct gdb_xml_element osdata_elements[] = { | |
| { "osdata", osdata_attributes, osdata_children, | |
| GDB_XML_EF_NONE, osdata_start_osdata, NULL }, | |
| { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } | |
| }; | |
| std::unique_ptr<osdata> | |
| osdata_parse (const char *xml) | |
| { | |
| osdata_parsing_data data; | |
| if (gdb_xml_parse_quick (_("osdata"), "osdata.dtd", | |
| osdata_elements, xml, &data) == 0) | |
| { | |
| /* Parsed successfully, don't need to delete the result. */ | |
| return std::move (data.osdata); | |
| } | |
| return NULL; | |
| } | |
| #endif | |
| std::unique_ptr<osdata> | |
| get_osdata (const char *type) | |
| { | |
| std::unique_ptr<osdata> osdata; | |
| std::optional<gdb::char_vector> xml = target_get_osdata (type); | |
| if (xml) | |
| { | |
| if ((*xml)[0] == '\0') | |
| { | |
| if (type) | |
| warning (_("Empty data returned by target. Wrong osdata type?")); | |
| else | |
| warning (_("Empty type list returned by target. No type data?")); | |
| } | |
| else | |
| osdata = osdata_parse (xml->data ()); | |
| } | |
| if (osdata == NULL) | |
| error (_("Can not fetch data now.")); | |
| return osdata; | |
| } | |
| const std::string * | |
| get_osdata_column (const osdata_item &item, const char *name) | |
| { | |
| for (const osdata_column &col : item.columns) | |
| if (col.name == name) | |
| return &col.value; | |
| return NULL; | |
| } | |
| void | |
| info_osdata (const char *type) | |
| { | |
| struct ui_out *uiout = current_uiout; | |
| struct osdata_item *last = NULL; | |
| int ncols = 0; | |
| int col_to_skip = -1; | |
| if (type == NULL) | |
| type = ""; | |
| std::unique_ptr<osdata> osdata = get_osdata (type); | |
| int nrows = osdata->items.size (); | |
| if (*type == '\0' && nrows == 0) | |
| error (_("Available types of OS data not reported.")); | |
| if (!osdata->items.empty ()) | |
| { | |
| last = &osdata->items.back (); | |
| ncols = last->columns.size (); | |
| /* As a special case, scan the listing of available data types | |
| for a column named "Title", and only include it with MI | |
| output; this column's normal use is for titles for interface | |
| elements like menus, and it clutters up CLI output. */ | |
| if (*type == '\0' && !uiout->is_mi_like_p ()) | |
| { | |
| for (int ix = 0; ix < last->columns.size (); ix++) | |
| { | |
| if (last->columns[ix].name == "Title") | |
| col_to_skip = ix; | |
| } | |
| /* Be sure to reduce the total column count, otherwise | |
| internal errors ensue. */ | |
| if (col_to_skip >= 0) | |
| --ncols; | |
| } | |
| } | |
| ui_out_emit_table table_emitter (uiout, ncols, nrows, "OSDataTable"); | |
| /* With no columns/items, we just output an empty table, but we | |
| still output the table. This matters for MI. */ | |
| if (ncols == 0) | |
| return; | |
| if (last != NULL && !last->columns.empty ()) | |
| { | |
| for (int ix = 0; ix < last->columns.size (); ix++) | |
| { | |
| char col_name[32]; | |
| if (ix == col_to_skip) | |
| continue; | |
| snprintf (col_name, 32, "col%d", ix); | |
| uiout->table_header (10, ui_left, | |
| col_name, last->columns[ix].name.c_str ()); | |
| } | |
| } | |
| uiout->table_body (); | |
| if (nrows != 0) | |
| { | |
| for (const osdata_item &item : osdata->items) | |
| { | |
| { | |
| ui_out_emit_tuple tuple_emitter (uiout, "item"); | |
| for (int ix_cols = 0; ix_cols < item.columns.size (); ix_cols++) | |
| { | |
| char col_name[32]; | |
| if (ix_cols == col_to_skip) | |
| continue; | |
| snprintf (col_name, 32, "col%d", ix_cols); | |
| uiout->field_string (col_name, item.columns[ix_cols].value); | |
| } | |
| } | |
| uiout->text ("\n"); | |
| } | |
| } | |
| } | |
| static void | |
| info_osdata_command (const char *arg, int from_tty) | |
| { | |
| info_osdata (arg); | |
| } | |
| INIT_GDB_FILE (osdata) | |
| { | |
| add_info ("os", info_osdata_command, | |
| _("Show OS data ARG.")); | |
| } |