1 #include "snd.h"
2 #include "sndlib-strings.h"
3 #include "clm-strings.h"
4 
5 
6 static char *current_match = NULL;
7 
8 /* TAB completion requires knowing what is currently defined, which requires scrounging
9  *   around in the symbol tables
10  */
11 
12 #if HAVE_SCHEME
13 
14 typedef struct {
15   const char *text;
16   int matches, len;
17 } match_info;
18 
compare_names(const char * symbol_name,void * data)19 static bool compare_names(const char *symbol_name, void *data)
20 {
21   match_info *m = (match_info *)data;
22 
23   if (strncmp(m->text, symbol_name, m->len) == 0)
24     {
25       m->matches++;
26       add_possible_completion(symbol_name);
27       if (!current_match)
28 	current_match = mus_strdup(symbol_name);
29       else
30 	{
31 	  int j, curlen;
32 	  curlen = mus_strlen(current_match);
33 	  for (j = 0; j < curlen; j++)
34 	    if (current_match[j] != symbol_name[j])
35 	      {
36 		current_match[j] = '\0';
37 		break;
38 	      }
39 	}
40     }
41   return(false);
42 }
43 
completions(const char * text)44 static int completions(const char *text)
45 {
46   match_info *m;
47   int matches;
48 
49   m = (match_info *)calloc(1, sizeof(match_info));
50   m->text = text;
51   m->len = strlen(text);
52   m->matches = 0;
53   s7_for_each_symbol_name(s7, compare_names, (void *)m);
54   matches = m->matches;
55   free(m);
56   return(matches);
57 }
58 
59 #endif
60 
61 
62 #if HAVE_RUBY
snd_rb_methods(void)63 static Xen snd_rb_methods(void)
64 {
65   /* returns all the functions we defined */
66   Xen argv[1];
67   argv[0] = Xen_true;
68   return(rb_class_private_instance_methods(1, argv, rb_mKernel));
69   /* rb_ary_new here -- should we free? */
70 }
71 
72 
completions(const char * text)73 static int completions(const char *text)
74 {
75   Xen tab;
76   int i, n, len, matches = 0;
77 
78   tab = snd_rb_methods();
79   n = Xen_vector_length(tab);
80   len = strlen(text);
81 
82   for (i = 0; i < n; ++i)
83     {
84       char *sym;
85       Xen handle;
86 
87       handle = Xen_vector_ref(tab, i);
88       sym = Xen_object_to_C_string(handle);
89 
90       if (strncmp(text, sym, len) == 0)
91 	{
92 	  matches++;
93 	  add_possible_completion(sym);
94 	  if (!current_match)
95 	    current_match = mus_strdup(sym);
96 	  else
97 	    {
98 	      int j, curlen;
99 	      curlen = mus_strlen(current_match);
100 	      for (j = 0; j < curlen; j++)
101 		if (current_match[j] != sym[j])
102 		  {
103 		    current_match[j] = '\0';
104 		    break;
105 		  }
106 	    }
107 	}
108     }
109   return(matches);
110 }
111 #endif
112 
113 
114 #if HAVE_FORTH
completions(const char * text)115 static int completions(const char *text)
116 {
117   Xen tab = fth_find_in_wordlist(text);
118   int i, matches = Xen_vector_length(tab);
119 
120   for (i = 0; i < matches; i++)
121     {
122       char *sym = Xen_string_to_C_string(Xen_vector_ref(tab, i));
123       add_possible_completion(sym);
124       if (!current_match)
125 	current_match = mus_strdup(sym);
126       else
127 	{
128 	  int j, curlen;
129 	  curlen = mus_strlen(current_match);
130 	  for (j = 0; j < curlen; j++)
131 	    if (current_match[j] != sym[j])
132 	      {
133 		current_match[j] = '\0';
134 		break;
135 	      }
136 	}
137     }
138   return(matches);
139 }
140 #endif
141 
142 
143 #if (!HAVE_EXTENSION_LANGUAGE)
completions(const char * text)144 static int completions(const char *text) {return(0);}
145 #endif
146 
147 
148 #if HAVE_FORTH
is_separator(char c)149 static bool is_separator(char c)
150 {
151   /* only space is separator */
152   return(!(isgraph((int)c)));
153 }
154 #else
is_separator(char c)155 static bool is_separator(char c)
156 {
157   return((!(isalpha((int)c))) &&
158 	 (!(isdigit((int)c))) &&
159 #if HAVE_RUBY
160 	 (c != '?') &&
161 	 (c != '!') &&
162 	 (c != '_') &&
163 #endif
164 #if HAVE_SCHEME
165 	 (c != '-') &&
166 	 (c != '_') &&
167 	 (c != '>') &&
168 	 (c != '?') &&
169 	 (c != '!') &&
170 	 (c != '=') &&
171 	 (c != '<') &&
172 	 (c != '*') &&
173 	 (c != '+') &&
174 	 (c != '%') &&
175 	 (c != ':') &&
176 	 (c != '/') &&
177 #endif
178 	 (c != '$'));
179 }
180 #endif
181 
182 
direct_completions(const char * str)183 char *direct_completions(const char *str)
184 {
185   int matches = 0;
186   current_match = NULL;
187   clear_possible_completions();
188   set_completion_matches(0);
189   set_save_completions(true);
190   matches = completions(str);
191   set_completion_matches(matches);
192   set_save_completions(false);
193   return(current_match);
194 }
195 
196 
expression_completer(widget_t w,const char * original_text,void * data)197 char *expression_completer(widget_t w, const char *original_text, void *data)
198 {
199   int len, beg, matches = 0;
200   /* first back up to some delimiter to get the current expression */
201 
202   current_match = NULL;
203   set_completion_matches(0);
204 
205   if ((original_text) && (*original_text))
206     {
207       const char *text;
208       int i;
209 
210       len = strlen(original_text);
211       for (i = len - 1; i >= 0; i--)
212 	if (is_separator(original_text[i]))
213 	  break;
214       beg = i + 1;
215 
216       if (beg == len)
217 	{
218 	  /* returning original = no-op response to <tab> which seems useless;
219 	   *   so, if it's a function that we recognize and that function has its own completer, call it?
220 	   *   result null -> return original (current behavior), else append result as selection to current;
221 	   *   this way, I think it's easy to clear the suggested completion (cursor at end, so backspace deletes all).
222 	   * or scan back through full text (assuming listener or history available) and find match?
223 	   */
224 	  return(mus_strdup(original_text));
225 	}
226 
227       if (beg > 0)
228 	text = (const char *)(original_text + beg);
229       else text = original_text;
230       matches = completions(text);
231     }
232   else return(mus_strdup(original_text));
233 
234   set_completion_matches(matches);
235   if ((current_match) &&
236       (*current_match))
237     {
238       if (beg == 0)
239 	return(current_match);
240       else
241 	{
242 	  char *text;
243 	  len = mus_strlen(current_match) + beg + 2;
244 	  text = (char *)calloc(len, sizeof(char));
245 	  memcpy(text, original_text, beg);
246 	  strcat(text, current_match);
247 	  free(current_match);
248 	  return(text);
249 	}
250     }
251   return(mus_strdup(original_text));
252 }
253 
254 
255 
256 /* -------- saved choices -------- */
257 
258 #define BEST_COMPLETIONS 64
259 static char *best_completions[BEST_COMPLETIONS];
260 
preload_best_completions(void)261 void preload_best_completions(void)
262 {
263   /* set up the array with some reasonable first choices */
264   int n = 0;
265   best_completions[n++] = mus_strdup(S_open_sound);
266   best_completions[n++] = mus_strdup(S_channels);
267   best_completions[n++] = mus_strdup(S_close_sound);
268   best_completions[n++] = mus_strdup(S_cursor);
269   best_completions[n++] = mus_strdup(S_env_channel);
270   best_completions[n++] = mus_strdup(S_file_name);
271   best_completions[n++] = mus_strdup(S_framples);
272   best_completions[n++] = mus_strdup(S_map_channel);
273   best_completions[n++] = mus_strdup(S_maxamp);
274   best_completions[n++] = mus_strdup(S_play);
275   best_completions[n++] = mus_strdup(S_save_sound);
276   best_completions[n++] = mus_strdup(S_scale_channel);
277   best_completions[n++] = mus_strdup(S_srate);
278   best_completions[n++] = mus_strdup("with-sound");
279   best_completions[n++] = mus_strdup(S_outa);
280   best_completions[n++] = mus_strdup("*output*");
281   best_completions[n++] = mus_strdup("lambda");
282   best_completions[n++] = mus_strdup("define");
283 }
284 
285 
save_completion_choice(const char * selection)286 void save_completion_choice(const char *selection)
287 {
288   int i;
289   char *cur, *old = NULL;
290 
291   cur = mus_strdup(selection);
292   for (i = 0; i < BEST_COMPLETIONS; i++)
293     {
294       old = best_completions[i];
295       best_completions[i] = cur;
296       if ((!old) ||
297 	  (mus_strcmp(old, cur)))
298 	break;
299       cur = old;
300     }
301   if (old) free(old);
302 }
303 
304 
find_best_completion(char ** choices,int num_choices)305 int find_best_completion(char **choices, int num_choices)
306 {
307   int i, k;
308   for (i = 0; (i < BEST_COMPLETIONS) && (best_completions[i]); i++)
309     for (k = 0; k < num_choices; k++)
310       if (mus_strcmp(choices[k], best_completions[i]))
311 	return(k + 1); /* row numbering is 1-based */
312   return(1);
313 }
314 
315 
316 
317 
318 
319 /* ---------------- EXPRESSION/FILENAME COMPLETIONS ---------------- */
320 
321 typedef char *(*completer_func)(widget_t w, const char *text, void *data);
322 static completer_func *completer_funcs = NULL;
323 typedef void (*multicompleter_func)(widget_t w, void *data);
324 static multicompleter_func *multicompleter_funcs = NULL;
325 static void **completer_data = NULL;
326 static int completer_funcs_size = 0;
327 static int completer_funcs_end = 0;
328 
329 
add_completer_func(char * (* func)(widget_t w,const char * text,void * context),void * data)330 int add_completer_func(char *(*func)(widget_t w, const char *text, void *context), void *data)
331 {
332   if (completer_funcs_size == completer_funcs_end)
333     {
334       completer_funcs_size += 8;
335       if (!completer_funcs)
336 	{
337 	  completer_funcs = (completer_func *)calloc(completer_funcs_size, sizeof(completer_func));
338 	  multicompleter_funcs = (multicompleter_func *)calloc(completer_funcs_size, sizeof(multicompleter_func));
339 	  completer_data = (void **)calloc(completer_funcs_size, sizeof(void *));
340 	}
341       else
342 	{
343 	  int i;
344 	  completer_funcs = (completer_func *)realloc(completer_funcs, completer_funcs_size * sizeof(completer_func));
345 	  multicompleter_funcs = (multicompleter_func *)realloc(multicompleter_funcs, completer_funcs_size * sizeof(multicompleter_func));
346 	  completer_data = (void **)realloc(completer_data, completer_funcs_size * sizeof(void *));
347 	  for (i = completer_funcs_end; i < completer_funcs_size; i++) completer_data[i] = NULL;
348 	}
349     }
350   completer_funcs[completer_funcs_end] = func;
351   completer_data[completer_funcs_end] = data;
352   completer_funcs_end++;
353   return(completer_funcs_end - 1);
354 }
355 
356 
add_completer_func_with_multicompleter(char * (* func)(widget_t w,const char * text,void * context),void * data,void (* multi_func)(widget_t w,void * data))357 int add_completer_func_with_multicompleter(char *(*func)(widget_t w, const char *text, void *context), void *data, void (*multi_func)(widget_t w, void *data))
358 {
359   int row;
360   row = add_completer_func(func, data);
361   multicompleter_funcs[row] = multi_func;
362   return(row);
363 }
364 
365 
366 static int completion_matches = 0;
367 
get_completion_matches(void)368 int get_completion_matches(void) {return(completion_matches);}
set_completion_matches(int matches)369 void set_completion_matches(int matches) {completion_matches = matches;}
370 
371 static bool save_completions = 0;
372 static char **possible_completions = NULL;
373 static int possible_completions_size = 0;
374 static int possible_completions_ctr = 0;
375 
set_save_completions(bool save)376 void set_save_completions(bool save) {save_completions = save;}
377 
378 
add_possible_completion(const char * text)379 void add_possible_completion(const char *text)
380 {
381   if (save_completions)
382     {
383       if (possible_completions_size == possible_completions_ctr)
384 	{
385 	  possible_completions_size += 16;
386 	  if (!possible_completions)
387 	    possible_completions = (char **)calloc(possible_completions_size, sizeof(char *));
388 	  else
389 	    {
390 	      int i;
391 	      possible_completions = (char **)realloc(possible_completions, possible_completions_size * sizeof(char *));
392 	      for (i = possible_completions_ctr; i < possible_completions_size; i++) possible_completions[i] = NULL;
393 	    }
394 	}
395       if (possible_completions[possible_completions_ctr]) free(possible_completions[possible_completions_ctr]);
396       possible_completions[possible_completions_ctr] = mus_strdup(text);
397       possible_completions_ctr++;
398     }
399 }
400 
401 
get_possible_completions_size(void)402 int get_possible_completions_size(void)
403 {
404   return(possible_completions_ctr);
405 }
406 
407 
get_possible_completions(void)408 char **get_possible_completions(void)
409 {
410   return(possible_completions);
411 }
412 
413 
handle_completions(widget_t w,int completer)414 void handle_completions(widget_t w, int completer)
415 {
416   if ((completer >= 0) &&
417       (completer < completer_funcs_end) &&
418       (multicompleter_funcs[completer]))
419     (*multicompleter_funcs[completer])(w, completer_data[completer]);
420 }
421 
422 
complete_text(widget_t w,const char * text,int func)423 char *complete_text(widget_t w, const char *text, int func)
424 {
425   /* given text, call proc table entry func, return new text (not text!) */
426   completion_matches = -1; /* i.e. no completer */
427   possible_completions_ctr = 0;
428   if ((func >= 0) &&
429       (func < completer_funcs_end))
430     return((*completer_funcs[func])(w, text, completer_data[func]));
431   else return(mus_strdup(text));
432 }
433 
434 
clear_possible_completions(void)435 void clear_possible_completions(void)
436 {
437   int i;
438   for (i = 0; i < possible_completions_size; i++)
439     if (possible_completions[i])
440       {
441 	free(possible_completions[i]);
442 	possible_completions[i] = NULL;
443       }
444   possible_completions_ctr = 0;
445 }
446 
447 
448 static list_completer_info *srate_info = NULL;
449 
init_srate_list(void)450 static void init_srate_list(void)
451 {
452   if (!srate_info)
453     {
454       int loc = 0;
455       srate_info = (list_completer_info *)calloc(1, sizeof(list_completer_info));
456       srate_info->exact_match = true;
457       srate_info->values_size = 16;
458       srate_info->values = (char **)calloc(srate_info->values_size, sizeof(char *));
459       srate_info->values[loc++] = mus_strdup("44100");
460       srate_info->values[loc++] = mus_strdup("22050");
461       srate_info->values[loc++] = mus_strdup("8000");
462       srate_info->values[loc++] = mus_strdup("48000");
463       srate_info->num_values = loc;
464     }
465 }
466 
467 
add_srate_to_completion_list(int srate)468 void add_srate_to_completion_list(int srate)
469 {
470   char *str;
471   int i;
472 
473   init_srate_list();
474   str = (char *)malloc(16 * sizeof(char));
475   snprintf(str, 16, "%d", srate);
476 
477   for (i = 0; i < srate_info->num_values; i++)
478     if (mus_strcmp(srate_info->values[i], str))
479       {
480 	free(str);
481 	return;
482       }
483 
484   if (srate_info->num_values >= srate_info->values_size)
485     {
486       srate_info->values_size += 16;
487       srate_info->values = (char **)realloc(srate_info->values, srate_info->values_size * sizeof(char *));
488       for (i = srate_info->num_values; i < srate_info->values_size; i++) srate_info->values[i] = NULL;
489     }
490 
491   srate_info->values[srate_info->num_values++] = str;
492 }
493 
494 
srate_completer(widget_t w,const char * text,void * data)495 char *srate_completer(widget_t w, const char *text, void * data)
496 {
497   init_srate_list();
498   return(list_completer(w, text, (void *)srate_info));
499 }
500 
501 
502 #ifndef _MSC_VER
503   #include <dirent.h>
504 #endif
505 
506 enum {ANY_FILE_TYPE, SOUND_FILE_TYPE};
507 
508 
filename_completer_1(widget_t w,const char * text,int file_type)509 static char *filename_completer_1(widget_t w, const char *text, int file_type)
510 {
511   /* assume text is a partial filename */
512   /* get directory name, opendir, read files checking for match */
513   /* return name of same form as original (i.e. don't change user's directory indication) */
514   /* if directory, add "/" -- is_directory(name) static in snd-xfile.c */
515 
516   char *full_name = NULL, *dir_name = NULL, *file_name = NULL, *current_match = NULL;
517   int i, j, k, len, curlen, matches = 0;
518   DIR *dpos;
519 
520   if (mus_strlen(text) == 0) return(NULL);
521   full_name = mus_expand_filename(text);
522   len = mus_strlen(full_name);
523   for (i = len - 1; i > 0; i--)
524     if (full_name[i] == '/')
525       break;
526 
527   dir_name = (char *)calloc(i + 1, sizeof(char));
528   memcpy(dir_name, full_name, i);
529 
530   file_name = (char *)calloc(len - i + 2, sizeof(char));
531   for (j = 0, k = i + 1; k < len; j++, k++)
532     file_name[j] = full_name[k];
533 
534   if (full_name)
535     {
536       free(full_name);
537       full_name = NULL;
538     }
539 
540   len = mus_strlen(file_name);
541   if ((dpos = opendir(dir_name)))
542     {
543       struct dirent *dirp;
544       while ((dirp = readdir(dpos)))
545 	if ((dirp->d_name[0] != '.') &&
546 	    (strncmp(dirp->d_name, file_name, len) == 0)) /* match dirp->d_name against rest of text */
547 	  {
548 	    if ((file_type == ANY_FILE_TYPE) ||
549 		(is_sound_file(dirp->d_name)))
550 	      {
551 		matches++;
552 		add_possible_completion(dirp->d_name);
553 		if (!current_match)
554 		  current_match = mus_strdup(dirp->d_name);
555 		else
556 		  {
557 		    curlen = strlen(current_match);
558 		    for (j = 0; j < curlen; j++)
559 		      if (current_match[j] != dirp->d_name[j])
560 			{
561 			  current_match[j] = '\0';
562 			  break;
563 			}
564 		  }
565 	      }
566 	  }
567 
568       if (closedir(dpos) != 0)
569 	snd_error("closedir %s failed (%s)!", dir_name, snd_io_strerror());
570     }
571 
572   if (dir_name) free(dir_name);
573   if (file_name) free(file_name);
574 
575   set_completion_matches(matches);
576 
577   if ((current_match) &&
578       (*current_match))
579     {
580       /* attach matched portion to user's indication of dir */
581       len = mus_strlen(text);
582       for (i = len - 1; i >= 0; i--)
583 	if (text[i] == '/')
584 	  break;
585       if (i < 0) return(current_match);
586       curlen = strlen(current_match) + len + 3;
587       file_name = (char *)calloc(curlen, sizeof(char));
588       memcpy(file_name, text, i + 1);
589       strcat(file_name, current_match);
590       if (is_directory(file_name))
591 	strcat(file_name, "/");
592       free(current_match);
593       return(file_name);
594     }
595   return(mus_strdup(text));
596 }
597 
598 
filename_completer(widget_t w,const char * text,void * data)599 char *filename_completer(widget_t w, const char *text, void *data)
600 {
601   return(filename_completer_1(w, text, ANY_FILE_TYPE));
602 }
603 
604 
sound_filename_completer(widget_t w,const char * text,void * data)605 char *sound_filename_completer(widget_t w, const char *text, void *data)
606 {
607   return(filename_completer_1(w, text, SOUND_FILE_TYPE));
608 }
609 
610 
find_indentation(char * str,int loc)611 static int find_indentation(char *str, int loc)
612 {
613   int line_beg = 0, open_paren = -1, parens, i;
614   parens = 0;
615   for (i = loc - 1; i >= 0; i--)
616     {
617       if (str[i] == ')') parens--;
618       if (str[i] == '(') parens++;
619       if (parens == 1)
620 	{
621 	  open_paren = i;
622 	  break;
623 	}
624     }
625   if (open_paren == -1) return(1);
626   if (open_paren == 0) return(3);
627   for (i = open_paren - 1; i > 0; i--)
628     if (str[i] == '\n')
629       {
630 	line_beg = i;
631 	break;
632       }
633   if (line_beg == 0) return(1);
634   return(open_paren - line_beg + 2);
635 }
636 
637 
complete_listener_text(char * old_text,int end,bool * try_completion,char ** to_file_text)638 char *complete_listener_text(char *old_text, int end, bool *try_completion, char **to_file_text)
639 {
640   int len, i, k, spaces, text_pos = 0, cr_pos = 0;
641   char *new_text = NULL, *file_text = NULL;
642 
643   len = strlen(old_text);
644   for (i = len - 1; i > 0; i--)
645     {
646       if (old_text[i] == '\n')
647 	{
648 	  /* tab as indentation */
649 	  /* look at previous line to decide */
650 	  spaces = find_indentation(old_text, i);
651 	  if (spaces > 0)
652 	    {
653 	      file_text = (char *)calloc(spaces + 1, sizeof(char));
654 	      for (k = 0; k < spaces; k++)
655 		file_text[k] = ' ';
656 	      file_text[spaces] = 0;
657 	      append_listener_text(end, file_text);
658 	      free(file_text);
659 	      file_text = NULL;
660 	    }
661 	  (*try_completion) = false;
662 	  return(NULL);
663 	}
664 
665       if (old_text[i] == ';')
666 	{
667 	  /* this isn't quite right, but how much effort should we put in it? */
668 	  spaces = 20;
669 	  for (k = i - 1; k > 0; k--)
670 	    if (old_text[k] == '\n')
671 	      {
672 		cr_pos = k;
673 		break;
674 	      }
675 	    else
676 	      if ((!(isspace((int)(old_text[k])))) &&
677 		  (text_pos == 0))
678 		text_pos = k;
679 	  if (text_pos > 0)
680 	    text_pos -= cr_pos;
681 	  if (cr_pos == 0) spaces--;
682 	  if (text_pos < spaces)
683 	    {
684 	      file_text = (char *)calloc(spaces + 2, sizeof(char));
685 	      for (k = text_pos + 1; k < spaces; k++)
686 		file_text[k - text_pos - 1] = ' ';
687 	      file_text[spaces] = ';';
688 	      file_text[spaces + 1] = 0;
689 	      append_listener_text(end - 1, file_text);
690 	      free(file_text);
691 	      file_text = NULL;
692 	    }
693 	  (*try_completion) = false;
694 	  return(NULL);
695 	}
696 
697       if (old_text[i] == '\"')
698 	{
699 	  char *new_file;
700 	  file_text = mus_strdup((char *)(old_text + i + 1));
701 	  new_file = filename_completer(NULL_WIDGET, file_text, NULL);
702 	  len = mus_strlen(new_file);
703 	  if (len > 0)
704 	    {
705 	      len += i + 2;
706 	      new_text = (char *)calloc(len, sizeof(char));
707 	      memcpy(new_text, old_text, i + 1);
708 	      strcat(new_text, new_file);
709 	      free(new_file);
710 	    }
711 	  break;
712 	}
713       if (isspace((int)(old_text[i]))) break;
714     }
715 
716   if (!new_text) new_text = expression_completer(NULL_WIDGET, old_text, NULL);
717   (*try_completion) = true;
718   (*to_file_text) = file_text;
719   return(new_text);
720 }
721 
722 
list_completer(widget_t w,const char * text,void * data)723 char *list_completer(widget_t w, const char *text, void *data)
724 {
725   list_completer_info *info = (list_completer_info *)data;
726   int i, j = 0, len, matches = 0, current_match = -1;
727   char *trimmed_text;
728 
729   set_completion_matches(0);
730   /* check for null text */
731   len = mus_strlen(text);
732   if (len == 0) return(mus_strdup(text));
733 
734   /* strip away leading and trailing white space */
735   trimmed_text = (char *)calloc(len + 1, sizeof(char));
736   for (i = 0; i < len; i++)
737     if (!(isspace((int)text[i])))
738       trimmed_text[j++] = text[i];
739 
740   if (j == 0)
741     {
742       free(trimmed_text);
743       return(mus_strdup(text));
744     }
745 
746   /* check for match(es) against values */
747   if (info->exact_match)
748     {
749       for (i = 0; i < info->num_values; i++)
750 	if ((info->values[i]) &&
751 	    (strncmp(info->values[i], trimmed_text, len) == 0))
752 	  {
753 	    matches++;
754 	    current_match = i;
755 	  }
756     }
757   else
758     {
759       for (i = 0; i < info->num_values; i++)
760 	if ((info->values[i]) &&
761 	    (STRNCMP(info->values[i], trimmed_text, len) == 0))
762 	  {
763 	    matches++;
764 	    current_match = i;
765 	  }
766     }
767   free(trimmed_text);
768   if (matches != 1)
769     return(mus_strdup(text));
770   set_completion_matches(1);
771 
772   return(mus_strdup(info->values[current_match]));
773 }
774