33 * ai-cli - readline wrapper to obtain a generative AI suggestion
44 * Configuration parsing and access
55 *
6- * Copyright 2023 Diomidis Spinellis
6+ * Copyright 2023-2024 Diomidis Spinellis
77 *
88 * Licensed under the Apache License, Version 2.0 (the "License");
99 * you may not use this file except in compliance with the License.
3030#include "ini.h"
3131
3232
33- const char hidden_config_name [] = ".aicliconfig" ;
33+ static const char hidden_config_name [] = ".aicliconfig" ;
3434
3535/*
3636 * Prefixes for providing n-shot user and assistant prompts in ini files.
@@ -39,19 +39,19 @@ const char hidden_config_name[] = ".aicliconfig";
3939 * user-1 = Disable breakpoint number 4
4040 * assistant-1 = delete 4
4141 */
42- const char prompt_ini_prefix [] = "prompt-" ;
43- const char user_ini_prefix [] = "user-" ;
44- const char assistant_ini_prefix [] = "assistant-" ;
42+ static const char prompt_ini_prefix [] = "prompt-" ;
43+ static const char user_ini_prefix [] = "user-" ;
44+ static const char assistant_ini_prefix [] = "assistant-" ;
4545
4646/*
4747 * Prefixes for providing n-shot user and assistant prompts in
4848 * environment variables. Examples:
4949 * AI_CLI_prompt_gdb_user_1=Disable breakpoint number 4
5050 * AI_CLI_prompt_gdb_assistant_1=delete 4
5151 */
52- const char env_prompt_prefix [] = "AI_CLI_prompt_" ;
53- const char user_env_prefix [] = "user_" ;
54- const char assistant_env_prefix [] = "assistant_" ;
52+ static const char env_prompt_prefix [] = "AI_CLI_prompt_" ;
53+ static const char user_env_prefix [] = "user_" ;
54+ static const char assistant_env_prefix [] = "assistant_" ;
5555
5656// Return true if the specified string starts with the given prefix
5757STATIC bool
@@ -74,7 +74,7 @@ prompt_id(const char *entry)
7474 const char * id_end = strchr (id_begin , '_' );
7575 if (!id_end )
7676 return NULL ;
77- return range_strdup (id_begin , id_end );
77+ return acl_range_strdup (id_begin , id_end );
7878}
7979
8080/*
@@ -86,12 +86,12 @@ prompt_id(const char *entry)
8686STATIC int
8787prompt_number (const char * name , const char * prompt_prefix )
8888{
89- int result = strtocard (name + strlen (prompt_prefix ));
89+ int result = acl_strtocard (name + strlen (prompt_prefix ));
9090 return result <= 0 || result > NPROMPTS ? -1 : result - 1 ;
9191}
9292
9393// Return true if the string contains the value true
94- bool
94+ static bool
9595strtobool (const char * string )
9696{
9797 return strcmp (string , "true" ) == 0 ;
@@ -138,25 +138,25 @@ fixed_matcher(config_t *pconfig, const char* section,
138138 } while(0)
139139
140140 // In section, key alphabetic order
141- MATCH (anthropic , endpoint , safe_strdup );
142- MATCH (anthropic , key , safe_strdup );
141+ MATCH (anthropic , endpoint , acl_safe_strdup );
142+ MATCH (anthropic , key , acl_safe_strdup );
143143 MATCH (anthropic , max_tokens , atoi );
144- MATCH (anthropic , model , safe_strdup );
144+ MATCH (anthropic , model , acl_safe_strdup );
145145 MATCH (anthropic , temperature , atof );
146146 MATCH (anthropic , top_k , atoi );
147147 MATCH (anthropic , top_p , atof );
148- MATCH (anthropic , version , safe_strdup );
148+ MATCH (anthropic , version , acl_safe_strdup );
149149
150- MATCH (binding , emacs , safe_strdup );
151- MATCH (binding , vi , safe_strdup );
150+ MATCH (binding , emacs , acl_safe_strdup );
151+ MATCH (binding , vi , acl_safe_strdup );
152152
153- MATCH (general , api , safe_strdup );
154- MATCH (general , logfile , safe_strdup );
155- MATCH (general , response_prefix , safe_strdup );
153+ MATCH (general , api , acl_safe_strdup );
154+ MATCH (general , logfile , acl_safe_strdup );
155+ MATCH (general , response_prefix , acl_safe_strdup );
156156 MATCH (general , timestamp , strtobool );
157157 MATCH (general , verbose , strtobool );
158158
159- MATCH (llamacpp , endpoint , safe_strdup );
159+ MATCH (llamacpp , endpoint , acl_safe_strdup );
160160 MATCH (llamacpp , frequency_penalty , atof );
161161 MATCH (llamacpp , mirostat , atoi );
162162 MATCH (llamacpp , mirostat_eta , atof );
@@ -174,13 +174,13 @@ fixed_matcher(config_t *pconfig, const char* section,
174174 MATCH (llamacpp , top_p , atof );
175175 MATCH (llamacpp , typical_p , atof );
176176
177- MATCH (openai , endpoint , safe_strdup );
178- MATCH (openai , key , safe_strdup );
179- MATCH (openai , model , safe_strdup );
177+ MATCH (openai , endpoint , acl_safe_strdup );
178+ MATCH (openai , key , acl_safe_strdup );
179+ MATCH (openai , model , acl_safe_strdup );
180180 MATCH (openai , temperature , atof );
181181
182- MATCH (prompt , context , strtocard );
183- MATCH (prompt , system , safe_strdup );
182+ MATCH (prompt , context , acl_strtocard );
183+ MATCH (prompt , system , acl_safe_strdup );
184184
185185 return 0 ;
186186}
@@ -208,9 +208,9 @@ fixed_program_matcher(config_t *pconfig, const char* name, const char* value)
208208 return 1; \
209209 } \
210210 } while (0)
211- MATCH_PROGRAM (comment , safe_strdup );
212- MATCH_PROGRAM (context , strtocard );
213- MATCH_PROGRAM (system , safe_strdup );
211+ MATCH_PROGRAM (comment , acl_safe_strdup );
212+ MATCH_PROGRAM (context , acl_strtocard );
213+ MATCH_PROGRAM (system , acl_safe_strdup );
214214
215215 return 0 ;
216216}
@@ -233,7 +233,7 @@ config_handler(void* user, const char* section, const char* name,
233233 return 1 ;
234234
235235 if (!starts_with (section , prompt_ini_prefix ))
236- errorf ("Unknown configuration section [%s], name `%s'." , section , name );
236+ acl_errorf ("Unknown configuration section [%s], name `%s'." , section , name );
237237
238238 /*
239239 * A program specific section. It can provide user or assistant
@@ -260,17 +260,17 @@ config_handler(void* user, const char* section, const char* name,
260260 if (starts_with (name , user_ini_prefix )) {
261261 int n = prompt_number (name , user_ini_prefix );
262262 if (n == -1 )
263- errorf ("Invalid prompt number, section [%s], name `%s', value `%s'." , section , name , value );
263+ acl_errorf ("Invalid prompt number, section [%s], name `%s', value `%s'." , section , name , value );
264264 pconfig -> prompt_user [n ] = strdup (value );
265265 return 1 ;
266266 } else if (starts_with (name , assistant_ini_prefix )) {
267267 int n = prompt_number (name , assistant_ini_prefix );
268268 if (n == -1 )
269- errorf ("Invalid prompt number, section [%s], name `%s', value `%s'." , section , name , value );
269+ acl_errorf ("Invalid prompt number, section [%s], name `%s', value `%s'." , section , name , value );
270270 pconfig -> prompt_assistant [n ] = strdup (value );
271271 return 1 ;
272272 }
273- errorf ("Unknown configuration section [%s], name `%s'." , section , name );
273+ acl_errorf ("Unknown configuration section [%s], name `%s'." , section , name );
274274 return 0 ; /* unknown section/name, error */
275275}
276276
@@ -307,7 +307,7 @@ env_override(config_t *config)
307307 // E.g. sqlite3 or gitconfig (which will be named git-config)
308308 char * program_name = prompt_id (entry );
309309 if (!program_name )
310- errorf ("Missing program identifier in prompt environment variable %s" , entry );
310+ acl_errorf ("Missing program identifier in prompt environment variable %s" , entry );
311311
312312 // Skip matching of programs other than ours
313313 if (strcmp (program_name , config -> program_name ) != 0 ) {
@@ -319,24 +319,24 @@ env_override(config_t *config)
319319 strlen (program_name );
320320 const char * prompt_name_end = strchr (prompt_name_begin , '=' );
321321 if (!prompt_name_end )
322- errorf ("Missing value in prompt environment variable %s" , entry );
323- char * prompt_name = range_strdup (prompt_name_begin , prompt_name_end );
322+ acl_errorf ("Missing value in prompt environment variable %s" , entry );
323+ char * prompt_name = acl_range_strdup (prompt_name_begin , prompt_name_end );
324324 const char * prompt_value = prompt_name_end + 1 ;
325325
326326 if (starts_with (prompt_name , user_env_prefix )) {
327327 int n = prompt_number (prompt_name , user_env_prefix );
328328 if (n == -1 )
329- errorf ("Invalid prompt value in environment variable %s" , entry );
329+ acl_errorf ("Invalid prompt value in environment variable %s" , entry );
330330 else
331331 config -> prompt_user [n ] = strdup (prompt_value );
332332 } else if (starts_with (prompt_name , assistant_env_prefix )) {
333333 int n = prompt_number (prompt_name , assistant_env_prefix );
334334 if (n == -1 )
335- errorf ("Invalid prompt value in environment variable %s" , entry );
335+ acl_errorf ("Invalid prompt value in environment variable %s" , entry );
336336 else
337337 config -> prompt_assistant [n ] = strdup (prompt_value );
338338 } else if (!fixed_program_matcher (config , prompt_name , prompt_value ))
339- errorf ("Invalid name in environment variable %s" , entry );
339+ acl_errorf ("Invalid name in environment variable %s" , entry );
340340 free (program_name );
341341 free (prompt_name );
342342 }
@@ -350,16 +350,16 @@ ini_checked_parse(const char* filename, ini_handler handler, config_t *config)
350350 int val = ini_parse (filename , handler , config );
351351 // When unable to open file val is -1, which we ignore
352352 if (val > 0 )
353- errorf ("%s:%d:1: Initialization file error" , filename , val );
353+ acl_errorf ("%s:%d:1: Initialization file error" , filename , val );
354354}
355355
356356/*
357357 * Read the configuration file from diverse directories into config.
358358 */
359359void
360- read_config (config_t * config )
360+ acl_read_config (config_t * config )
361361{
362- config -> program_name = short_program_name ();
362+ config -> program_name = acl_short_program_name ();
363363
364364 ini_checked_parse ("/usr/share/ai-cli/config" , config_handler , config );
365365 ini_checked_parse ("/usr/local/share/ai-cli/config" , config_handler , config );
@@ -370,11 +370,11 @@ read_config(config_t *config)
370370 if ((home_dir = getenv ("HOME" )) != NULL ) {
371371 char * home_config ;
372372
373- safe_asprintf (& home_config , "%s/%s" , home_dir , "share/ai-cli/config" );
373+ acl_safe_asprintf (& home_config , "%s/%s" , home_dir , "share/ai-cli/config" );
374374 ini_checked_parse (home_config , config_handler , config );
375375 free (home_config );
376376
377- safe_asprintf (& home_config , "%s/%s" , home_dir , hidden_config_name );
377+ acl_safe_asprintf (& home_config , "%s/%s" , home_dir , hidden_config_name );
378378 ini_checked_parse (home_config , config_handler , config );
379379 free (home_config );
380380 }
@@ -384,6 +384,7 @@ read_config(config_t *config)
384384 env_override (config );
385385}
386386
387+ #if defined(UNIT_TEST )
387388/*
388389 * Read the configuration file from the specified file path into config.
389390 * This allows testing the config handler.
@@ -393,29 +394,20 @@ read_file_config(config_t *config, const char *file_path)
393394{
394395 // Allow unit tests to override this value
395396 if (!config -> program_name )
396- config -> program_name = short_program_name ();
397+ config -> program_name = acl_short_program_name ();
397398
398399 ini_checked_parse (file_path , config_handler , config );
399400 env_override (config );
400401}
402+ #endif
401403
402404/*
403405 * Return the system role prompt string in dynamically allocated memory.
404406 */
405407char *
406- system_role_get (config_t * config )
408+ acl_system_role_get (config_t * config )
407409{
408410 char * system_role ;
409- safe_asprintf (& system_role , config -> prompt_system , config -> program_name );
411+ acl_safe_asprintf (& system_role , config -> prompt_system , config -> program_name );
410412 return system_role ;
411413}
412-
413- /*
414- * Set the name of the program executing the library.
415- * Provided for testing.
416- */
417- void
418- set_program_name (config_t * config , const char * name )
419- {
420- config -> program_name = name ;
421- }
0 commit comments