Skip to content

Commit 6a165fc

Browse files
committed
feat: Implement CLI scripting variables, built-in commands, traps, and UI features including aliases, bookmarks, and history.
1 parent cd4e8b2 commit 6a165fc

16 files changed

Lines changed: 5316 additions & 5107 deletions

src/cli/CLIcore/CLIcore/CLIcore_UI.c

Lines changed: 77 additions & 3694 deletions
Large diffs are not rendered by default.

src/cli/CLIcore/CLIcore/CLIcore_UI.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,20 @@ void cli_history_log_init(void);
9898
errno_t cli_ghistory(void);
9999
errno_t cli_lhistory(void);
100100

101-
#endif
101+
#define BOOKMARK_MAX 64
102+
#define BOOKMARK_NAMELEN 200
103+
#define BOOKMARK_CMDLEN 2000
104+
105+
void cli_history_expand(void);
106+
void cli_alias_expand(void);
107+
void cli_expand_cmdsub(char *line, int maxlen);
108+
void cli_expand_tilde(char *line, int maxlen);
109+
void cli_expand_braces(char *line, int maxlen);
110+
void cli_expand_globs(char *line, int maxlen);
111+
void cli_session_log_cmd(const char *cmd);
112+
void cli_history_log_cmd(const char *cmd);
113+
const char *CLI_history_file(void);
114+
void cli_save_last_argument(void);
115+
const char *strip_ws(const char *s);
116+
117+
#endif /* CLICORE_UI_H */
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
#include <stdio.h>
2+
#include <dirent.h>
3+
#include <sys/stat.h>
4+
#include <sys/ioctl.h>
5+
#include <unistd.h>
6+
#include <termios.h>
7+
#ifdef USE_READLINE
8+
#include <readline/history.h>
9+
#include <readline/readline.h>
10+
#endif
11+
#include "CLIcore.h"
12+
#include "CLIcore_UI.h"
13+
#include "CLIcore_script.h"
14+
#include "CLIcore/cli_calc_parser.h"
15+
#include <glob.h>
16+
#include <sys/wait.h>
17+
#include "COREMOD_memory/COREMOD_memory.h"
18+
#include "timeutils.h"
19+
20+
21+
22+
/*
23+
* ============================================================
24+
* Command Alias Subsystem
25+
* ============================================================
26+
*
27+
* Aliases are stored in data.alias[] and persisted
28+
* to ~/.milk_aliases (one "name=command" per line).
29+
*/
30+
31+
/** @brief Path to alias persistence file */
32+
const char *CLI_alias_file(void)
33+
{
34+
static char path[1024] = {0};
35+
if(path[0] == '\0')
36+
{
37+
const char *home = getenv("HOME");
38+
if(home)
39+
{
40+
snprintf(path, sizeof(path),
41+
"%s/.milk_aliases", home);
42+
}
43+
else
44+
{
45+
snprintf(path, sizeof(path),
46+
".milk_aliases");
47+
}
48+
}
49+
return path;
50+
}
51+
52+
/**
53+
* @brief Load aliases from ~/.milk_aliases
54+
*/
55+
void cli_alias_load(void)
56+
{
57+
data.NBalias = 0;
58+
FILE *fp = fopen(CLI_alias_file(), "r");
59+
if(fp == NULL)
60+
{
61+
return;
62+
}
63+
64+
char line[CLI_ALIAS_NAMELEN + CLI_ALIAS_CMDLEN + 4];
65+
while(fgets(line, (int) sizeof(line), fp)
66+
!= NULL)
67+
{
68+
if(data.NBalias >= CLI_MAX_ALIASES)
69+
{
70+
break;
71+
}
72+
/* Strip trailing newline */
73+
line[strcspn(line, "\n")] = '\0';
74+
/* Skip empty / comment lines */
75+
if(line[0] == '\0' || line[0] == '#')
76+
{
77+
continue;
78+
}
79+
/* Split on first '=' */
80+
char *eq = strchr(line, '=');
81+
if(eq == NULL)
82+
{
83+
continue;
84+
}
85+
*eq = '\0';
86+
strncpy(data.alias[data.NBalias].name,
87+
line,
88+
CLI_ALIAS_NAMELEN - 1);
89+
data.alias[data.NBalias].name[
90+
CLI_ALIAS_NAMELEN - 1] = '\0';
91+
strncpy(data.alias[data.NBalias].cmd,
92+
eq + 1,
93+
CLI_ALIAS_CMDLEN - 1);
94+
data.alias[data.NBalias].cmd[
95+
CLI_ALIAS_CMDLEN - 1] = '\0';
96+
data.NBalias++;
97+
}
98+
fclose(fp);
99+
}
100+
101+
/**
102+
* @brief Save aliases to ~/.milk_aliases
103+
*/
104+
void cli_alias_save(void)
105+
{
106+
FILE *fp = fopen(CLI_alias_file(), "w");
107+
if(fp == NULL)
108+
{
109+
printf("ERROR: cannot write %s\n",
110+
CLI_alias_file());
111+
return;
112+
}
113+
for(int i = 0; i < data.NBalias; i++)
114+
{
115+
fprintf(fp, "%s=%s\n",
116+
data.alias[i].name,
117+
data.alias[i].cmd);
118+
}
119+
fclose(fp);
120+
}
121+
122+
/**
123+
* @brief CLI handler: alias <name> <command...>
124+
*
125+
* Creates or updates a command alias.
126+
*/
127+
errno_t cli_alias_add(void)
128+
{
129+
if(data.cmdNBarg < 3)
130+
{
131+
printf("Usage: alias <name> <command...>\n");
132+
return RETURN_FAILURE;
133+
}
134+
135+
const char *name =
136+
data.cmdargtoken[1].val.string;
137+
138+
/* Build command from args 2..N */
139+
char cmd[CLI_ALIAS_CMDLEN];
140+
cmd[0] = '\0';
141+
for(long a = 2; a < data.cmdNBarg; a++)
142+
{
143+
if(a > 2)
144+
{
145+
strncat(cmd, " ",
146+
CLI_ALIAS_CMDLEN
147+
- strlen(cmd) - 1);
148+
}
149+
strncat(cmd,
150+
data.cmdargtoken[a].val.string,
151+
CLI_ALIAS_CMDLEN
152+
- strlen(cmd) - 1);
153+
}
154+
155+
/* Check if alias already exists — update */
156+
for(int i = 0; i < data.NBalias; i++)
157+
{
158+
if(strcmp(data.alias[i].name, name) == 0)
159+
{
160+
strncpy(data.alias[i].cmd, cmd,
161+
CLI_ALIAS_CMDLEN - 1);
162+
data.alias[i].cmd[
163+
CLI_ALIAS_CMDLEN - 1] = '\0';
164+
cli_alias_save();
165+
printf("Alias updated: %s = %s\n",
166+
name, cmd);
167+
return RETURN_SUCCESS;
168+
}
169+
}
170+
171+
/* Add new alias */
172+
if(data.NBalias >= CLI_MAX_ALIASES)
173+
{
174+
printf("ERROR: alias table full (%d)\n",
175+
CLI_MAX_ALIASES);
176+
return RETURN_FAILURE;
177+
}
178+
179+
strncpy(data.alias[data.NBalias].name,
180+
name, CLI_ALIAS_NAMELEN - 1);
181+
data.alias[data.NBalias].name[
182+
CLI_ALIAS_NAMELEN - 1] = '\0';
183+
strncpy(data.alias[data.NBalias].cmd,
184+
cmd, CLI_ALIAS_CMDLEN - 1);
185+
data.alias[data.NBalias].cmd[
186+
CLI_ALIAS_CMDLEN - 1] = '\0';
187+
data.NBalias++;
188+
189+
cli_alias_save();
190+
printf("Alias created: %s = %s\n",
191+
name, cmd);
192+
193+
return RETURN_SUCCESS;
194+
}
195+
196+
/**
197+
* @brief CLI handler: unalias <name>
198+
*/
199+
errno_t cli_alias_remove(void)
200+
{
201+
if(data.cmdNBarg < 2)
202+
{
203+
printf("Usage: unalias <name>\n");
204+
return RETURN_FAILURE;
205+
}
206+
207+
const char *name =
208+
data.cmdargtoken[1].val.string;
209+
210+
for(int i = 0; i < data.NBalias; i++)
211+
{
212+
if(strcmp(data.alias[i].name, name) == 0)
213+
{
214+
/* Shift remaining entries */
215+
for(int j = i;
216+
j < data.NBalias - 1; j++)
217+
{
218+
data.alias[j] = data.alias[j + 1];
219+
}
220+
data.NBalias--;
221+
cli_alias_save();
222+
printf("Alias removed: %s\n", name);
223+
return RETURN_SUCCESS;
224+
}
225+
}
226+
227+
printf("Alias '%s' not found\n", name);
228+
return RETURN_FAILURE;
229+
}
230+
231+
/**
232+
* @brief CLI handler: aliases
233+
*/
234+
errno_t cli_alias_list(void)
235+
{
236+
if(data.NBalias == 0)
237+
{
238+
printf("No aliases defined.\n");
239+
return RETURN_SUCCESS;
240+
}
241+
printf("--- Aliases (%d) ---\n",
242+
data.NBalias);
243+
for(int i = 0; i < data.NBalias; i++)
244+
{
245+
printf(" %-16s = %s\n",
246+
data.alias[i].name,
247+
data.alias[i].cmd);
248+
}
249+
return RETURN_SUCCESS;
250+
}
251+
252+
/**
253+
* @brief Expand alias in data.CLIcmdline
254+
*
255+
* If the first word matches an alias name,
256+
* substitute it with the alias command, keeping
257+
* any trailing arguments.
258+
*/
259+
void cli_alias_expand(void)
260+
{
261+
if(data.NBalias == 0)
262+
{
263+
return;
264+
}
265+
266+
/* Extract first word */
267+
char firstword[CLI_ALIAS_NAMELEN];
268+
int fwlen = 0;
269+
const char *p = data.CLIcmdline;
270+
271+
/* Skip leading whitespace */
272+
while(*p == ' ' || *p == '\t')
273+
{
274+
p++;
275+
}
276+
while(*p != '\0' && *p != ' '
277+
&& *p != '\t' && *p != '\n')
278+
{
279+
if(fwlen < CLI_ALIAS_NAMELEN - 1)
280+
{
281+
firstword[fwlen++] = *p;
282+
}
283+
p++;
284+
}
285+
firstword[fwlen] = '\0';
286+
287+
if(fwlen == 0)
288+
{
289+
return;
290+
}
291+
292+
/* Search aliases */
293+
for(int i = 0; i < data.NBalias; i++)
294+
{
295+
if(strcmp(data.alias[i].name,
296+
firstword) == 0)
297+
{
298+
/* Build expanded line */
299+
char expanded[STRINGMAXLEN_CLICMDLINE];
300+
snprintf(expanded,
301+
STRINGMAXLEN_CLICMDLINE,
302+
"%s%s",
303+
data.alias[i].cmd,
304+
p); /* p points to rest */
305+
strncpy(data.CLIcmdline, expanded,
306+
STRINGMAXLEN_CLICMDLINE - 1);
307+
data.CLIcmdline[
308+
STRINGMAXLEN_CLICMDLINE - 1]
309+
= '\0';
310+
return;
311+
}
312+
}
313+
}

0 commit comments

Comments
 (0)