blob: 2506019bac0bc8cd25afafd8b2550fd1e668ad4d [file] [log] [blame]
Radek Krejcied5acc52019-04-25 15:57:04 +02001/**
2 * @file completion.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief libyang's yanglint tool auto completion
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * https://opensource.org/licenses/BSD-3-Clause
13 */
14
Radek Krejci535ea9f2020-05-29 16:01:05 +020015#define _GNU_SOURCE
Radek Krejcif8dc59a2020-11-25 13:47:44 +010016#define _POSIX_C_SOURCE 200809L /* strdup */
Radek Krejcied5acc52019-04-25 15:57:04 +020017
Radek Krejcied5acc52019-04-25 15:57:04 +020018#include <errno.h>
Radek Krejci535ea9f2020-05-29 16:01:05 +020019#include <stdint.h>
20#include <stdio.h>
21#include <stdlib.h>
Radek Krejcied5acc52019-04-25 15:57:04 +020022#include <string.h>
23
Radek Krejcied5acc52019-04-25 15:57:04 +020024#include "libyang.h"
25
Radek Krejcie9f13b12020-11-09 17:42:04 +010026#include "cmd.h"
27#include "common.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020028#include "linenoise/linenoise.h"
29
Radek Krejcie9f13b12020-11-09 17:42:04 +010030/* from the main.c */
Radek Krejcied5acc52019-04-25 15:57:04 +020031extern struct ly_ctx *ctx;
32
33static void
34get_cmd_completion(const char *hint, char ***matches, unsigned int *match_count)
35{
36 int i;
37 void *p;
38
39 *match_count = 0;
40 *matches = NULL;
41
42 for (i = 0; commands[i].name; i++) {
43 if (!strncmp(hint, commands[i].name, strlen(hint))) {
44 ++(*match_count);
45 p = realloc(*matches, *match_count * sizeof **matches);
46 if (!p) {
Radek Krejcie9f13b12020-11-09 17:42:04 +010047 YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
Radek Krejcied5acc52019-04-25 15:57:04 +020048 return;
49 }
50 *matches = p;
Radek Krejcie9f13b12020-11-09 17:42:04 +010051 (*matches)[*match_count - 1] = strdup(commands[i].name);
Radek Krejcied5acc52019-04-25 15:57:04 +020052 }
53 }
54}
55
56static int
57last_is_opt(const char *hint)
58{
59 const char *ptr;
60
61 /* last is option */
62 if (hint[0] == '-') {
63 return 1;
64 }
65
66 do {
67 --hint;
68 } while (hint[0] == ' ');
69
70 /* last is option argument */
71 ptr = strrchr(hint, ' ');
72 if (ptr) {
73 ++ptr;
74 if (ptr[0] == '-') {
75 return 1;
76 }
77 }
78
79 return 0;
80}
81
82static void
83get_model_completion(const char *hint, char ***matches, unsigned int *match_count)
84{
Michal Vaskofd69e1d2020-07-03 11:57:17 +020085 LY_ARRAY_COUNT_TYPE u;
Radek Krejcied5acc52019-04-25 15:57:04 +020086 uint32_t idx = 0;
87 const struct lys_module *module;
88 void *p;
89
90 *match_count = 0;
91 *matches = NULL;
92
93 while ((module = ly_ctx_get_module_iter(ctx, &idx))) {
94 if (!strncmp(hint, module->name, strlen(hint))) {
95 ++(*match_count);
96 p = realloc(*matches, *match_count * sizeof **matches);
97 if (!p) {
Radek Krejcie9f13b12020-11-09 17:42:04 +010098 YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
Radek Krejcied5acc52019-04-25 15:57:04 +020099 return;
100 }
101 *matches = p;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100102 (*matches)[*match_count - 1] = strdup(module->name);
Radek Krejcied5acc52019-04-25 15:57:04 +0200103 }
104
105 LY_ARRAY_FOR(module->parsed->includes, u) {
106 if (!strncmp(hint, module->parsed->includes[u].submodule->name, strlen(hint))) {
107 ++(*match_count);
108 p = realloc(*matches, *match_count * sizeof **matches);
109 if (!p) {
Radek Krejcie9f13b12020-11-09 17:42:04 +0100110 YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
Radek Krejcied5acc52019-04-25 15:57:04 +0200111 return;
112 }
113 *matches = p;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100114 (*matches)[*match_count - 1] = strdup(module->parsed->includes[u].submodule->name);
Radek Krejcied5acc52019-04-25 15:57:04 +0200115 }
116 }
117 }
118}
119
120void
121complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc)
122{
123 char **matches = NULL;
124 unsigned int match_count = 0, i;
125
126 if (!strncmp(buf, "add ", 4)) {
127 linenoisePathCompletion(buf, hint, lc);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100128 } else if ((!strncmp(buf, "searchpath ", 11) || !strncmp(buf, "data ", 5) ||
129 !strncmp(buf, "config ", 7) || !strncmp(buf, "filter ", 7) ||
130 !strncmp(buf, "xpath ", 6) || !strncmp(buf, "clear ", 6)) && !last_is_opt(hint)) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200131 linenoisePathCompletion(buf, hint, lc);
132 } else if ((!strncmp(buf, "print ", 6) || !strncmp(buf, "feature ", 8)) && !last_is_opt(hint)) {
133 get_model_completion(hint, &matches, &match_count);
134 } else if (!strchr(buf, ' ') && hint[0]) {
135 get_cmd_completion(hint, &matches, &match_count);
136 }
137
138 for (i = 0; i < match_count; ++i) {
139 linenoiseAddCompletion(lc, matches[i]);
140 free(matches[i]);
141 }
142 free(matches);
143}