1 | /***************************************
2 | $Revision: 1.5 $
3 |
4 | Example code: Determine which keys to look for.
5 |
6 | This is based on the C code that was reversed engineered from existing Perl
7 | code. (~ottrey/which_table/which_table.c)
8 |
9 | ******************/ /******************
10 | Copyright (c) 1999 RIPE NCC
11 |
12 | All Rights Reserved
13 |
14 | Permission to use, copy, modify, and distribute this software and its
15 | documentation for any purpose and without fee is hereby granted,
16 | provided that the above copyright notice appear in all copies and that
17 | both that copyright notice and this permission notice appear in
18 | supporting documentation, and that the name of the author not be
19 | used in advertising or publicity pertaining to distribution of the
20 | software without specific, written prior permission.
21 |
22 | THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
23 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
24 | AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
25 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
26 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
27 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
28 | ***************************************/
29 | #include <stdio.h>
30 | #include <strings.h>
31 | #include <libgen.h>
32 | #include "isnic.h"
33 | #include "bitmask.h"
34 | #include "which_keytypes.h"
35 |
36 |
37 | #define DOMAINNAME "^[ ]*[a-zA-Z0-9--]*(\\.[a-zA-Z0-9--]+)*[ ]*$"
38 |
39 | #define LEN_MIN 0
40 | #define LEN_MAX 32
41 |
42 | #define NETLEN 16
43 | #define NETQUADS 4
44 | #define NETQUAD_MIN 0
45 | #define NETQUAD_MAX 255
46 |
47 | #define ASNUM_MIN 1
48 | #define ASNUM_MAX 65535
49 | #define ASNUM_NUMOFFSET 2 /* XXX - (This is really kludgy!) Offset to the number bit of ASNUM */
50 |
51 | #define VALIDIP6 "^[0-9A-F]{1,4}(:[0-9A-F]{1,4}){7}$"
52 | /*
53 | XXX Why doesn't this work?
54 | #define NET "^([0-9]{1,3}.){4}$"
55 | */
56 | #define NET "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"
57 |
58 | #define ASNUM "^AS[1-9]+[0-9]*$"
59 |
60 | #define NETNAME "^[A-Z][A-Z0-9-]*$"
61 |
62 | #define MAINTAINER "^[A-Z][A-Z0-9-]*$"
63 |
64 | #define LIMERICK "^LIM-[A-Z0-9-]+$"
65 |
66 | #define KEYCERT "^PGPKEY-[0-9A-F]{8}$"
67 |
68 | #define ASMACRO "^AS-[A-Z]+$"
69 |
70 | #define ROUTESETNAME "^RS-[A-Z0-9-]*$"
71 |
72 | #define ASSETNAME "^AS-[A-Z0-9-]*$"
73 |
74 | #define AUTONICPREFIXREGULAR "^AUTO-"
75 |
76 | /*
77 | XXX This seems to be the same as the Perl code. But I don't see where a " " is allowed for.
78 | I.e. Perl -> ^[a-zA-Z][\w\-\.\'\|\`]*$
79 | Does \w include [ ;:,?/}{()+*#] ?
80 | #define NAME_B "^[a-zA-Z][a-zA-Z_0-9.'|`-]*$"
81 | */
82 | #define NAME_B "^[a-zA-Z][a-zA-Z_0-9.'|`;:,?/}{()+*#&-]*$"
83 |
84 | #define PHONE_A "^[ ]*[+][0-9 ]*[(]{0,1}[0-9 -]+[)]{0,1}[0-9 -]*(ext\\.){0,1}[0-9 ]*$"
85 |
86 | #define VALIDIP4PREFIX
87 |
88 | #define EMAIL "^[a-zA-Z0-9--]*@[a-zA-Z0-9--]*(\\.[a-zA-Z0-9--]+)*$"
89 |
90 |
91 | /*+ Keytype strings +*/
92 | char * const Keytypes[] = {
93 | "name",
94 | "nichdl",
95 | "email",
96 | "maint",
97 | "pgpkey",
98 | "iprange",
99 | "ip6range",
100 | "netname",
101 | "asnum",
102 | "assetname",
103 | "routesetname",
104 | "domname",
105 | "hostname",
106 | "limerickname",
107 | NULL
108 | }; /* Keytypes[] */
109 |
110 | /*+ Peerword strings +*/
111 | const char * Peerword[] = {
112 | "EGP",
113 | "BGP",
114 | "BGP4",
115 | "IDRP",
116 | "IGP",
117 | "HELLO",
118 | "IGRP",
119 | "EIGRP",
120 | "OSPF",
121 | "ISIS",
122 | "RIP",
123 | "RIP2",
124 | "OTHER",
125 | ""
126 | }; /* Peerword[] */
127 |
128 | static int matching(char *string, char left_c, char right_c) {
129 | int result;
130 |
131 | int i;
132 | int length;
133 | int count=0;
134 |
135 | length = strlen(string);
136 |
137 | for(i=0; i < length; i++) {
138 | /*
139 | switch ((int)string[i]) {
140 | case left_c:
141 | break;
142 |
143 | case right_c:
144 | count--;
145 | break;
146 |
147 | default:
148 | }
149 | */
150 | if (string[i] == left_c) {
151 | count++;
152 | }
153 | if (string[i] == right_c) {
154 | count--;
155 | }
156 | }
157 |
158 | if (count == 0) {
159 | /* Matching characters */
160 | result=1;
161 | }
162 | else {
163 | /* Non-matching characters */
164 | result=0;
165 | }
166 |
167 | return result;
168 |
169 | } /* matching() */
170 |
171 |
172 | static int perform_regex_test(const char *pattern, char *string) {
173 | char *return_value;
174 | int match;
175 |
176 | char *re;
177 |
178 | re = regcmp(pattern, (char*)0);
179 | if (regex(re, string) == NULL) {
180 | match = 0;
181 | }
182 | else {
183 | match = 1;
184 | }
185 |
186 | free(re);
187 |
188 | return match;
189 |
190 | }
191 |
192 | static int isipv6prefix_a(char *string) {
193 | /*
194 | printf("isipv6prefix\n");
195 | */
196 | int result='-';
197 |
198 | result = perform_regex_test(VALIDIP6, string);
199 |
200 | return result;
201 | }
202 |
203 | static int isipv6prefix(char *string) {
204 | /*
205 | printf("isipv6prefix\n");
206 | */
207 | int result='-';
208 |
209 | return result;
210 | }
211 |
212 | static int islen(char *string) {
213 | /*
214 | printf("islen\n");
215 | */
216 | int result='-';
217 | int length;
218 |
219 | length = strlen(string);
220 |
221 | if ((length <= LEN_MAX) && (length >= LEN_MIN)) {
222 | /* A valid length */
223 | result=1;
224 | }
225 | else if (length < 0) {
226 | /* An invalid length */
227 | result=-1;
228 | }
229 | else {
230 | /* An invalid length */
231 | result=0;
232 | }
233 |
234 | return result;
235 | }
236 |
237 | static int isnet(char *string) {
238 | /*
239 | printf("isnet\n");
240 | */
241 | int result='-';
242 | char tmp_string[NETLEN];
243 | int quad_value;
244 | char *quad_value_str;
245 |
246 | /* First check if the string is in quad form */
247 | result = perform_regex_test(NET, string);
248 |
249 | /* Then check if the quad values are between NETQUAD_MIN and NETQUAD_MAX */
250 | if (result == 1) {
251 | strncpy(tmp_string, string, NETLEN);
252 | quad_value_str = strtok(tmp_string, ".");
253 | while (quad_value_str != NULL) {
254 | quad_value = atoi(quad_value_str);
255 | if ((quad_value < NETQUAD_MIN) || (quad_value > NETQUAD_MAX)) {
256 | /* an invalid value */
257 | result=0;
258 | break;
259 | }
260 | quad_value_str = strtok(NULL, ".");
261 | }
262 | }
263 |
264 | return result;
265 | }
266 |
267 | static int isasnum(char *string) {
268 | /*
269 | printf("isasnum\n");
270 | */
271 | int result='-';
272 | int as_value;
273 |
274 | /* First check if the string matches an ASNUM */
275 | result = perform_regex_test(ASNUM, string);
276 |
277 | /* Then check if the value is between ASNUM_MIN and ASNUM_MAX */
278 | if (result == 1) {
279 | as_value = atoi(string+ASNUM_NUMOFFSET);
280 | if ((as_value < ASNUM_MIN) || (as_value > ASNUM_MAX)) {
281 | /* an invalid value */
282 | result=0;
283 | }
284 | }
285 |
286 | return result;
287 | }
288 |
289 | static int isnetname(char *string) {
290 | /*
291 | printf("isnetname\n");
292 | */
293 | int result='-';
294 |
295 | result = perform_regex_test(NETNAME, string);
296 |
297 | return result;
298 | }
299 |
300 | static int ismaintainer(char *string) {
301 | /*
302 | printf("ismaintainer\n");
303 | */
304 | int result='-';
305 |
306 | result = perform_regex_test(MAINTAINER, string);
307 |
308 | return result;
309 | }
310 |
311 | static int islimerick(char *string) {
312 | /*
313 | printf("islimerick\n");
314 | */
315 | int result='-';
316 |
317 | result = perform_regex_test(LIMERICK, string);
318 |
319 | return result;
320 | }
321 |
322 | /*******************************************************
323 | # the problem is as follows:
324 | #
325 | # we can never find out which NIC handles are possible on the
326 | # globe since we don't know that they exist
327 | #
328 | # we want to solve this with once with DNS :
329 | #
330 | # RIPE.registries.int CNAME whois.ripe.net
331 | # InterNIC.registries.int CNAME whois.internic.net
332 | # and so on...
333 |
334 | #
335 | # 1) it first does a basic syntax check
336 | #
337 | # notes:
338 | #
339 | # - catches InterNIC handles
340 | # - catches the JP|JP-JP APNIC exceptions
341 | # - limits the number of initials to three with a good reason:
342 | # we have a much better chance to find syntax errors like:
343 | # RIPE-DK13 and other problems like this
344 | #
345 | # 2) checks for valid suffixes
346 | # - all 'source:' attribute values from sites that we mirror
347 | # are allowed
348 | # - country codes are allowed for APNIC compatibility
349 | # - APNIC AP|CC-AU exceptions are handled correctly
350 | # - -ORG organization InterNIC handles
351 | # - -ARIN ARIN handles
352 | # - -ORG-ARIN ARIN handles
353 | ********************************************************/
354 | static int isnichandle_joao(char *nichdl) {
355 |
356 | char *regexp, *match;
357 | char ret[1024];
358 | char *suffix;
359 |
360 | int i;
361 |
362 | /* set ret to the empty string *.
363 | ret[0]='\0';
364 | /** Return if there are any lower case characters */
365 |
366 | regexp = regcmp("[a-z]",(char *)0);
367 | match = regex(regexp,nichdl);
368 | free(regexp);
369 | if (match) return 0;
370 |
371 | /*
372 | # Japanese NIC handles
373 | #
374 | # leading zeros in the number part *are* allowed
375 | #
376 | # e.g. AB021JP AB199JP-JP
377 | #
378 | */
379 | regexp = regcmp("[A-Z]{2}[0-9]{3}JP(-JP){0,1}",(char *)0);
380 | match = regex(regexp,nichdl);
381 | free(regexp);
382 | if (match) return 1;
383 |
384 | /*
385 | # Standard NIC handles
386 | #
387 | # leading zeros in the number part are *not* allowed
388 | #
389 | # InterNIC - TBQ, IP4
390 | # RIPE format - AB1-RIPE
391 | # APNIC use two letter country code suffix
392 | # Austraila have used -1-AU, -2-AU, -CC-AU suffix.
393 | # Internic used -ORG suffix
394 | # ARIN use -ARIN suffix
395 | # ARIN also use -ORG-ARIN suffix
396 | #
397 | */
398 | regexp = regcmp("^[A-Z]{2,4}([1-9][0-9]{0,5}){0,1}((-[^ ]+){0,1})$0$",(char *)0);
399 | match = regex(regexp,nichdl,ret);
400 |
401 | free(regexp);
402 |
403 | if (match == NULL) {
404 | return 0;
405 | } else {
406 | if (ret[0] == '\0') {
407 | return 1;
408 | } else {
409 | /* strip leading '-' */
410 | suffix = ret+1;
411 | /* suffix of local sources */
412 | for (i=0;i<=NUM_NICPOSTFIX;i++) {
413 | if ( !strcmp(suffix,nicpostfix[i]) ) {
414 | return 1;
415 | }
416 | }
417 | /* country codes */
418 | for (i=0;i<NUM_COUNTRIES;i++) {
419 | if ( !strcmp(suffix,countries[i]) ) {
420 | return 1;
421 | }
422 | }
423 | /* special suffix */
424 | for (i=0;i<NUM_SPECIAL;i++) {
425 | if ( !strcmp(suffix,special[i]) ) {
426 | return 1;
427 | }
428 | }
429 | }
430 | }
431 | return 0;
432 | } /* isnichandle_joao() */
433 |
434 |
435 | static int isnichandle(char *string) {
436 | return isnichandle_joao(string);
437 | }
438 |
439 | static int isaskeyword(char *string) {
440 | /*
441 | printf("isaskeyword\n");
442 | */
443 | int result='-';
444 |
445 | return result;
446 | }
447 |
448 | static int isasmacro(char *string) {
449 | /*
450 | printf("isasmacro\n");
451 | */
452 | int result='-';
453 |
454 | result = perform_regex_test(ASMACRO, string);
455 |
456 | return result;
457 | }
458 |
459 | static int isclnskeyword(char *string) {
460 | /*
461 | printf("isclnskeyword\n");
462 | */
463 | int result='-';
464 |
465 | return result;
466 | }
467 |
468 | static int ispeerkeyword(char *string) {
469 | /*
470 | printf("ispeerkeyword\n");
471 | */
472 | int result='-';
473 | int i;
474 |
475 | result=0;
476 | for (i=0; Peerword[i] != ""; i++) {
477 | if ( strcmp(Peerword[i], string) == 0 ) {
478 | result=1;
479 | break;
480 | }
481 | }
482 |
483 | return result;
484 | }
485 |
486 | static int isnetlist(char *string) {
487 | /*
488 | printf("isnetlist\n");
489 | */
490 | int result='-';
491 |
492 | return result;
493 | }
494 |
495 | static int iscommunity(char *string) {
496 | /*
497 | printf("iscommunity\n");
498 | */
499 | int result='-';
500 |
501 | return result;
502 | }
503 |
504 | static int isaspref(char *string) {
505 | /*
506 | printf("isaspref\n");
507 | */
508 | int result='-';
509 |
510 | return result;
511 | }
512 |
513 | static int isnetnum(char *string) {
514 | /*
515 | printf("isnetnum\n");
516 | */
517 | int result='-';
518 |
519 | /* XXX - I don't see the difference between isnet and isnetnum */
520 | result=isnet(string);
521 |
522 | return result;
523 | }
524 |
525 | static int isipaddr(char *string) {
526 | /*
527 | printf("isipaddr\n");
528 | */
529 | int result='-';
530 |
531 | return result;
532 | }
533 |
534 | static int ismask(char *string) {
535 | /*
536 | printf("ismask\n");
537 | */
538 | int result='-';
539 |
540 | return result;
541 | }
542 |
543 | static int isclnsprefix(char *string) {
544 | /*
545 | printf("isclnsprefix\n");
546 | */
547 | int result='-';
548 |
549 | return result;
550 | }
551 |
552 | static int issubdomname(char *string) {
553 | /*
554 | printf("issubdomname\n");
555 | */
556 | int result='-';
557 |
558 | result = perform_regex_test(DOMAINNAME, string);
559 |
560 | return result;
561 | }
562 |
563 | static int isdomname(char *string) {
564 | /*
565 | printf("isdomname\n");
566 | */
567 | int result='-';
568 |
569 | result = perform_regex_test(DOMAINNAME, string);
570 |
571 | return result;
572 | }
573 |
574 | /*
575 | I split the isname up into isname_a & isname_b. And created isname_ab to join them together.
576 | - So I can test it properly. -ottrey
577 | */
578 | static int isname_a(char *string) {
579 | /*
580 | printf("isname_a\n");
581 | */
582 | int result='-';
583 |
584 | result = perform_regex_test(AUTONICPREFIXREGULAR, string);
585 |
586 | return result;
587 | }
588 |
589 | static int isname_b(char *string) {
590 | /*
591 | printf("isname_b\n");
592 | */
593 | int result='-';
594 |
595 | result = perform_regex_test(NAME_B, string);
596 |
597 | return result;
598 | }
599 |
600 | static int isname_ab(char *string) {
601 | /*
602 | printf("isname_ab\n");
603 | */
604 | int result='-';
605 |
606 | /* Note: the different logic here because I use 0 to be a match and 1 to not be a match.
607 | The Perl code uses the opposite. - ottrey */
608 | result = !(isname_a(string) && !isname_b(string));
609 |
610 | return result;
611 | }
612 |
613 | static int isname(char *string) {
614 | /*
615 | printf("isname\n");
616 | */
617 | int result='-';
618 |
619 | return result;
620 | }
621 |
622 | static int isphone_a(char *string) {
623 | /*
624 | printf("isphone_a\n");
625 | */
626 | int result='-';
627 |
628 | result = perform_regex_test(PHONE_A, string);
629 |
630 | return result;
631 | }
632 | static int isphone_b(char *string) {
633 | /*
634 | printf("isphone_b\n");
635 | */
636 | int result='-';
637 |
638 | result = isparen(string);
639 |
640 | return result;
641 | }
642 | static int isphone_ab(char *string) {
643 | /*
644 | printf("isphone_ab\n");
645 | */
646 | int result='-';
647 |
648 | /* Note: the different logic here because I use 0 to be a match and 1 to not be a match.
649 | The Perl code uses the opposite. - ottrey */
650 | result = !(!isphone_a(string) && !isphone_b(string));
651 |
652 | return result;
653 | }
654 | static int isphone(char *string) {
655 | /*
656 | printf("isphone\n");
657 | */
658 | int result='-';
659 |
660 | return result;
661 | }
662 |
663 | static int isemail(char *string) {
664 | /*
665 | printf("isemail\n");
666 | */
667 | int result='-';
668 |
669 | result = perform_regex_test(EMAIL, string);
670 |
671 | return result;
672 | }
673 |
674 | static int isbrace(char *string) {
675 | /*
676 | printf("isbrace\n");
677 | */
678 | int result='-';
679 |
680 | result=matching(string, '{', '}');
681 |
682 | return result;
683 | }
684 |
685 | static int isparen(char *string) {
686 | /*
687 | printf("isparen\n");
688 | */
689 | int result='-';
690 |
691 | result=matching(string, '(', ')');
692 |
693 | return result;
694 | }
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 | /* ****** The new bunch ******* */
709 | static int wk_is_name(char *key) {
710 |
711 | /* Everything matches to name */
712 | return 1;
713 |
714 | } /* wk_is_name() */
715 |
716 | static int wk_is_nichdl(char *key) {
717 |
718 | return isnichandle(key);
719 |
720 | } /* wk_is_nichdl() */
721 |
722 | static int wk_is_email(char *key) {
723 |
724 | return isemail(key);
725 |
726 | } /* wk_is_email() */
727 |
728 | static int wk_is_maint(char *key) {
729 |
730 | return ismaintainer(key);
731 |
732 | } /* wk_is_maint() */
733 |
734 | static int wk_is_keycert(char *key) {
735 | int result=1;
736 |
737 | result = perform_regex_test(KEYCERT, key);
738 |
739 | return result;
740 |
741 | } /* wk_is_pgpkey() */
742 |
743 | static int wk_is_iprange(char *key) {
744 | int result=1;
745 |
746 | /* XXX This is not very strict - but will cut out a lot of invalids */
747 | /* XXX And has been given a bad name and does a few things. */
748 | /* XXX This needs work. */
749 | #define IPRANGE "^[0-9./ -]*$"
750 | result = perform_regex_test(IPRANGE, key);
751 |
752 | return result;
753 |
754 | } /* wk_is_iprange() */
755 |
756 | static int wk_is_ip6range(char *key) {
757 |
758 | return isipv6prefix_a(key);
759 |
760 | } /* wk_is_ip6range() */
761 |
762 | static int wk_is_netname(char *key) {
763 |
764 | return isnetname(key);
765 |
766 | } /* wk_is_netname() */
767 |
768 | static int wk_is_asnum(char *key) {
769 |
770 | return isasnum(key);
771 |
772 | } /* wk_is_asnum() */
773 |
774 | static int wk_is_assetname(char *key) {
775 | int result=1;
776 |
777 | result = perform_regex_test(ASSETNAME, key);
778 |
779 | return result;
780 |
781 | } /* wk_is_assetname() */
782 |
783 | static int wk_is_routesetname(char *key) {
784 | int result=1;
785 |
786 | result = perform_regex_test(ROUTESETNAME, key);
787 |
788 | return result;
789 |
790 | } /* wk_is_routesetname() */
791 |
792 | static int wk_is_domname(char *key) {
793 |
794 | return isdomname(key);
795 |
796 | } /* wk_is_domname() */
797 |
798 | static int wk_is_hostname(char *key) {
799 |
800 | /* XXX Why is there a hostname & a domainname? */
801 | /* Answer - hostname can be a domainname or an IP */
802 | return (isdomname(key) || wk_is_iprange(key));
803 |
804 | } /* wk_is_hostname() */
805 |
806 | static int wk_is_limerickname(char *key) {
807 |
808 | return islimerick(key);
809 |
810 | } /* wk_is_limerickname() */
811 |
812 |
813 | /* WK_to_string() */
814 | /*++++++++++++++++++++++++++++++++++++++
815 | Convert the which keytypes bitmap into a string.
816 |
817 | mask_t wk The which keytypes mask to be converted.
818 |
819 | More:
820 | +html+ <PRE>
821 | Authors:
822 | ottrey
823 | +html+ </PRE><DL COMPACT>
824 | +html+ <DT>Online References:
825 | +html+ <DD><UL>
826 | +html+ </UL></DL>
827 |
828 | ++++++++++++++++++++++++++++++++++++++*/
829 | char *WK_to_string(mask_t wk) {
830 |
831 | return MA_to_string(wk, Keytypes, 1, 0);
832 |
833 | } /* WK_to_string() */
834 |
835 |
836 | /* WK_new() */
837 | /*++++++++++++++++++++++++++++++++++++++
838 | Create a new which keytypes bitmap.
839 |
840 | char *key The key to be examined.
841 |
842 | More:
843 | +html+ <PRE>
844 | Authors:
845 | ottrey
846 | +html+ </PRE><DL COMPACT>
847 | +html+ <DT>Online References:
848 | +html+ <DD><UL>
849 | +html+ </UL></DL>
850 |
851 | ++++++++++++++++++++++++++++++++++++++*/
852 | mask_t WK_new(char *key) {
853 | mask_t wk;
854 |
855 | wk = MA_new(MA_END);
856 |
857 | MA_set(&wk, WK_NAME, wk_is_name(key));
858 | MA_set(&wk, WK_NICHDL, wk_is_nichdl(key));
859 | MA_set(&wk, WK_EMAIL, wk_is_email(key));
860 | MA_set(&wk, WK_MAINT, wk_is_maint(key));
861 | MA_set(&wk, WK_KEYCERT, wk_is_keycert(key));
862 | MA_set(&wk, WK_IPRANGE, wk_is_iprange(key));
863 | MA_set(&wk, WK_IP6RANGE, wk_is_ip6range(key));
864 | MA_set(&wk, WK_NETNAME, wk_is_netname(key));
865 | MA_set(&wk, WK_ASNUM, wk_is_asnum(key));
866 | MA_set(&wk, WK_ASSETNAME, wk_is_assetname(key));
867 | MA_set(&wk, WK_ROUTESETNAME, wk_is_routesetname(key));
868 | MA_set(&wk, WK_DOMNAME, wk_is_domname(key));
869 | MA_set(&wk, WK_HOSTNAME, wk_is_hostname(key));
870 | MA_set(&wk, WK_LIMERICKNAME, wk_is_limerickname(key));
871 |
872 | return wk;
873 |
874 | } /* WK_new() */