summaryrefslogtreecommitdiff
path: root/usr/src/cmd/loadkeys/loadkeys.y
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/loadkeys/loadkeys.y
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/loadkeys/loadkeys.y')
-rw-r--r--usr/src/cmd/loadkeys/loadkeys.y1014
1 files changed, 1014 insertions, 0 deletions
diff --git a/usr/src/cmd/loadkeys/loadkeys.y b/usr/src/cmd/loadkeys/loadkeys.y
new file mode 100644
index 0000000000..2795f1f7bf
--- /dev/null
+++ b/usr/src/cmd/loadkeys/loadkeys.y
@@ -0,0 +1,1014 @@
+%{
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+#ifndef lint
+#pragma ident "%Z%%M% %I% %E% SMI"
+#endif
+
+/*
+ * Copyright (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#include <sys/param.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <search.h>
+#include <string.h>
+#include <malloc.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/kbd.h>
+#include <sys/kbio.h>
+
+#define ALL -1 /* special symbol for all tables */
+
+/*
+ * SunOS 4.x and Solaris 2.[1234] put Type 4 key tables into
+ * the keytables directory with no type qualification.
+ * If we're a SPARC, we might be using an NFS server that
+ * doesn't have the new type-qualified directories.
+ * (loadkeys wasn't used on non-SPARCs in 2.[1234].)
+ */
+#ifdef sparc
+#define COMPATIBILITY_DIR
+#endif
+
+static char keytable_dir[] = "/usr/share/lib/keytables/type_%d/";
+#ifdef COMPATIBILITY_DIR
+static char keytable_dir2[] = "/usr/share/lib/keytables/";
+#endif
+static char layout_prefix[] = "layout_";
+
+struct keyentry {
+ struct keyentry *ke_next;
+ struct kiockeymap ke_entry;
+};
+
+typedef struct keyentry keyentry;
+
+static keyentry *firstentry;
+static keyentry *lastentry;
+
+struct dupentry {
+ struct dupentry *de_next;
+ int de_station;
+ int de_otherstation;
+};
+
+typedef struct dupentry dupentry;
+
+static dupentry *firstduplicate;
+static dupentry *lastduplicate;
+
+static dupentry *firstswap;
+static dupentry *lastswap;
+
+static char *infilename;
+static FILE *infile;
+static int lineno;
+static int begline;
+
+static char *strings[16] = {
+ "\033[H", /* HOMEARROW */
+ "\033[A", /* UPARROW */
+ "\033[B", /* DOWNARROW */
+ "\033[D", /* LEFTARROW */
+ "\033[C", /* RIGHTARROW */
+};
+
+static int nstrings = 5; /* start out with 5 strings */
+
+typedef enum {
+ SM_INVALID, /* this shift mask is invalid for this keyboard */
+ SM_NORMAL, /* "normal", valid shift mask */
+ SM_NUMLOCK, /* "Num Lock" shift mask */
+ SM_UP /* "Up" shift mask */
+} smtype_t;
+
+typedef struct {
+ int sm_mask;
+ smtype_t sm_type;
+} smentry_t;
+
+static smentry_t shiftmasks[] = {
+ { 0, SM_NORMAL },
+ { SHIFTMASK, SM_NORMAL },
+ { CAPSMASK, SM_NORMAL },
+ { CTRLMASK, SM_NORMAL },
+ { ALTGRAPHMASK, SM_NORMAL },
+ { NUMLOCKMASK, SM_NUMLOCK },
+ { UPMASK, SM_UP },
+};
+
+
+#define NSHIFTS (sizeof (shiftmasks) / sizeof (shiftmasks[0]))
+
+static void enter_mapentry(int station, keyentry *entrylistp);
+static keyentry *makeentry(int tablemask, int entry);
+static int loadkey(int kbdfd, keyentry *kep);
+static int dupkey(int kbdfd, dupentry *dep, int shiftmask);
+static int swapkey(int kbdfd, dupentry *dep, int shiftmask);
+static int yylex();
+static int readesc(FILE *stream, int delim, int single_char);
+static int wordcmp(const void *w1, const void *w2);
+static int yyerror(char *msg);
+static void usage(void);
+static void set_layout(char *arg);
+static FILE *open_mapping_file(char *pathbuf, char *name,
+ boolean_t explicit_name, int type);
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register int kbdfd;
+ int type;
+ int layout;
+ /* maxint is 8 hex digits. */
+ char layout_filename[sizeof(layout_prefix)+8];
+ char pathbuf[MAXPATHLEN];
+ register int shift;
+ struct kiockeymap mapentry;
+ register keyentry *kep;
+ register dupentry *dep;
+ boolean_t explicit_name;
+
+ while(++argv, --argc) {
+ if(argv[0][0] != '-') break;
+ switch(argv[0][1]) {
+ case 'e':
+ /* -e obsolete, silently ignore */
+ break;
+ case 's':
+ if (argc != 2) {
+ usage();
+ /* NOTREACHED */
+ }
+ set_layout(argv[1]);
+ exit(0);
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ if (argc > 1) usage();
+
+ if ((kbdfd = open("/dev/kbd", O_WRONLY)) < 0) {
+ /* perror("loadkeys: /dev/kbd"); */
+ return (1);
+ }
+
+ if (ioctl(kbdfd, KIOCTYPE, &type) < 0) {
+ /*
+ * There may not be a keyboard connected,
+ * return silently
+ */
+ return (1);
+ }
+
+ if (argc == 0) {
+ /* If no keyboard detected, exit silently. */
+ if (type == -1)
+ return (0);
+
+ if (ioctl(kbdfd, KIOCLAYOUT, &layout) < 0) {
+ perror("loadkeys: ioctl(KIOCLAYOUT)");
+ return (1);
+ }
+
+ (void) sprintf(layout_filename,
+ "%s%.2x", layout_prefix, layout);
+ infilename = layout_filename;
+ explicit_name = B_FALSE;
+ } else {
+ infilename = argv[0];
+ explicit_name = B_TRUE;
+ }
+
+ infile = open_mapping_file(pathbuf, infilename, explicit_name, type);
+ if (infile == NULL) return (1);
+
+ infilename = pathbuf;
+
+ lineno = 0;
+ begline = 1;
+ yyparse();
+ fclose(infile);
+
+ /*
+ * See which shift masks are valid for this keyboard.
+ * We do that by trying to get the entry for keystation 0 and that
+ * shift mask; if the "ioctl" fails, we assume it's because the shift
+ * mask is invalid.
+ */
+ for (shift = 0; shift < NSHIFTS; shift++) {
+ mapentry.kio_tablemask =
+ shiftmasks[shift].sm_mask;
+ mapentry.kio_station = 0;
+ if (ioctl(kbdfd, KIOCGKEY, &mapentry) < 0)
+ shiftmasks[shift].sm_type = SM_INVALID;
+ }
+
+ for (kep = firstentry; kep != NULL; kep = kep->ke_next) {
+ if (kep->ke_entry.kio_tablemask == ALL) {
+ for (shift = 0; shift < NSHIFTS; shift++) {
+ switch (shiftmasks[shift].sm_type) {
+
+ case SM_INVALID:
+ continue;
+
+ case SM_NUMLOCK:
+ /*
+ * Defaults to NONL, not to a copy of
+ * the base entry.
+ */
+ if (kep->ke_entry.kio_entry != HOLE)
+ kep->ke_entry.kio_entry = NONL;
+ break;
+
+ case SM_UP:
+ /*
+ * Defaults to NOP, not to a copy of
+ * the base entry.
+ */
+ if (kep->ke_entry.kio_entry != HOLE)
+ kep->ke_entry.kio_entry = NOP;
+ break;
+ }
+ kep->ke_entry.kio_tablemask =
+ shiftmasks[shift].sm_mask;
+ if (!loadkey(kbdfd, kep))
+ return (1);
+ }
+ } else {
+ if (!loadkey(kbdfd, kep))
+ return (1);
+ }
+ }
+
+ for (dep = firstswap; dep != NULL; dep = dep->de_next) {
+ for (shift = 0; shift < NSHIFTS; shift++) {
+ if (shiftmasks[shift].sm_type != SM_INVALID) {
+ if (!swapkey(kbdfd, dep,
+ shiftmasks[shift].sm_mask))
+ return (0);
+ }
+ }
+ }
+
+ for (dep = firstduplicate; dep != NULL; dep = dep->de_next) {
+ for (shift = 0; shift < NSHIFTS; shift++) {
+ if (shiftmasks[shift].sm_type != SM_INVALID) {
+ if (!dupkey(kbdfd, dep,
+ shiftmasks[shift].sm_mask))
+ return (0);
+ }
+ }
+ }
+
+ close(kbdfd);
+ return (0);
+}
+
+static void
+usage()
+{
+ (void) fprintf(stderr, "usage: loadkeys [ file ]\n");
+ exit(1);
+}
+
+static void
+set_layout(char *arg)
+{
+ int layout;
+ int ret;
+ int kbdfd;
+
+ layout = (int) strtol(arg, &arg, 0);
+ if (*arg != '\0') {
+ fprintf(stderr, "usage: loadkeys -s layoutnumber\n");
+ exit(1);
+ }
+
+ if ((kbdfd = open("/dev/kbd", O_WRONLY)) < 0) {
+ perror("/dev/kbd");
+ exit(1);
+ }
+
+ ret = ioctl(kbdfd, KIOCSLAYOUT, layout);
+ if (ret == -1) {
+ perror("KIOCSLAYOUT");
+ }
+
+ close(kbdfd);
+}
+
+/*
+ * Attempt to find the specified mapping file. Return a FILE * if found,
+ * else print a message on stderr and return NULL.
+ */
+FILE *
+open_mapping_file(
+ char *pathbuf,
+ char *name,
+ boolean_t explicit_name,
+ int type
+) {
+ /* If the user specified the name, try it "raw". */
+ if (explicit_name) {
+ strcpy(pathbuf, name);
+ infile = fopen(pathbuf, "r");
+ if (infile) return (infile);
+ if (errno != ENOENT) goto fopen_fail;
+ }
+
+ /* Everything after this point applies only to relative names. */
+ if (*name == '/') goto fopen_fail;
+
+ /* Try the type-qualified directory name. */
+ sprintf(pathbuf, keytable_dir, type);
+ if ((int)(strlen(pathbuf) + strlen(name) + 1) >= MAXPATHLEN) {
+ (void) fprintf(stderr, "loadkeys: Name %s is too long\n",
+ name);
+ return (NULL);
+ }
+ (void) strcat(pathbuf, name);
+ infile = fopen(pathbuf, "r");
+ if (infile) return (infile);
+ if (errno != ENOENT) goto fopen_fail;
+
+#ifdef COMPATIBILITY_DIR
+ /* If not, and either the name was specified explicitly */
+ /* or this is a type 4... */
+ if (explicit_name || type == KB_SUN4) {
+ /* Try the compatibility name. */
+ /* No need to check len here, it's shorter. */
+ (void) strcpy(pathbuf, keytable_dir2);
+ (void) strcat(pathbuf, infilename);
+ infile = fopen(pathbuf, "r");
+ if (infile) return (infile);
+ if (errno != ENOENT) goto fopen_fail;
+ }
+#endif
+
+fopen_fail:
+ (void) fprintf(stderr, "loadkeys: ");
+ perror(name);
+ return (NULL);
+}
+
+/*
+ * We have a list of entries for a given keystation, and the keystation number
+ * for that keystation; put that keystation number into all the entries in that
+ * list, and chain that list to the end of the main list of entries.
+ */
+static void
+enter_mapentry(station, entrylistp)
+ int station;
+ keyentry *entrylistp;
+{
+ register keyentry *kep;
+
+ if (lastentry == NULL)
+ firstentry = entrylistp;
+ else
+ lastentry->ke_next = entrylistp;
+ kep = entrylistp;
+ for (;;) {
+ kep->ke_entry.kio_station = (u_char)station;
+ if (kep->ke_next == NULL) {
+ lastentry = kep;
+ break;
+ }
+ kep = kep->ke_next;
+ }
+}
+
+/*
+ * Allocate and fill in a new entry.
+ */
+static keyentry *
+makeentry(tablemask, entry)
+ int tablemask;
+ int entry;
+{
+ register keyentry *kep;
+ register int index;
+
+ if ((kep = (keyentry *) malloc((unsigned)sizeof (keyentry))) == NULL)
+ yyerror("out of memory for entries");
+ kep->ke_next = NULL;
+ kep->ke_entry.kio_tablemask = tablemask;
+ kep->ke_entry.kio_station = 0;
+ kep->ke_entry.kio_entry = (u_short)entry;
+ index = entry - STRING;
+ if (index >= 0 && index <= 15)
+ (void) strncpy(kep->ke_entry.kio_string, strings[index],
+ KTAB_STRLEN);
+ return (kep);
+}
+
+/*
+ * Make a set of entries for a keystation that indicate that that keystation's
+ * settings should be copied from another keystation's settings.
+ */
+static void
+duplicate_mapentry(station, otherstation)
+ int station;
+ int otherstation;
+{
+ register dupentry *dep;
+
+ if ((dep = (dupentry *) malloc((unsigned)sizeof (dupentry))) == NULL)
+ yyerror("out of memory for entries");
+
+ if (lastduplicate == NULL)
+ firstduplicate = dep;
+ else
+ lastduplicate->de_next = dep;
+ lastduplicate = dep;
+ dep->de_next = NULL;
+ dep->de_station = station;
+ dep->de_otherstation = otherstation;
+}
+
+/*
+ * Make a set of entries for a keystation that indicate that that keystation's
+ * settings should be swapped with another keystation's settings.
+ */
+static void
+swap_mapentry(station, otherstation)
+ int station;
+ int otherstation;
+{
+ register dupentry *dep;
+
+ if ((dep = (dupentry *) malloc((unsigned)sizeof (dupentry))) == NULL)
+ yyerror("out of memory for entries");
+
+ if (lastswap == NULL)
+ firstswap = dep;
+ else
+ lastswap->de_next = dep;
+ lastswap = dep;
+ dep->de_next = NULL;
+ dep->de_station = station;
+ dep->de_otherstation = otherstation;
+}
+
+static int
+loadkey(kbdfd, kep)
+ int kbdfd;
+ register keyentry *kep;
+{
+ if (ioctl(kbdfd, KIOCSKEY, &kep->ke_entry) < 0) {
+ perror("loadkeys: ioctl(KIOCSKEY)");
+ return (0);
+ }
+ return (1);
+}
+
+static int
+dupkey(kbdfd, dep, shiftmask)
+ int kbdfd;
+ register dupentry *dep;
+ int shiftmask;
+{
+ struct kiockeymap entry;
+
+ entry.kio_tablemask = shiftmask;
+ entry.kio_station = dep->de_otherstation;
+ if (ioctl(kbdfd, KIOCGKEY, &entry) < 0) {
+ perror("loadkeys: ioctl(KIOCGKEY)");
+ return (0);
+ }
+ entry.kio_station = dep->de_station;
+ if (ioctl(kbdfd, KIOCSKEY, &entry) < 0) {
+ perror("loadkeys: ioctl(KIOCSKEY)");
+ return (0);
+ }
+ return (1);
+}
+
+
+
+static int
+swapkey(kbdfd, dep, shiftmask)
+ int kbdfd;
+ register dupentry *dep;
+ int shiftmask;
+{
+ struct kiockeymap entry1, entry2;
+
+ entry1.kio_tablemask = shiftmask;
+ entry1.kio_station = dep->de_station;
+ if (ioctl(kbdfd, KIOCGKEY, &entry1) < 0) {
+ perror("loadkeys: ioctl(KIOCGKEY)");
+ return (0);
+ }
+ entry2.kio_tablemask = shiftmask;
+ entry2.kio_station = dep->de_otherstation;
+ if (ioctl(kbdfd, KIOCGKEY, &entry2) < 0) {
+ perror("loadkeys: ioctl(KIOCGKEY)");
+ return (0);
+ }
+ entry1.kio_station = dep->de_otherstation;
+ if (ioctl(kbdfd, KIOCSKEY, &entry1) < 0) {
+ perror("loadkeys: ioctl(KIOCSKEY)");
+ return (0);
+ }
+ entry2.kio_station = dep->de_station;
+ if (ioctl(kbdfd, KIOCSKEY, &entry2) < 0) {
+ perror("loadkeys: ioctl(KIOCSKEY)");
+ return (0);
+ }
+ return (1);
+}
+%}
+
+%term TABLENAME INT CHAR CHARSTRING CONSTANT FKEY KEY SAME AS SWAP WITH
+
+%union {
+ keyentry *keyentry;
+ int number;
+};
+
+%type <keyentry> entrylist entry
+%type <number> CHARSTRING CHAR INT CONSTANT FKEY TABLENAME
+%type <number> code expr term number
+
+%%
+
+table:
+ table line
+| /* null */
+;
+
+line:
+ KEY number entrylist '\n'
+ {
+ enter_mapentry($2, $3);
+ }
+| KEY number SAME AS number '\n'
+ {
+ duplicate_mapentry($2, $5);
+ }
+| SWAP number WITH number '\n'
+ {
+ swap_mapentry($2, $4);
+ }
+| '\n'
+;
+
+entrylist:
+ entrylist entry
+ {
+ /*
+ * Append this entry to the end of the entry list.
+ */
+ register keyentry *kep;
+ kep = $1;
+ for (;;) {
+ if (kep->ke_next == NULL) {
+ kep->ke_next = $2;
+ break;
+ }
+ kep = kep->ke_next;
+ }
+ $$ = $1;
+ }
+| entry
+ {
+ $$ = $1;
+ }
+;
+
+entry:
+ TABLENAME code
+ {
+ $$ = makeentry($1, $2);
+ }
+;
+
+code:
+ CHARSTRING
+ {
+ $$ = $1;
+ }
+| CHAR
+ {
+ $$ = $1;
+ }
+| '('
+ {
+ $$ = '(';
+ }
+| ')'
+ {
+ $$ = ')';
+ }
+| '+'
+ {
+ $$ = '+';
+ }
+| expr
+ {
+ $$ = $1;
+ }
+;
+
+expr:
+ term
+ {
+ $$ = $1;
+ }
+| expr '+' term
+ {
+ $$ = $1 + $3;
+ }
+;
+
+term:
+ CONSTANT
+ {
+ $$ = $1;
+ }
+| FKEY '(' number ')'
+ {
+ if ($3 < 1 || $3 > 16)
+ yyerror("invalid function key number");
+ $$ = $1 + $3 - 1;
+ }
+;
+
+number:
+ INT
+ {
+ $$ = $1;
+ }
+| CHAR
+ {
+ if (isdigit($1))
+ $$ = $1 - '0';
+ else
+ yyerror("syntax error");
+ }
+;
+
+%%
+
+typedef struct {
+ char *w_string;
+ int w_type; /* token type */
+ int w_lval; /* yylval for this token */
+} word_t;
+
+/*
+ * Table must be in alphabetical order.
+ */
+word_t wordtab[] = {
+ { "all", TABLENAME, ALL },
+ { "alt", CONSTANT, ALT },
+ { "altg", TABLENAME, ALTGRAPHMASK },
+ { "altgraph", CONSTANT, ALTGRAPH },
+ { "as", AS, 0 },
+ { "base", TABLENAME, 0 },
+ { "bf", FKEY, BOTTOMFUNC },
+ { "buckybits", CONSTANT, BUCKYBITS },
+ { "caps", TABLENAME, CAPSMASK },
+ { "capslock", CONSTANT, CAPSLOCK },
+ { "compose", CONSTANT, COMPOSE },
+ { "ctrl", TABLENAME, CTRLMASK },
+ { "downarrow", CONSTANT, DOWNARROW },
+ { "error", CONSTANT, ERROR },
+ { "fa_acute", CONSTANT, FA_ACUTE },
+ { "fa_cedilla", CONSTANT, FA_CEDILLA },
+ { "fa_cflex", CONSTANT, FA_CFLEX },
+ { "fa_grave", CONSTANT, FA_GRAVE },
+ { "fa_tilde", CONSTANT, FA_TILDE },
+ { "fa_umlaut", CONSTANT, FA_UMLAUT },
+ { "hole", CONSTANT, HOLE },
+ { "homearrow", CONSTANT, HOMEARROW },
+ { "idle", CONSTANT, IDLE },
+ { "key", KEY, 0 },
+ { "leftarrow", CONSTANT, LEFTARROW },
+ { "leftctrl", CONSTANT, LEFTCTRL },
+ { "leftshift", CONSTANT, LEFTSHIFT },
+ { "lf", FKEY, LEFTFUNC },
+ { "metabit", CONSTANT, METABIT },
+ { "nonl", CONSTANT, NONL },
+ { "nop", CONSTANT, NOP },
+ { "numl", TABLENAME, NUMLOCKMASK },
+ { "numlock", CONSTANT, NUMLOCK },
+ { "oops", CONSTANT, OOPS },
+ { "pad0", CONSTANT, PAD0 },
+ { "pad1", CONSTANT, PAD1 },
+ { "pad2", CONSTANT, PAD2 },
+ { "pad3", CONSTANT, PAD3 },
+ { "pad4", CONSTANT, PAD4 },
+ { "pad5", CONSTANT, PAD5 },
+ { "pad6", CONSTANT, PAD6 },
+ { "pad7", CONSTANT, PAD7 },
+ { "pad8", CONSTANT, PAD8 },
+ { "pad9", CONSTANT, PAD9 },
+ { "paddot", CONSTANT, PADDOT },
+ { "padenter", CONSTANT, PADENTER },
+ { "padequal", CONSTANT, PADEQUAL },
+ { "padminus", CONSTANT, PADMINUS },
+ { "padplus", CONSTANT, PADPLUS },
+ { "padsep", CONSTANT, PADSEP },
+ { "padslash", CONSTANT, PADSLASH },
+ { "padstar", CONSTANT, PADSTAR },
+ { "reset", CONSTANT, RESET },
+ { "rf", FKEY, RIGHTFUNC },
+ { "rightarrow", CONSTANT, RIGHTARROW },
+ { "rightctrl", CONSTANT, RIGHTCTRL },
+ { "rightshift", CONSTANT, RIGHTSHIFT },
+ { "same", SAME, 0 },
+ { "shift", TABLENAME, SHIFTMASK },
+ { "shiftkeys", CONSTANT, SHIFTKEYS },
+ { "shiftlock", CONSTANT, SHIFTLOCK },
+ { "string", CONSTANT, STRING },
+ { "swap", SWAP, 0 },
+ { "systembit", CONSTANT, SYSTEMBIT },
+ { "tf", FKEY, TOPFUNC },
+ { "up", TABLENAME, UPMASK },
+ { "uparrow", CONSTANT, UPARROW },
+ { "with", WITH, 0 },
+};
+
+#define NWORDS (sizeof (wordtab) / sizeof (wordtab[0]))
+
+static int
+yylex()
+{
+ register int c;
+ char tokbuf[256+1];
+ register char *cp;
+ register int tokentype;
+
+ while ((c = getc(infile)) == ' ' || c == '\t')
+ ;
+ if (begline) {
+ lineno++;
+ begline = 0;
+ if (c == '#') {
+ while ((c = getc(infile)) != EOF && c != '\n')
+ ;
+ }
+ }
+ if (c == EOF)
+ return (0); /* end marker */
+ if (c == '\n') {
+ begline = 1;
+ return (c);
+ }
+
+ switch (c) {
+
+ case '\'':
+ tokentype = CHAR;
+ if ((c = getc(infile)) == EOF)
+ yyerror("unterminated character constant");
+ if (c == '\n') {
+ (void) ungetc(c, infile);
+ yylval.number = '\'';
+ } else {
+ switch (c) {
+
+ case '\'':
+ yyerror("null character constant");
+ break;
+
+ case '\\':
+ yylval.number = readesc(infile, '\'', 1);
+ break;
+
+ default:
+ yylval.number = c;
+ break;
+ }
+ if ((c = getc(infile)) == EOF || c == '\n')
+ yyerror("unterminated character constant");
+ else if (c != '\'')
+ yyerror("only one character allowed in character constant");
+ }
+ break;
+
+ case '"':
+ if ((c = getc(infile)) == EOF)
+ yyerror("unterminated string constant");
+ if (c == '\n') {
+ (void) ungetc(c, infile);
+ tokentype = CHAR;
+ yylval.number = '"';
+ } else {
+ tokentype = CHARSTRING;
+ cp = &tokbuf[0];
+ do {
+ if (cp > &tokbuf[256])
+ yyerror("line too long");
+ if (c == '\\')
+ c = readesc(infile, '"', 0);
+ *cp++ = (char)c;
+ } while ((c = getc(infile)) != EOF && c != '\n' &&
+ c != '"');
+ if (c != '"')
+ yyerror("unterminated string constant");
+ *cp = '\0';
+ if (nstrings == 16)
+ yyerror("too many strings");
+ if ((int) strlen(tokbuf) > KTAB_STRLEN)
+ yyerror("string too long");
+ strings[nstrings] = strdup(tokbuf);
+ yylval.number = STRING+nstrings;
+ nstrings++;
+ }
+ break;
+
+ case '(':
+ case ')':
+ case '+':
+ tokentype = c;
+ break;
+
+ case '^':
+ if ((c = getc(infile)) == EOF)
+ yyerror("missing newline at end of line");
+ tokentype = CHAR;
+ if (c == ' ' || c == '\t' || c == '\n') {
+ /*
+ * '^' by itself.
+ */
+ yylval.number = '^';
+ } else {
+ yylval.number = c & 037;
+ if ((c = getc(infile)) == EOF)
+ yyerror("missing newline at end of line");
+ if (c != ' ' && c != '\t' && c != '\n')
+ yyerror("invalid control character");
+ }
+ (void) ungetc(c, infile);
+ break;
+
+ default:
+ cp = &tokbuf[0];
+ do {
+ if (cp > &tokbuf[256])
+ yyerror("line too long");
+ *cp++ = (char)c;
+ } while ((c = getc(infile)) != EOF && (isalnum(c) || c == '_'));
+ if (c == EOF)
+ yyerror("newline missing");
+ (void) ungetc(c, infile);
+ *cp = '\0';
+ if (strlen(tokbuf) == 1) {
+ tokentype = CHAR;
+ yylval.number = (unsigned char)tokbuf[0];
+ } else if (strlen(tokbuf) == 2 && tokbuf[0] == '^') {
+ tokentype = CHAR;
+ yylval.number = (unsigned char)(tokbuf[1] & 037);
+ } else {
+ word_t word;
+ register word_t *wptr;
+ char *ptr;
+
+ for (cp = &tokbuf[0]; (c = *cp) != '\0'; cp++) {
+ if (isupper(c))
+ *cp = tolower(c);
+ }
+ word.w_string = tokbuf;
+ wptr = (word_t *)bsearch((char *)&word,
+ (char *)wordtab, NWORDS, sizeof (word_t),
+ wordcmp);
+ if (wptr != NULL) {
+ yylval.number = wptr->w_lval;
+ tokentype = wptr->w_type;
+ } else {
+ yylval.number = strtol(tokbuf, &ptr, 10);
+ if (ptr == tokbuf)
+ yyerror("syntax error");
+ else
+ tokentype = INT;
+ }
+ break;
+ }
+ }
+
+ return (tokentype);
+}
+
+static int
+readesc(stream, delim, single_char)
+ FILE *stream;
+ int delim;
+ int single_char;
+{
+ register int c;
+ register int val;
+ register int i;
+
+ if ((c = getc(stream)) == EOF || c == '\n')
+ yyerror("unterminated character constant");
+
+ if (c >= '0' && c <= '7') {
+ val = 0;
+ i = 1;
+ for (;;) {
+ val = val*8 + c - '0';
+ if ((c = getc(stream)) == EOF || c == '\n')
+ yyerror("unterminated character constant");
+ if (c == delim)
+ break;
+ i++;
+ if (i > 3) {
+ if (single_char)
+ yyerror("escape sequence too long");
+ else
+ break;
+ }
+ if (c < '0' || c > '7') {
+ if (single_char)
+ yyerror("illegal character in escape sequence");
+ else
+ break;
+ }
+ }
+ (void) ungetc(c, stream);
+ } else {
+ switch (c) {
+
+ case 'n':
+ val = '\n';
+ break;
+
+ case 't':
+ val = '\t';
+ break;
+
+ case 'b':
+ val = '\b';
+ break;
+
+ case 'r':
+ val = '\r';
+ break;
+
+ case 'v':
+ val = '\v';
+ break;
+
+ case '\\':
+ val = '\\';
+ break;
+
+ default:
+ if (c == delim)
+ val = delim;
+ else
+ yyerror("illegal character in escape sequence");
+ }
+ }
+ return (val);
+}
+
+static int
+wordcmp(const void *w1, const void *w2)
+{
+ return (strcmp(
+ ((const word_t *)w1)->w_string,
+ ((const word_t *)w2)->w_string));
+}
+
+static int
+yyerror(msg)
+ char *msg;
+{
+ (void) fprintf(stderr, "%s, line %d: %s\n", infilename, lineno, msg);
+ exit(1);
+}