blob: 4172a2bae7e26d8b58280be220ce1fc7472a8794 [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 Krejci535ea9f2020-05-29 16:01:05 +020025#include "commands.h"
26#include "linenoise/linenoise.h"
27
Radek Krejcied5acc52019-04-25 15:57:04 +020028extern struct ly_ctx *ctx;
29
30static void
31get_cmd_completion(const char *hint, char ***matches, unsigned int *match_count)
32{
33 int i;
34 void *p;
35
36 *match_count = 0;
37 *matches = NULL;
38
39 for (i = 0; commands[i].name; i++) {
40 if (!strncmp(hint, commands[i].name, strlen(hint))) {
41 ++(*match_count);
42 p = realloc(*matches, *match_count * sizeof **matches);
43 if (!p) {
44 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
45 return;
46 }
47 *matches = p;
48 (*matches)[*match_count-1] = strdup(commands[i].name);
49 }
50 }
51}
52
53static int
54last_is_opt(const char *hint)
55{
56 const char *ptr;
57
58 /* last is option */
59 if (hint[0] == '-') {
60 return 1;
61 }
62
63 do {
64 --hint;
65 } while (hint[0] == ' ');
66
67 /* last is option argument */
68 ptr = strrchr(hint, ' ');
69 if (ptr) {
70 ++ptr;
71 if (ptr[0] == '-') {
72 return 1;
73 }
74 }
75
76 return 0;
77}
78
79static void
80get_model_completion(const char *hint, char ***matches, unsigned int *match_count)
81{
Radek Krejci7eb54ba2020-05-18 16:30:04 +020082 LY_ARRAY_SIZE_TYPE u;
Radek Krejcied5acc52019-04-25 15:57:04 +020083 uint32_t idx = 0;
84 const struct lys_module *module;
85 void *p;
86
87 *match_count = 0;
88 *matches = NULL;
89
90 while ((module = ly_ctx_get_module_iter(ctx, &idx))) {
91 if (!strncmp(hint, module->name, strlen(hint))) {
92 ++(*match_count);
93 p = realloc(*matches, *match_count * sizeof **matches);
94 if (!p) {
95 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
96 return;
97 }
98 *matches = p;
99 (*matches)[*match_count-1] = strdup(module->name);
100 }
101
102 LY_ARRAY_FOR(module->parsed->includes, u) {
103 if (!strncmp(hint, module->parsed->includes[u].submodule->name, strlen(hint))) {
104 ++(*match_count);
105 p = realloc(*matches, *match_count * sizeof **matches);
106 if (!p) {
107 fprintf(stderr, "Memory allocation failed (%s:%d, %s)", __FILE__, __LINE__, strerror(errno));
108 return;
109 }
110 *matches = p;
111 (*matches)[*match_count-1] = strdup(module->parsed->includes[u].submodule->name);
112 }
113 }
114 }
115}
116
117void
118complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc)
119{
120 char **matches = NULL;
121 unsigned int match_count = 0, i;
122
123 if (!strncmp(buf, "add ", 4)) {
124 linenoisePathCompletion(buf, hint, lc);
125 } else if ((!strncmp(buf, "searchpath ", 11) || !strncmp(buf, "data ", 5)
126 || !strncmp(buf, "config ", 7) || !strncmp(buf, "filter ", 7)
127 || !strncmp(buf, "xpath ", 6) || !strncmp(buf, "clear ", 6)) && !last_is_opt(hint)) {
128 linenoisePathCompletion(buf, hint, lc);
129 } else if ((!strncmp(buf, "print ", 6) || !strncmp(buf, "feature ", 8)) && !last_is_opt(hint)) {
130 get_model_completion(hint, &matches, &match_count);
131 } else if (!strchr(buf, ' ') && hint[0]) {
132 get_cmd_completion(hint, &matches, &match_count);
133 }
134
135 for (i = 0; i < match_count; ++i) {
136 linenoiseAddCompletion(lc, matches[i]);
137 free(matches[i]);
138 }
139 free(matches);
140}