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
58 static int *keynote_kth_array = NULL;
59 static int keylistcount = 0;
60
61 static int resolve_assertion(char *);
62 static int keynote_init_kth(void);
63 static int isfloatstring(char *);
64 static int checkexception(int);
65 static char *my_lookup(char *);
66 static int intpow(int, int);
67 static int get_kth(int);
68 %}
69 %%
70
71 grammarswitch: 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
90 keypredicate: /* Nothing */ { keynote_returnvalue = 0;
91 return 0;
92 }
93 | notemptykeypredicate { keynote_returnvalue = $1;
94 return 0;
95 }
96
97 notemptykeypredicate: key { $$ = $1; }
98 | keyexp { $$ = $1; }
99
100 keyexp: 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
137 keylist: 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
154 key: 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
192 localinit: /* Nothing */
193 | localconstants
194
195 localconstants: 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
264 program: prog {
265 keynote_returnvalue = $1;
266 return 0;
267 }
268
269 prog: /* 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
284 notemptyprog: 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
299 afterhint: 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
315 expr: 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
333 numexp: 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
340 floatexp: floatex LT floatex { $$ = $1 < $3; }
341 | floatex GT floatex { $$ = $1 > $3; }
342 | floatex LE floatex { $$ = $1 <= $3; }
343 | floatex GE floatex { $$ = $1 >= $3; }
344
345 numex: 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
383 floatex: 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
417 stringexp: 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
567 str: 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
591 strnotconcat: 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 */
651 static int
652 resolve_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 */
687 static char *
my_lookup(char * s)688 my_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 */
781 static int
checkexception(int i)782 checkexception(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 */
797 static int
intpow(int x,int y)798 intpow(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 */
824 static int
isfloatstring(char * s)825 isfloatstring(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 */
849 static int
keynote_init_kth(void)850 keynote_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 */
870 static int
get_kth(int k)871 get_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 */
889 void
keynote_cleanup_kth(void)890 keynote_cleanup_kth(void)
891 {
892 if (keynote_kth_array != NULL)
893 {
894 free(keynote_kth_array);
895 keynote_kth_array = NULL;
896 }
897 }
898
899 void
knerror(char * s)900 knerror(char *s)
901 {}
902