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