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