| /* Routines for handling XML generic OS data provided by target. |
| |
| Copyright (C) 2008-2024 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.reset (new struct 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); |
| } |
| |
| void _initialize_osdata (); |
| void |
| _initialize_osdata () |
| { |
| add_info ("os", info_osdata_command, |
| _("Show OS data ARG.")); |
| } |