1 %{
   2 /*
   3  * CDDL HEADER START
   4  *
   5  * The contents of this file are subject to the terms of the
   6  * Common Development and Distribution License, Version 1.0 only
   7  * (the "License").  You may not use this file except in compliance
   8  * with the License.
   9  *
  10  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  11  * or http://www.opensolaris.org/os/licensing.
  12  * See the License for the specific language governing permissions
  13  * and limitations under the License.
  14  *
  15  * When distributing Covered Code, include this CDDL HEADER in each
  16  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  17  * If applicable, add the following below this CDDL HEADER, with the
  18  * fields enclosed by brackets "[]" replaced with your own identifying
  19  * information: Portions Copyright [yyyy] [name of copyright owner]
  20  *
  21  * CDDL HEADER END
  22  */
  23 
  24 #ifndef lint
  25 #pragma ident   "%Z%%M% %I%     %E% SMI"
  26 #endif
  27 
  28 /*
  29  * Copyright (c) 1999 by Sun Microsystems, Inc.
  30  * All rights reserved.
  31  */
  32 
  33 #include <sys/param.h>
  34 #include <ctype.h>
  35 #include <stdio.h>
  36 #include <search.h>
  37 #include <string.h>
  38 #include <malloc.h>
  39 #include <fcntl.h>
  40 #include <stdlib.h>
  41 #include <errno.h>
  42 #include <unistd.h>
  43 #include <sys/kbd.h>
  44 #include <sys/kbio.h>
  45 
  46 #define ALL     -1      /* special symbol for all tables */
  47 
  48 /*
  49  * SunOS 4.x and Solaris 2.[1234] put Type 4 key tables into
  50  * the keytables directory with no type qualification.
  51  * If we're a SPARC, we might be using an NFS server that
  52  * doesn't have the new type-qualified directories.
  53  * (loadkeys wasn't used on non-SPARCs in 2.[1234].)
  54  */
  55 #ifdef  sparc
  56 #define COMPATIBILITY_DIR
  57 #endif
  58 
  59 static char     keytable_dir[] = "/usr/share/lib/keytables/type_%d/";
  60 #ifdef  COMPATIBILITY_DIR
  61 static char     keytable_dir2[] = "/usr/share/lib/keytables/";
  62 #endif
  63 static char     layout_prefix[] = "layout_";
  64 
  65 struct keyentry {
  66         struct keyentry *ke_next;
  67         struct kiockeymap ke_entry;
  68 };
  69 
  70 typedef struct keyentry keyentry;
  71 
  72 static keyentry *firstentry;
  73 static keyentry *lastentry;
  74 
  75 struct dupentry {
  76         struct dupentry *de_next;
  77         int     de_station;
  78         int     de_otherstation;
  79 };
  80 
  81 typedef struct dupentry dupentry;
  82 
  83 static dupentry *firstduplicate;
  84 static dupentry *lastduplicate;
  85 
  86 static dupentry *firstswap;
  87 static dupentry *lastswap;
  88 
  89 static char     *infilename;
  90 static FILE     *infile;
  91 static int      lineno;
  92 static int      begline;
  93 
  94 static char     *strings[16] = {
  95         "\033[H",               /* HOMEARROW */
  96         "\033[A",               /* UPARROW */
  97         "\033[B",               /* DOWNARROW */
  98         "\033[D",               /* LEFTARROW */
  99         "\033[C",               /* RIGHTARROW */
 100 };
 101 
 102 static int      nstrings = 5;   /* start out with 5 strings */
 103 
 104 typedef enum {
 105         SM_INVALID,     /* this shift mask is invalid for this keyboard */
 106         SM_NORMAL,      /* "normal", valid shift mask */
 107         SM_NUMLOCK,     /* "Num Lock" shift mask */
 108         SM_UP           /* "Up" shift mask */
 109 } smtype_t;
 110 
 111 typedef struct {
 112         int     sm_mask;
 113         smtype_t sm_type;
 114 } smentry_t;
 115 
 116 static  smentry_t shiftmasks[] = {
 117         { 0,            SM_NORMAL },
 118         { SHIFTMASK,    SM_NORMAL },
 119         { CAPSMASK,     SM_NORMAL },
 120         { CTRLMASK,     SM_NORMAL },
 121         { ALTGRAPHMASK, SM_NORMAL },
 122         { NUMLOCKMASK,  SM_NUMLOCK },
 123         { UPMASK,       SM_UP },
 124 };
 125 
 126 
 127 #define NSHIFTS (sizeof (shiftmasks) / sizeof (shiftmasks[0]))
 128 
 129 static void     enter_mapentry(int station, keyentry *entrylistp);
 130 static keyentry *makeentry(int tablemask, int entry);
 131 static int      loadkey(int kbdfd, keyentry *kep);
 132 static int      dupkey(int kbdfd, dupentry *dep, int shiftmask);
 133 static int      swapkey(int kbdfd, dupentry *dep, int shiftmask);
 134 static int      yylex();
 135 static int      readesc(FILE *stream, int delim, int single_char);
 136 static int      wordcmp(const void *w1, const void *w2);
 137 static int      yyerror(char *msg);
 138 static void     usage(void);
 139 static void     set_layout(char *arg);
 140 static FILE     *open_mapping_file(char *pathbuf, char *name,
 141                         boolean_t explicit_name, int type);
 142 
 143 int
 144 main(argc, argv)
 145         int argc;
 146         char **argv;
 147 {
 148         register int kbdfd;
 149         int type;
 150         int layout;
 151         /* maxint is 8 hex digits. */
 152         char layout_filename[sizeof(layout_prefix)+8];
 153         char pathbuf[MAXPATHLEN];
 154         register int shift;
 155         struct kiockeymap mapentry;
 156         register keyentry *kep;
 157         register dupentry *dep;
 158         boolean_t explicit_name;
 159 
 160         while(++argv, --argc) {
 161                 if(argv[0][0] != '-') break;
 162                 switch(argv[0][1]) {
 163                 case 'e':
 164                         /* -e obsolete, silently ignore */
 165                         break;
 166                 case 's':
 167                         if (argc != 2) {
 168                                 usage();
 169                                 /* NOTREACHED */
 170                         }
 171                         set_layout(argv[1]);
 172                         exit(0);
 173                 default:
 174                         usage();
 175                         /* NOTREACHED */
 176                 }
 177         }
 178 
 179         if (argc > 1) usage();
 180 
 181         if ((kbdfd = open("/dev/kbd", O_WRONLY)) < 0) {
 182                 /* perror("loadkeys: /dev/kbd"); */
 183                 return (1);
 184         }
 185 
 186         if (ioctl(kbdfd, KIOCTYPE, &type) < 0) {
 187                 /*
 188                  * There may not be a keyboard connected,
 189                  * return silently
 190                  */
 191                 return (1);
 192         }
 193 
 194         if (argc == 0) {
 195                 /* If no keyboard detected, exit silently. */
 196                 if (type == -1)
 197                         return (0);
 198 
 199                 if (ioctl(kbdfd, KIOCLAYOUT, &layout) < 0) {
 200                         perror("loadkeys: ioctl(KIOCLAYOUT)");
 201                         return (1);
 202                 }
 203 
 204                 (void) sprintf(layout_filename,
 205                                 "%s%.2x", layout_prefix, layout);
 206                 infilename = layout_filename;
 207                 explicit_name = B_FALSE;
 208         } else {
 209                 infilename = argv[0];
 210                 explicit_name = B_TRUE;
 211         }
 212 
 213         infile = open_mapping_file(pathbuf, infilename, explicit_name, type);
 214         if (infile == NULL) return (1);
 215 
 216         infilename = pathbuf;
 217 
 218         lineno = 0;
 219         begline = 1;
 220         yyparse();
 221         fclose(infile);
 222 
 223         /*
 224          * See which shift masks are valid for this keyboard.
 225          * We do that by trying to get the entry for keystation 0 and that
 226          * shift mask; if the "ioctl" fails, we assume it's because the shift
 227          * mask is invalid.
 228          */
 229         for (shift = 0; shift < NSHIFTS; shift++) {
 230                 mapentry.kio_tablemask =
 231                     shiftmasks[shift].sm_mask;
 232                 mapentry.kio_station = 0;
 233                 if (ioctl(kbdfd, KIOCGKEY, &mapentry) < 0)
 234                         shiftmasks[shift].sm_type = SM_INVALID;
 235         }
 236 
 237         for (kep = firstentry; kep != NULL; kep = kep->ke_next) {
 238                 if (kep->ke_entry.kio_tablemask == ALL) {
 239                         for (shift = 0; shift < NSHIFTS; shift++) {
 240                                 switch (shiftmasks[shift].sm_type) {
 241 
 242                                 case SM_INVALID:
 243                                         continue;
 244 
 245                                 case SM_NUMLOCK:
 246                                         /*
 247                                          * Defaults to NONL, not to a copy of
 248                                          * the base entry.
 249                                          */
 250                                         if (kep->ke_entry.kio_entry != HOLE)
 251                                                 kep->ke_entry.kio_entry = NONL;
 252                                         break;
 253 
 254                                 case SM_UP:
 255                                         /*
 256                                          * Defaults to NOP, not to a copy of
 257                                          * the base entry.
 258                                          */
 259                                         if (kep->ke_entry.kio_entry != HOLE)
 260                                                 kep->ke_entry.kio_entry = NOP;
 261                                         break;
 262                                 }
 263                                 kep->ke_entry.kio_tablemask =
 264                                     shiftmasks[shift].sm_mask;
 265                                 if (!loadkey(kbdfd, kep))
 266                                         return (1);
 267                         }
 268                 } else {
 269                         if (!loadkey(kbdfd, kep))
 270                                 return (1);
 271                 }
 272         }
 273 
 274         for (dep = firstswap; dep != NULL; dep = dep->de_next) {
 275                 for (shift = 0; shift < NSHIFTS; shift++) {
 276                         if (shiftmasks[shift].sm_type != SM_INVALID) {
 277                                 if (!swapkey(kbdfd, dep,
 278                                     shiftmasks[shift].sm_mask))
 279                                         return (0);
 280                         }
 281                 }
 282         }
 283 
 284         for (dep = firstduplicate; dep != NULL; dep = dep->de_next) {
 285                 for (shift = 0; shift < NSHIFTS; shift++) {
 286                         if (shiftmasks[shift].sm_type != SM_INVALID) {
 287                                 if (!dupkey(kbdfd, dep,
 288                                     shiftmasks[shift].sm_mask))
 289                                         return (0);
 290                         }
 291                 }
 292         }
 293 
 294         close(kbdfd);
 295         return (0);
 296 }
 297 
 298 static void
 299 usage()
 300 {
 301         (void) fprintf(stderr, "usage: loadkeys [ file ]\n");
 302         exit(1);
 303 }
 304 
 305 static void
 306 set_layout(char *arg)
 307 {
 308         int layout;
 309         int ret;
 310         int kbdfd;
 311 
 312         layout = (int) strtol(arg, &arg, 0);
 313         if (*arg != '\0') {
 314                 fprintf(stderr, "usage:  loadkeys -s layoutnumber\n");
 315                 exit(1);
 316         }
 317 
 318         if ((kbdfd = open("/dev/kbd", O_WRONLY)) < 0) {
 319                 perror("/dev/kbd");
 320                 exit(1);
 321         }
 322 
 323         ret = ioctl(kbdfd, KIOCSLAYOUT, layout);
 324         if (ret == -1) {
 325                 perror("KIOCSLAYOUT");
 326         }
 327 
 328         close(kbdfd);
 329 }
 330 
 331 /*
 332  * Attempt to find the specified mapping file.  Return a FILE * if found,
 333  * else print a message on stderr and return NULL.
 334  */
 335 FILE *
 336 open_mapping_file(
 337         char *pathbuf,
 338         char *name,
 339         boolean_t explicit_name,
 340         int type
 341 ) {
 342         /* If the user specified the name, try it "raw". */
 343         if (explicit_name) {
 344                 strcpy(pathbuf, name);
 345                 infile = fopen(pathbuf, "r");
 346                 if (infile) return (infile);
 347                 if (errno != ENOENT) goto fopen_fail;
 348         }
 349 
 350         /* Everything after this point applies only to relative names. */
 351         if (*name == '/') goto fopen_fail;
 352 
 353         /* Try the type-qualified directory name. */
 354         sprintf(pathbuf, keytable_dir, type);
 355         if ((int)(strlen(pathbuf) + strlen(name) + 1) >= MAXPATHLEN) {
 356                 (void) fprintf(stderr, "loadkeys: Name %s is too long\n",
 357                                 name);
 358                 return (NULL);
 359         }
 360         (void) strcat(pathbuf, name);
 361         infile = fopen(pathbuf, "r");
 362         if (infile) return (infile);
 363         if (errno != ENOENT) goto fopen_fail;
 364 
 365 #ifdef  COMPATIBILITY_DIR
 366         /* If not, and either the name was specified explicitly */
 367         /*     or this is a type 4... */
 368         if (explicit_name || type == KB_SUN4) {
 369                 /* Try the compatibility name. */
 370                 /* No need to check len here, it's shorter. */
 371                 (void) strcpy(pathbuf, keytable_dir2);
 372                 (void) strcat(pathbuf, infilename);
 373                 infile = fopen(pathbuf, "r");
 374                 if (infile) return (infile);
 375                 if (errno != ENOENT) goto fopen_fail;
 376         }
 377 #endif
 378 
 379 fopen_fail:
 380         (void) fprintf(stderr, "loadkeys: ");
 381         perror(name);
 382         return (NULL);
 383 }
 384 
 385 /*
 386  * We have a list of entries for a given keystation, and the keystation number
 387  * for that keystation; put that keystation number into all the entries in that
 388  * list, and chain that list to the end of the main list of entries.
 389  */
 390 static void
 391 enter_mapentry(station, entrylistp)
 392         int station;
 393         keyentry *entrylistp;
 394 {
 395         register keyentry *kep;
 396 
 397         if (lastentry == NULL)
 398                 firstentry = entrylistp;
 399         else
 400                 lastentry->ke_next = entrylistp;
 401         kep = entrylistp;
 402         for (;;) {
 403                 kep->ke_entry.kio_station = (u_char)station;
 404                 if (kep->ke_next == NULL) {
 405                         lastentry = kep;
 406                         break;
 407                 }
 408                 kep = kep->ke_next;
 409         }
 410 }
 411 
 412 /*
 413  * Allocate and fill in a new entry.
 414  */
 415 static keyentry *
 416 makeentry(tablemask, entry)
 417         int tablemask;
 418         int entry;
 419 {
 420         register keyentry *kep;
 421         register int index;
 422 
 423         if ((kep = (keyentry *) malloc((unsigned)sizeof (keyentry))) == NULL)
 424                 yyerror("out of memory for entries");
 425         kep->ke_next = NULL;
 426         kep->ke_entry.kio_tablemask = tablemask;
 427         kep->ke_entry.kio_station = 0;
 428         kep->ke_entry.kio_entry = (u_short)entry;
 429         index = entry - STRING;
 430         if (index >= 0 && index <= 15)
 431                 (void) strncpy(kep->ke_entry.kio_string, strings[index],
 432                     KTAB_STRLEN);
 433         return (kep);
 434 }
 435 
 436 /*
 437  * Make a set of entries for a keystation that indicate that that keystation's
 438  * settings should be copied from another keystation's settings.
 439  */
 440 static void
 441 duplicate_mapentry(station, otherstation)
 442         int station;
 443         int otherstation;
 444 {
 445         register dupentry *dep;
 446 
 447         if ((dep = (dupentry *) malloc((unsigned)sizeof (dupentry))) == NULL)
 448                 yyerror("out of memory for entries");
 449 
 450         if (lastduplicate == NULL)
 451                 firstduplicate = dep;
 452         else
 453                 lastduplicate->de_next = dep;
 454         lastduplicate = dep;
 455         dep->de_next = NULL;
 456         dep->de_station = station;
 457         dep->de_otherstation = otherstation;
 458 }
 459 
 460 /*
 461  * Make a set of entries for a keystation that indicate that that keystation's
 462  * settings should be swapped with another keystation's settings.
 463  */
 464 static void
 465 swap_mapentry(station, otherstation)
 466         int station;
 467         int otherstation;
 468 {
 469         register dupentry *dep;
 470 
 471         if ((dep = (dupentry *) malloc((unsigned)sizeof (dupentry))) == NULL)
 472                 yyerror("out of memory for entries");
 473 
 474         if (lastswap == NULL)
 475                 firstswap = dep;
 476         else
 477                 lastswap->de_next = dep;
 478         lastswap = dep;
 479         dep->de_next = NULL;
 480         dep->de_station = station;
 481         dep->de_otherstation = otherstation;
 482 }
 483 
 484 static int
 485 loadkey(kbdfd, kep)
 486         int kbdfd;
 487         register keyentry *kep;
 488 {
 489         if (ioctl(kbdfd, KIOCSKEY, &kep->ke_entry) < 0) {
 490                 perror("loadkeys: ioctl(KIOCSKEY)");
 491                 return (0);
 492         }
 493         return (1);
 494 }
 495 
 496 static int
 497 dupkey(kbdfd, dep, shiftmask)
 498         int kbdfd;
 499         register dupentry *dep;
 500         int shiftmask;
 501 {
 502         struct kiockeymap entry;
 503 
 504         entry.kio_tablemask = shiftmask;
 505         entry.kio_station = dep->de_otherstation;
 506         if (ioctl(kbdfd, KIOCGKEY, &entry) < 0) {
 507                 perror("loadkeys: ioctl(KIOCGKEY)");
 508                 return (0);
 509         }
 510         entry.kio_station = dep->de_station;
 511         if (ioctl(kbdfd, KIOCSKEY, &entry) < 0) {
 512                 perror("loadkeys: ioctl(KIOCSKEY)");
 513                 return (0);
 514         }
 515         return (1);
 516 }
 517 
 518 
 519 
 520 static int
 521 swapkey(kbdfd, dep, shiftmask)
 522         int kbdfd;
 523         register dupentry *dep;
 524         int shiftmask;
 525 {
 526         struct kiockeymap entry1, entry2;
 527 
 528         entry1.kio_tablemask = shiftmask;
 529         entry1.kio_station = dep->de_station;
 530         if (ioctl(kbdfd, KIOCGKEY, &entry1) < 0) {
 531                 perror("loadkeys: ioctl(KIOCGKEY)");
 532                 return (0);
 533         }
 534         entry2.kio_tablemask = shiftmask;
 535         entry2.kio_station = dep->de_otherstation;
 536         if (ioctl(kbdfd, KIOCGKEY, &entry2) < 0) {
 537                 perror("loadkeys: ioctl(KIOCGKEY)");
 538                 return (0);
 539         }
 540         entry1.kio_station = dep->de_otherstation;
 541         if (ioctl(kbdfd, KIOCSKEY, &entry1) < 0) {
 542                 perror("loadkeys: ioctl(KIOCSKEY)");
 543                 return (0);
 544         }
 545         entry2.kio_station = dep->de_station;
 546         if (ioctl(kbdfd, KIOCSKEY, &entry2) < 0) {
 547                 perror("loadkeys: ioctl(KIOCSKEY)");
 548                 return (0);
 549         }
 550         return (1);
 551 }
 552 %}
 553 
 554 %term TABLENAME INT CHAR CHARSTRING CONSTANT FKEY KEY SAME AS SWAP WITH
 555 
 556 %union {
 557         keyentry *keyentry;
 558         int     number;
 559 };
 560 
 561 %type <keyentry>  entrylist entry
 562 %type <number>            CHARSTRING CHAR INT CONSTANT FKEY TABLENAME
 563 %type <number>            code expr term number
 564 
 565 %%
 566 
 567 table:
 568         table line
 569 |       /* null */
 570 ;
 571 
 572 line:
 573         KEY number entrylist '\n'
 574                 {
 575                 enter_mapentry($2, $3);
 576                 }
 577 |       KEY number SAME AS number '\n'
 578                 {
 579                 duplicate_mapentry($2, $5);
 580                 }
 581 |       SWAP number WITH number '\n'
 582                 {
 583                 swap_mapentry($2, $4);
 584                 }
 585 |       '\n'
 586 ;
 587 
 588 entrylist:
 589         entrylist entry
 590                 {
 591                 /*
 592                  * Append this entry to the end of the entry list.
 593                  */
 594                 register keyentry *kep;
 595                 kep = $1;
 596                 for (;;) {
 597                         if (kep->ke_next == NULL) {
 598                                 kep->ke_next = $2;
 599                                 break;
 600                         }
 601                         kep = kep->ke_next;
 602                 }
 603                 $$ = $1;
 604                 }
 605 |       entry
 606                 {
 607                 $$ = $1;
 608                 }
 609 ;
 610 
 611 entry:
 612         TABLENAME code
 613                 {
 614                 $$ = makeentry($1, $2);
 615                 }
 616 ;
 617 
 618 code:
 619         CHARSTRING
 620                 {
 621                 $$ = $1;
 622                 }
 623 |       CHAR
 624                 {
 625                 $$ = $1;
 626                 }
 627 |       '('
 628                 {
 629                 $$ = '(';
 630                 }
 631 |       ')'
 632                 {
 633                 $$ = ')';
 634                 }
 635 |       '+'
 636                 {
 637                 $$ = '+';
 638                 }
 639 |       expr
 640                 {
 641                 $$ = $1;
 642                 }
 643 ;
 644 
 645 expr:
 646         term
 647                 {
 648                 $$ = $1;
 649                 }
 650 |       expr '+' term
 651                 {
 652                 $$ = $1 + $3;
 653                 }
 654 ;
 655 
 656 term:
 657         CONSTANT
 658                 {
 659                 $$ = $1;
 660                 }
 661 |       FKEY '(' number ')'
 662                 {
 663                 if ($3 < 1 || $3 > 16)
 664                         yyerror("invalid function key number");
 665                 $$ = $1 + $3 - 1;
 666                 }
 667 ;
 668 
 669 number:
 670         INT
 671                 {
 672                 $$ = $1;
 673                 }
 674 |       CHAR
 675                 {
 676                 if (isdigit($1))
 677                         $$ = $1 - '0';
 678                 else
 679                         yyerror("syntax error");
 680                 }
 681 ;
 682 
 683 %%
 684 
 685 typedef struct {
 686         char    *w_string;
 687         int     w_type;         /* token type */
 688         int     w_lval;         /* yylval for this token */
 689 } word_t;
 690 
 691 /*
 692  * Table must be in alphabetical order.
 693  */
 694 word_t  wordtab[] = {
 695         { "all",        TABLENAME,      ALL },
 696         { "alt",        CONSTANT,       ALT },
 697         { "altg",       TABLENAME,      ALTGRAPHMASK },
 698         { "altgraph",   CONSTANT,       ALTGRAPH },
 699         { "as",         AS,             0 },
 700         { "base",       TABLENAME,      0 },
 701         { "bf",         FKEY,           BOTTOMFUNC },
 702         { "buckybits",  CONSTANT,       BUCKYBITS },
 703         { "caps",       TABLENAME,      CAPSMASK },
 704         { "capslock",   CONSTANT,       CAPSLOCK },
 705         { "compose",    CONSTANT,       COMPOSE },
 706         { "ctrl",       TABLENAME,      CTRLMASK },
 707         { "downarrow",  CONSTANT,       DOWNARROW },
 708         { "error",      CONSTANT,       ERROR },
 709         { "fa_acute",   CONSTANT,       FA_ACUTE },
 710         { "fa_cedilla", CONSTANT,       FA_CEDILLA },
 711         { "fa_cflex",   CONSTANT,       FA_CFLEX },
 712         { "fa_grave",   CONSTANT,       FA_GRAVE },
 713         { "fa_tilde",   CONSTANT,       FA_TILDE },
 714         { "fa_umlaut",  CONSTANT,       FA_UMLAUT },
 715         { "hole",       CONSTANT,       HOLE },
 716         { "homearrow",  CONSTANT,       HOMEARROW },
 717         { "idle",       CONSTANT,       IDLE },
 718         { "key",        KEY,            0 },
 719         { "leftarrow",  CONSTANT,       LEFTARROW },
 720         { "leftctrl",   CONSTANT,       LEFTCTRL },
 721         { "leftshift",  CONSTANT,       LEFTSHIFT },
 722         { "lf",         FKEY,           LEFTFUNC },
 723         { "metabit",    CONSTANT,       METABIT },
 724         { "nonl",       CONSTANT,       NONL },
 725         { "nop",        CONSTANT,       NOP },
 726         { "numl",       TABLENAME,      NUMLOCKMASK },
 727         { "numlock",    CONSTANT,       NUMLOCK },
 728         { "oops",       CONSTANT,       OOPS },
 729         { "pad0",       CONSTANT,       PAD0 },
 730         { "pad1",       CONSTANT,       PAD1 },
 731         { "pad2",       CONSTANT,       PAD2 },
 732         { "pad3",       CONSTANT,       PAD3 },
 733         { "pad4",       CONSTANT,       PAD4 },
 734         { "pad5",       CONSTANT,       PAD5 },
 735         { "pad6",       CONSTANT,       PAD6 },
 736         { "pad7",       CONSTANT,       PAD7 },
 737         { "pad8",       CONSTANT,       PAD8 },
 738         { "pad9",       CONSTANT,       PAD9 },
 739         { "paddot",     CONSTANT,       PADDOT },
 740         { "padenter",   CONSTANT,       PADENTER },
 741         { "padequal",   CONSTANT,       PADEQUAL },
 742         { "padminus",   CONSTANT,       PADMINUS },
 743         { "padplus",    CONSTANT,       PADPLUS },
 744         { "padsep",     CONSTANT,       PADSEP },
 745         { "padslash",   CONSTANT,       PADSLASH },
 746         { "padstar",    CONSTANT,       PADSTAR },
 747         { "reset",      CONSTANT,       RESET },
 748         { "rf",         FKEY,           RIGHTFUNC },
 749         { "rightarrow", CONSTANT,       RIGHTARROW },
 750         { "rightctrl",  CONSTANT,       RIGHTCTRL },
 751         { "rightshift", CONSTANT,       RIGHTSHIFT },
 752         { "same",       SAME,           0 },
 753         { "shift",      TABLENAME,      SHIFTMASK },
 754         { "shiftkeys",  CONSTANT,       SHIFTKEYS },
 755         { "shiftlock",  CONSTANT,       SHIFTLOCK },
 756         { "string",     CONSTANT,       STRING },
 757         { "swap",       SWAP,           0 },
 758         { "systembit",  CONSTANT,       SYSTEMBIT },
 759         { "tf",         FKEY,           TOPFUNC },
 760         { "up",         TABLENAME,      UPMASK },
 761         { "uparrow",    CONSTANT,       UPARROW },
 762         { "with",       WITH,           0 },
 763 };
 764 
 765 #define NWORDS          (sizeof (wordtab) / sizeof (wordtab[0]))
 766 
 767 static int
 768 yylex()
 769 {
 770         register int c;
 771         char tokbuf[256+1];
 772         register char *cp;
 773         register int tokentype;
 774 
 775         while ((c = getc(infile)) == ' ' || c == '\t')
 776                 ;
 777         if (begline) {
 778                 lineno++;
 779                 begline = 0;
 780                 if (c == '#') {
 781                         while ((c = getc(infile)) != EOF && c != '\n')
 782                                 ;
 783                 }
 784         }
 785         if (c == EOF)
 786                 return (0);     /* end marker */
 787         if (c == '\n') {
 788                 begline = 1;
 789                 return (c);
 790         }
 791 
 792         switch (c) {
 793 
 794         case '\'':
 795                 tokentype = CHAR;
 796                 if ((c = getc(infile)) == EOF)
 797                         yyerror("unterminated character constant");
 798                 if (c == '\n') {
 799                         (void) ungetc(c, infile);
 800                         yylval.number = '\'';
 801                 } else {
 802                         switch (c) {
 803 
 804                         case '\'':
 805                                 yyerror("null character constant");
 806                                 break;
 807 
 808                         case '\\':
 809                                 yylval.number = readesc(infile, '\'', 1);
 810                                 break;
 811 
 812                         default:
 813                                 yylval.number = c;
 814                                 break;
 815                         }
 816                         if ((c = getc(infile)) == EOF || c == '\n')
 817                                 yyerror("unterminated character constant");
 818                         else if (c != '\'')
 819                                 yyerror("only one character allowed in character constant");
 820                 }
 821                 break;
 822 
 823         case '"':
 824                 if ((c = getc(infile)) == EOF)
 825                         yyerror("unterminated string constant");
 826                 if (c == '\n') {
 827                         (void) ungetc(c, infile);
 828                         tokentype = CHAR;
 829                         yylval.number = '"';
 830                 } else {
 831                         tokentype = CHARSTRING;
 832                         cp = &tokbuf[0];
 833                         do {
 834                                 if (cp > &tokbuf[256])
 835                                         yyerror("line too long");
 836                                 if (c == '\\')
 837                                         c = readesc(infile, '"', 0);
 838                                 *cp++ = (char)c;
 839                         } while ((c = getc(infile)) != EOF && c != '\n' &&
 840                                 c != '"');
 841                         if (c != '"')
 842                                 yyerror("unterminated string constant");
 843                         *cp = '\0';
 844                         if (nstrings == 16)
 845                                 yyerror("too many strings");
 846                         if ((int) strlen(tokbuf) > KTAB_STRLEN)
 847                                 yyerror("string too long");
 848                         strings[nstrings] = strdup(tokbuf);
 849                         yylval.number = STRING+nstrings;
 850                         nstrings++;
 851                 }
 852                 break;
 853 
 854         case '(':
 855         case ')':
 856         case '+':
 857                 tokentype = c;
 858                 break;
 859 
 860         case '^':
 861                 if ((c = getc(infile)) == EOF)
 862                         yyerror("missing newline at end of line");
 863                 tokentype = CHAR;
 864                 if (c == ' ' || c == '\t' || c == '\n') {
 865                         /*
 866                          * '^' by itself.
 867                          */
 868                         yylval.number = '^';
 869                 } else {
 870                         yylval.number = c & 037;
 871                         if ((c = getc(infile)) == EOF)
 872                                 yyerror("missing newline at end of line");
 873                         if (c != ' ' && c != '\t' && c != '\n')
 874                                 yyerror("invalid control character");
 875                 }
 876                 (void) ungetc(c, infile);
 877                 break;
 878 
 879         default:
 880                 cp = &tokbuf[0];
 881                 do {
 882                         if (cp > &tokbuf[256])
 883                                 yyerror("line too long");
 884                         *cp++ = (char)c;
 885                 } while ((c = getc(infile)) != EOF && (isalnum(c) || c == '_'));
 886                 if (c == EOF)
 887                         yyerror("newline missing");
 888                 (void) ungetc(c, infile);
 889                 *cp = '\0';
 890                 if (strlen(tokbuf) == 1) {
 891                         tokentype = CHAR;
 892                         yylval.number = (unsigned char)tokbuf[0];
 893                 } else if (strlen(tokbuf) == 2 && tokbuf[0] == '^') {
 894                         tokentype = CHAR;
 895                         yylval.number = (unsigned char)(tokbuf[1] & 037);
 896                 } else {
 897                         word_t word;
 898                         register word_t *wptr;
 899                         char *ptr;
 900 
 901                         for (cp = &tokbuf[0]; (c = *cp) != '\0'; cp++) {
 902                                 if (isupper(c))
 903                                         *cp = tolower(c);
 904                         }
 905                         word.w_string = tokbuf;
 906                         wptr = (word_t *)bsearch((char *)&word,
 907                             (char *)wordtab, NWORDS, sizeof (word_t),
 908                             wordcmp);
 909                         if (wptr != NULL) {
 910                                 yylval.number = wptr->w_lval;
 911                                 tokentype = wptr->w_type;
 912                         } else {
 913                                 yylval.number = strtol(tokbuf, &ptr, 10);
 914                                 if (ptr == tokbuf)
 915                                         yyerror("syntax error");
 916                                 else
 917                                         tokentype = INT;
 918                         }
 919                         break;
 920                 }
 921         }
 922 
 923         return (tokentype);
 924 }
 925 
 926 static int
 927 readesc(stream, delim, single_char)
 928         FILE *stream;
 929         int delim;
 930         int single_char;
 931 {
 932         register int c;
 933         register int val;
 934         register int i;
 935 
 936         if ((c = getc(stream)) == EOF || c == '\n')
 937                 yyerror("unterminated character constant");
 938 
 939         if (c >= '0' && c <= '7') {
 940                 val = 0;
 941                 i = 1;
 942                 for (;;) {
 943                         val = val*8 + c - '0';
 944                         if ((c = getc(stream)) == EOF || c == '\n')
 945                                 yyerror("unterminated character constant");
 946                         if (c == delim)
 947                                 break;
 948                         i++;
 949                         if (i > 3) {
 950                                 if (single_char)
 951                                         yyerror("escape sequence too long");
 952                                 else
 953                                         break;
 954                         }
 955                         if (c < '0' || c > '7') {
 956                                 if (single_char)
 957                                         yyerror("illegal character in escape sequence");
 958                                 else
 959                                         break;
 960                         }
 961                 }
 962                 (void) ungetc(c, stream);
 963         } else {
 964                 switch (c) {
 965 
 966                 case 'n':
 967                         val = '\n';
 968                         break;
 969 
 970                 case 't':
 971                         val = '\t';
 972                         break;
 973 
 974                 case 'b':
 975                         val = '\b';
 976                         break;
 977 
 978                 case 'r':
 979                         val = '\r';
 980                         break;
 981 
 982                 case 'v':
 983                         val = '\v';
 984                         break;
 985 
 986                 case '\\':
 987                         val = '\\';
 988                         break;
 989 
 990                 default:
 991                         if (c == delim)
 992                                 val = delim;
 993                         else
 994                                 yyerror("illegal character in escape sequence");
 995                 }
 996         }
 997         return (val);
 998 }
 999 
1000 static int
1001 wordcmp(const void *w1, const void *w2)
1002 {
1003         return (strcmp(
1004                 ((const word_t *)w1)->w_string,
1005                 ((const word_t *)w2)->w_string));
1006 }
1007 
1008 static int
1009 yyerror(msg)
1010         char *msg;
1011 {
1012         (void) fprintf(stderr, "%s, line %d: %s\n", infilename, lineno, msg);
1013         exit(1);
1014 }