blob: 4db17a043d77ad9db834fd41da48a40d185cfa85 [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 Krejcied5acc52019-04-25 15:57:04 +020016
Radek Krejcied5acc52019-04-25 15:57:04 +020017#include <errno.h>
Radek Krejci535ea9f2020-05-29 16:01:05 +020018#include <stdint.h>
19#include <stdio.h>
20#include <stdlib.h>
Radek Krejcied5acc52019-04-25 15:57:04 +020021#include <string.h>
22
Radek Krejcied5acc52019-04-25 15:57:04 +020023#include "libyang.h"
24
Radek Krejcie9f13b12020-11-09 17:42:04 +010025#include "cmd.h"
26#include "common.h"
Radek Krejci535ea9f2020-05-29 16:01:05 +020027#include "linenoise/linenoise.h"
28
Radek Krejcie9f13b12020-11-09 17:42:04 +010029/* from the main.c */
Radek Krejcied5acc52019-04-25 15:57:04 +020030extern struct ly_ctx *ctx;
31
32static void
33get_cmd_completion(const char *hint, char ***matches, unsigned int *match_count)
34{
35 int i;
36 void *p;
37
38 *match_count = 0;
39 *matches = NULL;
40
41 for (i = 0; commands[i].name; i++) {
42 if (!strncmp(hint, commands[i].name, strlen(hint))) {
43 ++(*match_count);
44 p = realloc(*matches, *match_count * sizeof **matches);
45 if (!p) {
Radek Krejcie9f13b12020-11-09 17:42:04 +010046 YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
Radek Krejcied5acc52019-04-25 15:57:04 +020047 return;
48 }
49 *matches = p;
Radek Krejcie9f13b12020-11-09 17:42:04 +010050 (*matches)[*match_count - 1] = strdup(commands[i].name);
Radek Krejcied5acc52019-04-25 15:57:04 +020051 }
52 }
53}
54
55static int
56last_is_opt(const char *hint)
57{
58 const char *ptr;
59
60 /* last is option */
61 if (hint[0] == '-') {
62 return 1;
63 }
64
65 do {
66 --hint;
67 } while (hint[0] == ' ');
68
69 /* last is option argument */
70 ptr = strrchr(hint, ' ');
71 if (ptr) {
72 ++ptr;
73 if (ptr[0] == '-') {
74 return 1;
75 }
76 }
77
78 return 0;
79}
80
81static void
82get_model_completion(const char *hint, char ***matches, unsigned int *match_count)
83{
Michal Vaskofd69e1d2020-07-03 11:57:17 +020084 LY_ARRAY_COUNT_TYPE u;
Radek Krejcied5acc52019-04-25 15:57:04 +020085 uint32_t idx = 0;
86 const struct lys_module *module;
87 void *p;
88
89 *match_count = 0;
90 *matches = NULL;
91
92 while ((module = ly_ctx_get_module_iter(ctx, &idx))) {
93 if (!strncmp(hint, module->name, strlen(hint))) {
94 ++(*match_count);
95 p = realloc(*matches, *match_count * sizeof **matches);
96 if (!p) {
Radek Krejcie9f13b12020-11-09 17:42:04 +010097 YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
Radek Krejcied5acc52019-04-25 15:57:04 +020098 return;
99 }
100 *matches = p;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100101 (*matches)[*match_count - 1] = strdup(module->name);
Radek Krejcied5acc52019-04-25 15:57:04 +0200102 }
103
104 LY_ARRAY_FOR(module->parsed->includes, u) {
105 if (!strncmp(hint, module->parsed->includes[u].submodule->name, strlen(hint))) {
106 ++(*match_count);
107 p = realloc(*matches, *match_count * sizeof **matches);
108 if (!p) {
Radek Krejcie9f13b12020-11-09 17:42:04 +0100109 YLMSG_E("Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
Radek Krejcied5acc52019-04-25 15:57:04 +0200110 return;
111 }
112 *matches = p;
Radek Krejcie9f13b12020-11-09 17:42:04 +0100113 (*matches)[*match_count - 1] = strdup(module->parsed->includes[u].submodule->name);
Radek Krejcied5acc52019-04-25 15:57:04 +0200114 }
115 }
116 }
117}
118
119void
120complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc)
121{
122 char **matches = NULL;
123 unsigned int match_count = 0, i;
124
125 if (!strncmp(buf, "add ", 4)) {
126 linenoisePathCompletion(buf, hint, lc);
Radek Krejcie9f13b12020-11-09 17:42:04 +0100127 } else if ((!strncmp(buf, "searchpath ", 11) || !strncmp(buf, "data ", 5) ||
128 !strncmp(buf, "config ", 7) || !strncmp(buf, "filter ", 7) ||
129 !strncmp(buf, "xpath ", 6) || !strncmp(buf, "clear ", 6)) && !last_is_opt(hint)) {
Radek Krejcied5acc52019-04-25 15:57:04 +0200130 linenoisePathCompletion(buf, hint, lc);
131 } else if ((!strncmp(buf, "print ", 6) || !strncmp(buf, "feature ", 8)) && !last_is_opt(hint)) {
132 get_model_completion(hint, &matches, &match_count);
133 } else if (!strchr(buf, ' ') && hint[0]) {
134 get_cmd_completion(hint, &matches, &match_count);
135 }
136
137 for (i = 0; i < match_count; ++i) {
138 linenoiseAddCompletion(lc, matches[i]);
139 free(matches[i]);
140 }
141 free(matches);
142}