From: Jan Kiszka <jan.kis...@siemens.com>

Implement monitor command line completion for device tree paths. The
first user is device_del.

Signed-off-by: Jan Kiszka <jan.kis...@siemens.com>
---
 hw/qdev.c       |   50 ++++++++++++++++++++++----------
 hw/qdev.h       |    2 +
 monitor.c       |   85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-monitor.hx |    2 +-
 4 files changed, 123 insertions(+), 16 deletions(-)

diff --git a/hw/qdev.c b/hw/qdev.c
index db005ce..7db839f 100644
--- a/hw/qdev.c
+++ b/hw/qdev.c
@@ -39,7 +39,7 @@ DeviceInfo *device_info_list;
 
 static BusState *qbus_find_recursive(BusState *bus, const char *name,
                                      const BusInfo *info);
-static BusState *qbus_find(const char *path);
+static BusState *qbus_find_internal(const char *path, bool report_errors);
 
 /* Register a new device type.  */
 void qdev_register(DeviceInfo *info)
@@ -217,7 +217,7 @@ DeviceState *qdev_device_add(QemuOpts *opts)
     /* find bus */
     path = qemu_opt_get(opts, "bus");
     if (path != NULL) {
-        bus = qbus_find(path);
+        bus = qbus_find_internal(path, true);
         if (!bus) {
             return NULL;
         }
@@ -575,7 +575,7 @@ static DeviceState *qbus_find_dev(BusState *bus, const char 
*elem)
     return NULL;
 }
 
-static BusState *qbus_find(const char *path)
+static BusState *qbus_find_internal(const char *path, bool report_errors)
 {
     DeviceState *dev, *next_dev;
     BusState *bus;
@@ -593,7 +593,9 @@ static BusState *qbus_find(const char *path)
         }
         bus = qbus_find_recursive(main_system_bus, elem, NULL);
         if (!bus) {
-            qerror_report(QERR_BUS_NOT_FOUND, elem);
+            if (report_errors) {
+                qerror_report(QERR_BUS_NOT_FOUND, elem);
+            }
             return NULL;
         }
         pos = len;
@@ -616,8 +618,10 @@ static BusState *qbus_find(const char *path)
         pos += len;
         dev = qbus_find_dev(bus, elem);
         if (!dev) {
-            qerror_report(QERR_DEVICE_NOT_FOUND, elem);
-            qbus_list_dev(bus);
+            if (report_errors) {
+                qerror_report(QERR_DEVICE_NOT_FOUND, elem);
+                qbus_list_dev(bus);
+            }
             return NULL;
         }
 
@@ -631,13 +635,17 @@ search_dev_bus:
              * one child bus accept it nevertheless */
             switch (dev->num_child_bus) {
             case 0:
-                qerror_report(QERR_DEVICE_NO_BUS, elem);
+                if (report_errors) {
+                    qerror_report(QERR_DEVICE_NO_BUS, elem);
+                }
                 return NULL;
             case 1:
                 return QTAILQ_FIRST(&dev->child_bus);
             default:
-                qerror_report(QERR_DEVICE_MULTIPLE_BUSSES, elem);
-                qbus_list_bus(dev);
+                if (report_errors) {
+                    qerror_report(QERR_DEVICE_MULTIPLE_BUSSES, elem);
+                    qbus_list_bus(dev);
+                }
                 return NULL;
             }
         }
@@ -659,14 +667,21 @@ search_dev_bus:
                     goto search_dev_bus;
                 }
             }
-            qerror_report(QERR_BUS_NOT_FOUND, elem);
-            qbus_list_bus(dev);
+            if (report_errors) {
+                qerror_report(QERR_BUS_NOT_FOUND, elem);
+                qbus_list_bus(dev);
+            }
             return NULL;
         }
     }
 }
 
