1/* $OpenBSD: keynote.y,v 1.19 2022/12/27 17:10:06 jmc Exp $ */
2/*
3 * The author of this code is Angelos D. Keromytis (angelos@dsl.cis.upenn.edu)
4 *
5 * This code was written by Angelos D. Keromytis in Philadelphia, PA, USA,
6 * in April-May 1998
7 *
8 * Copyright (C) 1998, 1999 by Angelos D. Keromytis.
9 *
10 * Permission to use, copy, and modify this software with or without fee
11 * is hereby granted, provided that this entire notice is included in
12 * all copies of any software which is or includes a copy or
13 * modification of this software.
14 *
15 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
16 * IMPLIED WARRANTY. IN PARTICULAR, THE AUTHORS MAKES NO
17 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
18 * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
19 * PURPOSE.
20 */
21%union {
22    char   *string;
23    double  doubval;
24    int     intval;
25    int     bool;
26};
27%type <bool> stringexp numexp expr floatexp
28%type <intval> NUM KOF numex afterhint notemptyprog
29%type <intval> notemptykeypredicate prog key keyexp keylist
30%type <doubval> FLOAT floatex
31%type <string> STRING VARIABLE str strnotconcat
32%token TRUE FALSE NUM FLOAT STRING VARIABLE
33%token OPENPAREN CLOSEPAREN EQQ COMMA ACTSTR LOCINI KOF KEYPRE KNVERSION
34%token DOTT SIGNERKEY HINT OPENBLOCK CLOSEBLOCK SIGNATUREENTRY PRIVATEKEY
35%token SEMICOLON TRUE FALSE
36%nonassoc EQ NE LT GT LE GE REGEXP
37%left OR
38%left AND
39%right NOT
40%left PLUS MINUS DOTT
41%left MULT DIV MOD
42%left EXP
43%nonassoc UNARYMINUS DEREF OPENNUM OPENFLT
44%start grammarswitch
45%{
46#include <sys/types.h>
47
48#include <ctype.h>
49#include <math.h>
50#include <regex.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54
55#include "keynote.h"
56#include "assertion.h"
57
58static int *keynote_kth_array = NULL;
59static int keylistcount = 0;
60
61static int   resolve_assertion(char *);
62static int   keynote_init_kth(void);
63static int   isfloatstring(char *);
64static int   checkexception(int);
65static char *my_lookup(char *);
66static int   intpow(int, int);
67static int   get_kth(int);
68%}
69%%
70
71grammarswitch: LOCINI { keynote_exceptionflag = keynote_donteval = 0; }
72                localinit
73             | ACTSTR { keynote_exceptionflag = keynote_donteval = 0; } program
74   	     | KEYPRE { keynote_exceptionflag = keynote_donteval = 0; }
75                keypredicate
76             | SIGNERKEY { keynote_exceptionflag = keynote_donteval = 0; } key
77             | SIGNATUREENTRY { keynote_exceptionflag = keynote_donteval = 0; }
78                key
79             | KNVERSION { keynote_exceptionflag = keynote_donteval = 0; }
80                        STRING { keynote_lex_remove($3);
81				 if (strcmp($3, KEYNOTE_VERSION_STRING))
82				   keynote_errno = ERROR_SYNTAX;
83				 free($3);
84			       }
85             | PRIVATEKEY { keynote_exceptionflag = keynote_donteval = 0; }
86                        STRING { keynote_lex_remove($3);
87			         keynote_privkey = $3;
88			       }
89
90keypredicate: /* Nothing */   { keynote_returnvalue = 0;
91                                return 0;
92                              }
93       | notemptykeypredicate { keynote_returnvalue = $1;
94				return 0;
95                              }
96
97notemptykeypredicate:  key     { $$ = $1; }
98       		     | keyexp  { $$ = $1; }
99
100keyexp: notemptykeypredicate AND { if (($1 == 0) && !keynote_justrecord)
101                                     keynote_donteval = 1;
102                                 } notemptykeypredicate
103                 { if ($1 > $4)
104		     $$ = $4;
105		   else
106	       	     $$ = $1;
107		   keynote_donteval = 0;
108                 }  /* Min */
109      | notemptykeypredicate OR { if (($1 == (keynote_current_session->ks_values_num - 1)) && !keynote_justrecord)
110	                             keynote_donteval = 1;
111       	                         } notemptykeypredicate
112                 { if ($1 >= $4)
113		     $$ = $1;
114		   else
115		     $$ = $4;
116		   keynote_donteval = 0;
117                 }  /* Max */
118       | OPENPAREN keyexp CLOSEPAREN { $$ = $2; }
119       | KOF { keylistcount = 0; } OPENPAREN {
120			 if (!keynote_justrecord && !keynote_donteval)
121 	                   if (keynote_init_kth() == -1)
122			     return -1;
123                       } keylist CLOSEPAREN
124                          {
125			      if (keylistcount < $1)
126			      {
127				  keynote_errno = ERROR_SYNTAX;
128				  return -1;
129			      }
130
131			    if (!keynote_justrecord && !keynote_donteval)
132			      $$ = get_kth($1);
133			    else
134			      $$ = 0;
135			  }  /* K-th */
136
137keylist: key
138	    { /* Don't do anything if we're just recording */
139              if (!keynote_justrecord && !keynote_donteval)
140		if (($1 < keynote_current_session->ks_values_num) && ($1 >= 0))
141		  keynote_kth_array[$1]++;
142
143	      keylistcount++;
144            }
145        | key COMMA keylist
146            { /* Don't do anything if we're just recording */
147	      if (!keynote_justrecord && !keynote_donteval)
148		if (($1 < keynote_current_session->ks_values_num) && ($1 >= 0))
149		  keynote_kth_array[$1]++;
150
151	      keylistcount++;
152            }
153
154key: str        {
155		   if (keynote_donteval)
156		     $$ = 0;
157		   else
158		   {
159		       keynote_lex_remove($1);
160		       if (keynote_justrecord)
161		       {
162			   if (keynote_keylist_add(&keynote_keypred_keylist,
163						   $1) == -1)
164			   {
165			       free($1);
166			       return -1;
167			   }
168
169			   $$ = 0;
170		       }
171		       else
172			 switch (keynote_in_action_authorizers($1, KEYNOTE_ALGORITHM_UNSPEC))
173			 {
174			     case -1:
175				 free($1);
176				 return -1;
177
178			     case RESULT_TRUE:
179				 free($1);
180				 $$ = keynote_current_session->ks_values_num -
181				      1;
182				 break;
183
184			     default:
185				 $$ = resolve_assertion($1);
186				 free($1);
187				 break;
188			 }
189		   }
190                 }
191
192localinit: /* Nothing */
193         | localconstants
194
195localconstants: VARIABLE EQQ STRING
196	  {
197            int i;
198
199            keynote_lex_remove($1);
200	    keynote_lex_remove($3);
201
202	    /*
203	     * Variable names starting with underscores are illegal here.
204	     */
205	    if ($1[0] == '_')
206	    {
207		free($1);
208		free($3);
209		keynote_errno = ERROR_SYNTAX;
210		return -1;
211	    }
212
213	    /* If the identifier already exists, report error. */
214	    if (keynote_env_lookup($1, &keynote_init_list, 1) != NULL)
215	    {
216		free($1);
217		free($3);
218		keynote_errno = ERROR_SYNTAX;
219		return -1;
220	    }
221
222	    i = keynote_env_add($1, $3, &keynote_init_list, 1, 0);
223	    free($1);
224	    free($3);
225
226	    if (i != RESULT_TRUE)
227	      return -1;
228	  }
229         | VARIABLE EQQ STRING
230	  {
231            int i;
232
233	    keynote_lex_remove($1);
234	    keynote_lex_remove($3);
235
236	    /*
237	     * Variable names starting with underscores are illegal here.
238	     */
239	    if ($1[0] == '_')
240	    {
241		free($1);
242		free($3);
243		keynote_errno = ERROR_SYNTAX;
244		return -1;
245	    }
246
247	    /* If the identifier already exists, report error. */
248	    if (keynote_env_lookup($1, &keynote_init_list, 1) != NULL)
249	    {
250		free($1);
251		free($3);
252		keynote_errno = ERROR_SYNTAX;
253		return -1;
254	    }
255
256	    i = keynote_env_add($1, $3, &keynote_init_list, 1, 0);
257	    free($1);
258	    free($3);
259
260	    if (i != RESULT_TRUE)
261	      return -1;
262	  } localconstants
263
264program: prog {
265	        keynote_returnvalue = $1;
266		return 0;
267	      }
268
269prog:   /* Nada */ { $$ = 0; }
270       | notemptyprog {
271			  /*
272			   * Cleanup envlist of additions such as
273			   * regexp results
274			   */
275			  keynote_env_cleanup(&keynote_temp_list, 1);
276                    } SEMICOLON prog
277                    {
278		      if ($1 > $4)
279			$$ = $1;
280		      else
281			$$ = $4;
282                    }
283
284notemptyprog: expr HINT afterhint
285              {
286		if (checkexception($1))
287		  $$ = $3;
288		else
289		  $$ = 0;
290	      }
291       |  expr
292              {
293		if (checkexception($1))
294		  $$ = keynote_current_session->ks_values_num - 1;
295		else
296		  $$ = 0;
297	      }
298
299afterhint: str {  if (keynote_exceptionflag || keynote_donteval)
300		    $$ = 0;
301		  else
302		  {
303		      keynote_lex_remove($1);
304
305		      $$ = keynote_retindex($1);
306		      if ($$ == -1)   /* Invalid return value */
307			$$ = 0;
308
309		      free($1);
310		  }
311                }
312         | OPENBLOCK prog CLOSEBLOCK { $$ = $2; }
313
314
315expr:     OPENPAREN expr CLOSEPAREN 	{ $$ = $2; }
316	| expr AND { if ($1 == 0)
317	               keynote_donteval = 1;
318	           } expr               { $$ = ($1 && $4);
319		                          keynote_donteval = 0;
320		                        }
321	| expr OR { if ($1)
322	              keynote_donteval = 1;
323	          } expr 		{ $$ = ($1 || $4);
324		                          keynote_donteval = 0;
325                                        }
326	| NOT expr 			{ $$ = !($2); }
327	| numexp 			{ $$ = $1; }
328	| floatexp			{ $$ = $1; }
329	| stringexp 			{ $$ = $1; }
330        | TRUE	  		        { $$ = 1; }
331        | FALSE	  		        { $$ = 0; }
332
333numexp:	  numex LT numex { $$ = $1 < $3; }
334	| numex GT numex { $$ = $1 > $3; }
335	| numex EQ numex { $$ = $1 == $3; }
336	| numex LE numex { $$ = $1 <= $3; }
337	| numex GE numex { $$ = $1 >= $3; }
338	| numex NE numex { $$ = $1 != $3; }
339
340floatexp: floatex LT floatex { $$ = $1 < $3; }
341	| floatex GT floatex { $$ = $1 > $3; }
342	| floatex LE floatex { $$ = $1 <= $3; }
343	| floatex GE floatex { $$ = $1 >= $3; }
344
345numex:	  numex PLUS numex  { $$ = $1 + $3; }
346	| numex MINUS numex { $$ = $1 - $3; }
347	| numex MULT numex  { $$ = $1 * $3; }
348        | numex DIV numex   { if ($3 == 0)
349	                      {
350				  if (!keynote_donteval)
351				    keynote_exceptionflag = 1;
352			      }
353	                      else
354			        $$ = ($1 / $3);
355			    }
356	| numex MOD numex   { if ($3 == 0)
357	                      {
358				  if (!keynote_donteval)
359				    keynote_exceptionflag = 1;
360			      }
361	                      else
362			        $$ = $1 % $3;
363			    }
364	| numex EXP numex   		{ $$ = intpow($1, $3); }
365	| MINUS numex %prec UNARYMINUS 	{ $$ = -($2); }
366	| OPENPAREN numex CLOSEPAREN   	{ $$ = $2; }
367	| NUM 			       	{ $$ = $1; }
368        | OPENNUM strnotconcat         	{ if (keynote_exceptionflag ||
369					      keynote_donteval)
370	                                    $$ = 0;
371 	                                  else
372					  {
373					      keynote_lex_remove($2);
374
375					      if (!isfloatstring($2))
376						$$ = 0;
377					      else
378						$$ = (int) floor(atof($2));
379					      free($2);
380					  }
381					}
382
383floatex:  floatex PLUS floatex  	{ $$ = ($1 + $3); }
384	| floatex MINUS floatex 	{ $$ = ($1 - $3); }
385	| floatex MULT floatex          { $$ = ($1 * $3); }
386        | floatex DIV floatex   	{ if ($3 == 0)
387	                                  {
388					      if (!keynote_donteval)
389						keynote_exceptionflag = 1;
390					  }
391	                                  else
392			        	   $$ = ($1 / $3);
393					}
394	| floatex EXP floatex  			{ if (!keynote_exceptionflag &&
395						      !keynote_donteval)
396	                                            $$ = pow($1, $3);
397	                                        }
398	| MINUS floatex %prec UNARYMINUS 	{ $$ = -($2); }
399	| OPENPAREN floatex CLOSEPAREN	 	{ $$ = $2; }
400	| FLOAT			       		{ $$ = $1; }
401        | OPENFLT strnotconcat          {
402	                                  if (keynote_exceptionflag ||
403					      keynote_donteval)
404					    $$ = 0.0;
405					  else
406					  {
407					      keynote_lex_remove($2);
408
409					      if (!isfloatstring($2))
410						$$ = 0.0;
411					      else
412						$$ = atof($2);
413					      free($2);
414					  }
415	                                }
416
417stringexp: str EQ str {
418                        if (keynote_exceptionflag || keynote_donteval)
419			  $$ = 0;
420			else
421			{
422			    $$ = strcmp($1, $3) == 0 ? 1 : 0;
423			    keynote_lex_remove($1);
424			    keynote_lex_remove($3);
425			    free($1);
426			    free($3);
427			}
428		      }
429	 | str NE str {
430	                if (keynote_exceptionflag || keynote_donteval)
431			  $$ = 0;
432			else
433			{
434			    $$ = strcmp($1, $3) != 0 ? 1 : 0;
435			    keynote_lex_remove($1);
436			    keynote_lex_remove($3);
437			    free($1);
438			    free($3);
439			}
440		      }
441	 | str LT str {
442	                if (keynote_exceptionflag || keynote_donteval)
443			  $$ = 0;
444			else
445			{
446			    $$ = strcmp($1, $3) < 0 ? 1 : 0;
447			    keynote_lex_remove($1);
448			    keynote_lex_remove($3);
449			    free($1);
450			    free($3);
451			}
452		      }
453	 | str GT str {
454	                if (keynote_exceptionflag || keynote_donteval)
455			  $$ = 0;
456			else
457			{
458			    $$ = strcmp($1, $3) > 0 ? 1 : 0;
459			    keynote_lex_remove($1);
460			    keynote_lex_remove($3);
461			    free($1);
462			    free($3);
463			}
464		      }
465	 | str LE str {
466	                if (keynote_exceptionflag || keynote_donteval)
467			  $$ = 0;
468			else
469			{
470			    $$ = strcmp($1, $3) <= 0 ? 1 : 0;
471			    keynote_lex_remove($1);
472			    keynote_lex_remove($3);
473			    free($1);
474			    free($3);
475			}
476		      }
477	 | str GE str {
478	                if (keynote_exceptionflag || keynote_donteval)
479			  $$ = 0;
480			else
481			{
482			    $$ = strcmp($1, $3) >= 0 ? 1 : 0;
483			    keynote_lex_remove($1);
484			    keynote_lex_remove($3);
485			    free($1);
486			    free($3);
487			}
488		      }
489	 | str REGEXP str
490            {
491	      regmatch_t pmatch[32];
492	      char grp[10], *gr;
493	      regex_t preg;
494	      int i;
495
496	      if (keynote_exceptionflag || keynote_donteval)
497		$$ = 0;
498	      else
499	      {
500		  keynote_lex_remove($1);
501		  keynote_lex_remove($3);
502
503		  memset(pmatch, 0, sizeof(pmatch));
504		  memset(grp, 0, sizeof(grp));
505
506		  if (regcomp(&preg, $3, REG_EXTENDED))
507		  {
508		      free($1);
509		      free($3);
510		      keynote_exceptionflag = 1;
511		  }
512		  else
513		  {
514		      /* Clean-up residuals from previous regexps */
515		      keynote_env_cleanup(&keynote_temp_list, 1);
516
517		      free($3);
518		      i = regexec(&preg, $1, 32, pmatch, 0);
519		      $$ = (i == 0 ? 1 : 0);
520		      if (i == 0)
521		      {
522			  snprintf(grp, sizeof grp, "%lu",
523			        (unsigned long)preg.re_nsub);
524			  if (keynote_env_add("_0", grp, &keynote_temp_list,
525					      1, 0) != RESULT_TRUE)
526			  {
527			      free($1);
528			      regfree(&preg);
529			      return -1;
530			  }
531
532			  for (i = 1; i < 32 && pmatch[i].rm_so != -1; i++)
533			  {
534			      gr = calloc(pmatch[i].rm_eo - pmatch[i].rm_so +
535					  1, sizeof(char));
536			      if (gr == NULL)
537			      {
538				  free($1);
539				  regfree(&preg);
540				  keynote_errno = ERROR_MEMORY;
541				  return -1;
542			      }
543
544			      strncpy(gr, $1 + pmatch[i].rm_so,
545				      pmatch[i].rm_eo - pmatch[i].rm_so);
546			      gr[pmatch[i].rm_eo - pmatch[i].rm_so] = '\0';
547			      snprintf(grp, sizeof grp, "_%d", i);
548			      if (keynote_env_add(grp, gr, &keynote_temp_list,
549						  1, 0) == -1)
550			      {
551				  free($1);
552				  regfree(&preg);
553				  free(gr);
554				  return -1;
555			      }
556			      else
557				free(gr);
558			  }
559		      }
560
561		      regfree(&preg);
562		      free($1);
563		  }
564	      }
565	    }
566
567str: str DOTT str    {  if (keynote_exceptionflag || keynote_donteval)
568			  $$ = NULL;
569			else
570			{
571			    int len = strlen($1) + strlen($3) + 1;
572			    $$ = calloc(len, sizeof(char));
573			    keynote_lex_remove($1);
574			    keynote_lex_remove($3);
575			    if ($$ == NULL)
576			    {
577				free($1);
578				free($3);
579				keynote_errno = ERROR_MEMORY;
580				return -1;
581			    }
582			    snprintf($$, len, "%s%s", $1, $3);
583			    free($1);
584			    free($3);
585			    if (keynote_lex_add($$, LEXTYPE_CHAR) == -1)
586			      return -1;
587			}
588		      }
589	| strnotconcat { $$ = $1; }
590
591strnotconcat: STRING 	                { $$ = $1; }
592        | OPENPAREN str CLOSEPAREN 	{ $$ = $2; }
593        | VARIABLE      {  if (keynote_exceptionflag || keynote_donteval)
594	                     $$ = NULL;
595 	                   else
596			   {
597			       $$ = my_lookup($1);
598			       keynote_lex_remove($1);
599			       free($1);
600			       if ($$ == NULL)
601			       {
602				   if (keynote_errno)
603				     return -1;
604				   $$ = strdup("");
605			       }
606			       else
607				 $$ = strdup($$);
608
609			       if ($$ == NULL)
610			       {
611				   keynote_errno = ERROR_MEMORY;
612				   return -1;
613			       }
614
615			       if (keynote_lex_add($$, LEXTYPE_CHAR) == -1)
616				 return -1;
617			   }
618	                 }
619	| DEREF str      {  if (keynote_exceptionflag || keynote_donteval)
620			      $$ = NULL;
621			    else
622			    {
623				$$ = my_lookup($2);
624				keynote_lex_remove($2);
625				free($2);
626				if ($$ == NULL)
627				{
628				    if (keynote_errno)
629				      return -1;
630				    $$ = strdup("");
631				}
632				else
633				  $$ = strdup($$);
634
635				if ($$ == NULL)
636				{
637				    keynote_errno = ERROR_MEMORY;
638				    return -1;
639				}
640
641				if (keynote_lex_add($$, LEXTYPE_CHAR) == -1)
642				  return -1;
643			    }
644			 }
645%%
646
647/*
648 * Find all assertions signed by s and give us the one with the highest
649 * return value.
650 */
651static int
652resolve_assertion(char *s)
653{
654    int i, alg = KEYNOTE_ALGORITHM_NONE, p = 0;
655    void *key = (void *) s;
656    struct assertion *as;
657    struct keylist *kl;
658
659    kl = keynote_keylist_find(keynote_current_assertion->as_keylist, s);
660    if (kl != NULL)
661    {
662	alg = kl->key_alg;
663	key = kl->key_key;
664    }
665
666    for (i = 0;; i++)
667    {
668	as = keynote_find_assertion(key, i, alg);
669	if (as == NULL)  /* Gone through all of them */
670	  return p;
671
672	if (as->as_kresult == KRESULT_DONE)
673	  if (p < as->as_result)
674	    p = as->as_result;
675
676	/* Short circuit if we find an assertion with maximum return value */
677	if (p == (keynote_current_session->ks_values_num - 1))
678	  return p;
679    }
680
681    return 0;
682}
683
684/*
685 * Environment variable lookup.
686 */
687static char *
688my_lookup(char *s)
689{
690    struct keynote_session *ks = keynote_current_session;
691    char *ret;
692
693    if (!strcmp(s, "_MIN_TRUST"))
694    {
695	keynote_used_variable = 1;
696	return ks->ks_values[0];
697    }
698    else
699    {
700	if (!strcmp(s, "_MAX_TRUST"))
701	{
702	    keynote_used_variable = 1;
703	    return ks->ks_values[ks->ks_values_num - 1];
704	}
705	else
706	{
707	    if (!strcmp(s, "_VALUES"))
708	    {
709		keynote_used_variable = 1;
710		return keynote_env_lookup("_VALUES", ks->ks_env_table,
711					  HASHTABLESIZE);
712	    }
713	    else
714	    {
715		if (!strcmp(s, "_ACTION_AUTHORIZERS"))
716		{
717		    keynote_used_variable = 1;
718		    return keynote_env_lookup("_ACTION_AUTHORIZERS",
719					      ks->ks_env_table, HASHTABLESIZE);
720		}
721	    }
722	}
723    }
724
725    /* Temporary list (regexp results) */
726    if (keynote_temp_list != NULL)
727    {
728	ret = keynote_env_lookup(s, &keynote_temp_list, 1);
729	if (ret != NULL)
730	  return ret;
731	else
732	  if (keynote_errno != 0)
733	    return NULL;
734    }
735
736    /* Local-Constants */
737    if (keynote_init_list != NULL)
738    {
739	ret = keynote_env_lookup(s, &keynote_init_list, 1);
740	if (ret != NULL)
741	  return ret;
742	else
743	  if (keynote_errno != 0)
744	    return NULL;
745    }
746
747    if (ks != NULL)
748    {
749	/* Action environment */
750	ret = keynote_env_lookup(s, ks->ks_env_table, HASHTABLESIZE);
751	if (ret != NULL)
752	{
753	    keynote_used_variable = 1;
754	    return ret;
755	}
756	else
757	  if (keynote_errno != 0)
758	    return NULL;
759    }
760
761    /* Regex table */
762    if ((ks != NULL) && (ks->ks_env_regex != NULL))
763    {
764	ret = keynote_env_lookup(s, &(ks->ks_env_regex), 1);
765	if (ret != NULL)
766	{
767	    keynote_used_variable = 1;
768	    return ret;
769	}
770
771	return NULL;
772    }
773
774    return NULL;
775}
776
777/*
778 * If we had an exception, the boolean expression should return false.
779 * Otherwise, return the result of the expression (the argument).
780 */
781static int
782checkexception(int i)
783{
784    if (keynote_exceptionflag)
785    {
786	keynote_exceptionflag = 0;
787	return 0;
788    }
789    else
790      return i;
791}
792
793
794/*
795 * Integer exponentiation -- copied from Schneier's AC2, page 244.
796 */
797static int
798intpow(int x, int y)
799{
800    int s = 1;
801
802    /*
803     * x^y with y < 0 is equivalent to 1/(x^y), which for
804     * integer arithmetic is 0.
805     */
806    if (y < 0)
807      return 0;
808
809    while (y)
810    {
811	if (y & 1)
812	  s *= x;
813
814	y >>= 1;
815	x *= x;
816    }
817
818    return s;
819}
820
821/*
822 * Check whether the string is a floating point number.
823 */
824static int
825isfloatstring(char *s)
826{
827    int i, point = 0;
828
829    for (i = strlen(s) - 1; i >= 0; i--)
830      if (!isdigit((unsigned char)s[i]))
831      {
832	  if (s[i] == '.')
833	  {
834	      if (point == 1)
835	        return 0;
836	      else
837	        point = 1;
838	  }
839	  else
840	    return 0;
841      }
842
843    return 1;
844}
845
846/*
847 * Initialize array for threshold search.
848 */
849static int
850keynote_init_kth(void)
851{
852    int i = keynote_current_session->ks_values_num;
853
854    if (i == -1)
855      return -1;
856
857    keynote_kth_array = calloc(i, sizeof(int));
858    if (keynote_kth_array == NULL)
859    {
860	keynote_errno = ERROR_MEMORY;
861	return -1;
862    }
863
864    return RESULT_TRUE;
865}
866
867/*
868 * Get the k-th best return value.
869 */
870static int
871get_kth(int k)
872{
873    int i;
874
875    for (i = keynote_current_session->ks_values_num - 1; i >= 0; i--)
876    {
877	k -= keynote_kth_array[i];
878
879	if (k <= 0)
880	  return i;
881    }
882
883    return 0;
884}
885
886/*
887 * Cleanup array.
888 */
889void
890keynote_cleanup_kth(void)
891{
892    if (keynote_kth_array != NULL)
893    {
894	free(keynote_kth_array);
895	keynote_kth_array = NULL;
896    }
897}
898
899void
900knerror(char *s)
901{}
902