-static DeviceState *qdev_find(const char *path)
+BusState *qbus_find(const char *path)
+{
+    return qbus_find_internal(path, false);
+}
+
+static DeviceState *qdev_find_internal(const char *path, bool report_errors)
 {
     const char *dev_name;
     DeviceState *dev;
@@ -686,7 +701,7 @@ static DeviceState *qdev_find(const char *path)
         bus_path = qemu_strdup(path);
         bus_path[dev_name - path] = 0;
 
-        bus = qbus_find(bus_path);
+        bus = qbus_find_internal(bus_path, report_errors);
         qemu_free(bus_path);
 
         if (!bus) {
@@ -695,13 +710,18 @@ static DeviceState *qdev_find(const char *path)
         }
     }
     dev = qbus_find_dev(bus, dev_name);
-    if (!dev) {
+    if (!dev && report_errors) {
         qerror_report(QERR_DEVICE_NOT_FOUND, dev_name);
         qbus_list_dev(bus);
     }
     return dev;
 }
 
+DeviceState *qdev_find(const char *path)
+{
+    return qdev_find_internal(path, false);
+}
+
 void qbus_create_inplace(BusState *bus, BusInfo *info,
                          DeviceState *parent, const char *name)
 {
@@ -863,7 +883,7 @@ int do_device_del(Monitor *mon, const QDict *qdict, QObject 
**ret_data)
     const char *path = qdict_get_str(qdict, "path");
     DeviceState *dev;
 
-    dev = qdev_find(path);
+    dev = qdev_find_internal(path, true);
     if (!dev) {
         qerror_report(QERR_DEVICE_NOT_FOUND, path);
         return -1;
diff --git a/hw/qdev.h b/hw/qdev.h
index 53f5565..b27d33b 100644
--- a/hw/qdev.h
+++ b/hw/qdev.h
@@ -165,6 +165,7 @@ void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, 
int n);
 CharDriverState *qdev_init_chardev(DeviceState *dev);
 
 BusState *qdev_get_parent_bus(DeviceState *dev);
+DeviceState *qdev_find(const char *path);
 
 /*** BUS API. ***/
 
@@ -172,6 +173,7 @@ void qbus_create_inplace(BusState *bus, BusInfo *info,
                          DeviceState *parent, const char *name);
 BusState *qbus_create(BusInfo *info, DeviceState *parent, const char *name);
 void qbus_free(BusState *bus);
+BusState *qbus_find(const char *path);
 
 #define FROM_QBUS(type, dev) DO_UPCAST(type, qbus, dev)
 
diff --git a/monitor.c b/monitor.c
index 4c95d7b..64de10a 100644
--- a/monitor.c
+++ b/monitor.c
@@ -64,6 +64,7 @@
  *
  * 'F'          filename
  * 'B'          block device name
+ * 'Q'          qdev device path
  * 's'          string (accept optional quote)
  * 'O'          option string of the form NAME=VALUE,...
  *              parsed according to QemuOptsList given by its name
@@ -3437,6 +3438,7 @@ static const mon_cmd_t *monitor_parse_command(Monitor 
*mon,
         switch(c) {
         case 'F':
         case 'B':
+        case 'Q':
         case 's':
             {
                 int ret;
@@ -3461,6 +3463,10 @@ static const mon_cmd_t *monitor_parse_command(Monitor 
*mon,
                         monitor_printf(mon, "%s: block device name expected\n",
                                        cmdname);
                         break;
+                    case 'Q':
+                        monitor_printf(mon, "%s: qdev device path expected\n",
+                                       cmdname);
+                        break;
                     default:
                         monitor_printf(mon, "%s: string expected\n", cmdname);
                         break;
@@ -3947,6 +3953,79 @@ static void block_completion_it(void *opaque, 
BlockDriverState *bs)
     }
 }
 
+static void add_qdev_completion(const char *parent_path, const char *name,
+                                bool trailing_slash)
+{
+    size_t parent_len = strlen(parent_path);
+    size_t name_len = strlen(name);
+    char *completion = qemu_malloc(parent_len + name_len + 2);
+
+    memcpy(completion, parent_path, parent_len);
+    memcpy(completion + parent_len, name, name_len);
+    if (trailing_slash) {
+        completion[parent_len + name_len] = '/';
+        completion[parent_len + name_len + 1] = 0;
+    } else {
+        completion[parent_len + name_len] = 0;
+    }
+    readline_add_completion(cur_mon->rs, completion);
+
+    qemu_free(completion);
+}
+
+static void qdev_completion(const char *input)
+{
+    size_t parent_len, name_len;
+    char *parent_path = NULL;
+    const char *p, *name;
+    DeviceState *dev;
+    BusState *bus;
+
+    p = strrchr(input, '/');
+    if (!p) {
+        if (*input == '\0') {
+            readline_add_completion(cur_mon->rs, "/");
+        }
+        return;
+    }
+
+    p++;
+    parent_path = qemu_strndup(input, p - input);
+    bus = qbus_find(parent_path);
+
+    if (bus) {
+        parent_len = strlen(parent_path);
+        name_len = strlen(bus->name);
+        if (bus->parent && strncmp(bus->name, p, strlen(p)) == 0 &&
+            (parent_len - 1 < name_len ||
+             strncmp(parent_path + parent_len - 1 - name_len, bus->name,
+                     name_len) != 0)) {
+            add_qdev_completion(parent_path, bus->name, true);
+        }
+        QTAILQ_FOREACH(dev, &bus->children, sibling) {
+            name = dev->id ? : dev->info->name;
+            if (strncmp(name, p, strlen(p)) == 0) {
+                add_qdev_completion(parent_path, name, false);
+                if (!QTAILQ_EMPTY(&dev->child_bus)) {
+                    add_qdev_completion(parent_path, name, true);
+                }
+            }
+        }
+    } else {
+        parent_path[strlen(parent_path) - 1] = 0;
+        dev = qdev_find(parent_path);
+        if (dev) {
+            parent_path[strlen(parent_path)] = '/';
+            QTAILQ_FOREACH(bus, &dev->child_bus, sibling) {
+                if (strncmp(bus->name, p, strlen(p)) == 0) {
+                    add_qdev_completion(parent_path, bus->name, true);
+                }
+            }
+        }
+    }
+    qemu_free(parent_path);
+}
+
 /* NOTE: this parser is an approximate form of the real command parser */
 static void parse_cmdline(const char *cmdline,
                          int *pnb_args, char **args)
@@ -4044,6 +4123,11 @@ static void monitor_find_completion(const char *cmdline)
             readline_set_completion_index(cur_mon->rs, strlen(str));
             bdrv_iterate(block_completion_it, (void *)str);
             break;
+        case 'Q':
+            /* qdev device path completion */
+            readline_set_completion_index(cur_mon->rs, strlen(str));
+            qdev_completion(str);
+            break;
         case 's':
             /* XXX: more generic ? */
             if (!strcmp(cmd->name, "info")) {
@@ -4122,6 +4206,7 @@ static int check_arg(const CmdArgs *cmd_args, QDict *args)
     switch (cmd_args->type) {
         case 'F':
         case 'B':
+        case 'Q':
         case 's':
             if (qobject_type(value) != QTYPE_QSTRING) {
                 qerror_report(QERR_INVALID_PARAMETER_TYPE, name, "string");
diff --git a/qemu-monitor.hx b/qemu-monitor.hx
index 754d71e..eb257a6 100644
--- a/qemu-monitor.hx
+++ b/qemu-monitor.hx
@@ -703,7 +703,7 @@ EQMP
 
     {
         .name       = "device_del",
-        .args_type  = "path:s",
+        .args_type  = "path:Q",
         .params     = "device",
         .help       = "remove device",
         .user_print = monitor_user_noop,
-- 
1.6.0.2


Reply via email to