"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "less-424/lesskey.c" of archive less-424.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 /*
    2  * Copyright (C) 1984-2008  Mark Nudelman
    3  *
    4  * You may distribute under the terms of either the GNU General Public
    5  * License or the Less License, as specified in the README file.
    6  *
    7  * For more information about less, or for information on how to
    8  * contact the author, see the README file.
    9  */
   10 
   11 
   12 /*
   13  *	lesskey [-o output] [input]
   14  *
   15  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
   16  *
   17  *	Make a .less file.
   18  *	If no input file is specified, standard input is used.
   19  *	If no output file is specified, $HOME/.less is used.
   20  *
   21  *	The .less file is used to specify (to "less") user-defined
   22  *	key bindings.  Basically any sequence of 1 to MAX_CMDLEN
   23  *	keystrokes may be bound to an existing less function.
   24  *
   25  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
   26  *
   27  *	The input file is an ascii file consisting of a
   28  *	sequence of lines of the form:
   29  *		string <whitespace> action [chars] <newline>
   30  *
   31  *	"string" is a sequence of command characters which form
   32  *		the new user-defined command.  The command
   33  *		characters may be:
   34  *		1. The actual character itself.
   35  *		2. A character preceded by ^ to specify a
   36  *		   control character (e.g. ^X means control-X).
   37  *		3. A backslash followed by one to three octal digits
   38  *		   to specify a character by its octal value.
   39  *		4. A backslash followed by b, e, n, r or t
   40  *		   to specify \b, ESC, \n, \r or \t, respectively.
   41  *		5. Any character (other than those mentioned above) preceded
   42  *		   by a \ to specify the character itself (characters which
   43  *		   must be preceded by \ include ^, \, and whitespace.
   44  *	"action" is the name of a "less" action, from the table below.
   45  *	"chars" is an optional sequence of characters which is treated
   46  *		as keyboard input after the command is executed.
   47  *
   48  *	Blank lines and lines which start with # are ignored,
   49  *	except for the special control lines:
   50  *		#command	Signals the beginning of the command
   51  *				keys section.
   52  *		#line-edit	Signals the beginning of the line-editing
   53  *				keys section.
   54  *		#env		Signals the beginning of the environment
   55  *				variable section.
   56  *		#stop		Stops command parsing in less;
   57  *				causes all default keys to be disabled.
   58  *
   59  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
   60  *
   61  *	The output file is a non-ascii file, consisting of a header,
   62  *	one or more sections, and a trailer.
   63  *	Each section begins with a section header, a section length word
   64  *	and the section data.  Normally there are three sections:
   65  *		CMD_SECTION	Definition of command keys.
   66  *		EDIT_SECTION	Definition of editing keys.
   67  *		END_SECTION	A special section header, with no
   68  *				length word or section data.
   69  *
   70  *	Section data consists of zero or more byte sequences of the form:
   71  *		string <0> <action>
   72  *	or
   73  *		string <0> <action|A_EXTRA> chars <0>
   74  *
   75  *	"string" is the command string.
   76  *	"<0>" is one null byte.
   77  *	"<action>" is one byte containing the action code (the A_xxx value).
   78  *	If action is ORed with A_EXTRA, the action byte is followed
   79  *		by the null-terminated "chars" string.
   80  *
   81  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
   82  */
   83 
   84 #include "less.h"
   85 #include "lesskey.h"
   86 #include "cmd.h"
   87 
   88 struct cmdname
   89 {
   90 	char *cn_name;
   91 	int cn_action;
   92 };
   93 
   94 struct cmdname cmdnames[] =
   95 {
   96 	{ "back-bracket",         A_B_BRACKET },
   97 	{ "back-line",            A_B_LINE },
   98 	{ "back-line-force",      A_BF_LINE },
   99 	{ "back-screen",          A_B_SCREEN },
  100 	{ "back-scroll",          A_B_SCROLL },
  101 	{ "back-search",          A_B_SEARCH },
  102 	{ "back-window",          A_B_WINDOW },
  103 	{ "debug",                A_DEBUG },
  104 	{ "digit",                A_DIGIT },
  105 	{ "display-flag",         A_DISP_OPTION },
  106 	{ "display-option",       A_DISP_OPTION },
  107 	{ "end",                  A_GOEND },
  108 	{ "examine",              A_EXAMINE },
  109 	{ "filter",               A_FILTER },
  110 	{ "first-cmd",            A_FIRSTCMD },
  111 	{ "firstcmd",             A_FIRSTCMD },
  112 	{ "flush-repaint",        A_FREPAINT },
  113 	{ "forw-bracket",         A_F_BRACKET },
  114 	{ "forw-forever",         A_F_FOREVER },
  115 	{ "forw-line",            A_F_LINE },
  116 	{ "forw-line-force",      A_FF_LINE },
  117 	{ "forw-screen",          A_F_SCREEN },
  118 	{ "forw-screen-force",    A_FF_SCREEN },
  119 	{ "forw-scroll",          A_F_SCROLL },
  120 	{ "forw-search",          A_F_SEARCH },
  121 	{ "forw-window",          A_F_WINDOW },
  122 	{ "goto-end",             A_GOEND },
  123 	{ "goto-line",            A_GOLINE },
  124 	{ "goto-mark",            A_GOMARK },
  125 	{ "help",                 A_HELP },
  126 	{ "index-file",           A_INDEX_FILE },
  127 	{ "invalid",              A_UINVALID },
  128 	{ "left-scroll",          A_LSHIFT },
  129 	{ "next-file",            A_NEXT_FILE },
  130 	{ "next-tag",             A_NEXT_TAG },
  131 	{ "noaction",             A_NOACTION },
  132 	{ "percent",              A_PERCENT },
  133 	{ "pipe",                 A_PIPE },
  134 	{ "prev-file",            A_PREV_FILE },
  135 	{ "prev-tag",             A_PREV_TAG },
  136 	{ "quit",                 A_QUIT },
  137 	{ "remove-file",          A_REMOVE_FILE },
  138 	{ "repaint",              A_REPAINT },
  139 	{ "repaint-flush",        A_FREPAINT },
  140 	{ "repeat-search",        A_AGAIN_SEARCH },
  141 	{ "repeat-search-all",    A_T_AGAIN_SEARCH },
  142 	{ "reverse-search",       A_REVERSE_SEARCH },
  143 	{ "reverse-search-all",   A_T_REVERSE_SEARCH },
  144 	{ "right-scroll",         A_RSHIFT },
  145 	{ "set-mark",             A_SETMARK },
  146 	{ "shell",                A_SHELL },
  147 	{ "status",               A_STAT },
  148 	{ "toggle-flag",          A_OPT_TOGGLE },
  149 	{ "toggle-option",        A_OPT_TOGGLE },
  150 	{ "undo-hilite",          A_UNDO_SEARCH },
  151 	{ "version",              A_VERSION },
  152 	{ "visual",               A_VISUAL },
  153 	{ NULL,   0 }
  154 };
  155 
  156 struct cmdname editnames[] =
  157 {
  158 	{ "back-complete",	EC_B_COMPLETE },
  159 	{ "backspace",		EC_BACKSPACE },
  160 	{ "delete",		EC_DELETE },
  161 	{ "down",		EC_DOWN },
  162 	{ "end",		EC_END },
  163 	{ "expand",		EC_EXPAND },
  164 	{ "forw-complete",	EC_F_COMPLETE },
  165 	{ "home",		EC_HOME },
  166 	{ "insert",		EC_INSERT },
  167 	{ "invalid",		EC_UINVALID },
  168 	{ "kill-line",		EC_LINEKILL },
  169 	{ "left",		EC_LEFT },
  170 	{ "literal",		EC_LITERAL },
  171 	{ "right",		EC_RIGHT },
  172 	{ "up",			EC_UP },
  173 	{ "word-backspace",	EC_W_BACKSPACE },
  174 	{ "word-delete",	EC_W_DELETE },
  175 	{ "word-left",		EC_W_LEFT },
  176 	{ "word-right",		EC_W_RIGHT },
  177 	{ NULL, 0 }
  178 };
  179 
  180 struct table
  181 {
  182 	struct cmdname *names;
  183 	char *pbuffer;
  184 	char buffer[MAX_USERCMD];
  185 };
  186 
  187 struct table cmdtable;
  188 struct table edittable;
  189 struct table vartable;
  190 struct table *currtable = &cmdtable;
  191 
  192 char fileheader[] = {
  193 	C0_LESSKEY_MAGIC,
  194 	C1_LESSKEY_MAGIC,
  195 	C2_LESSKEY_MAGIC,
  196 	C3_LESSKEY_MAGIC
  197 };
  198 char filetrailer[] = {
  199 	C0_END_LESSKEY_MAGIC,
  200 	C1_END_LESSKEY_MAGIC,
  201 	C2_END_LESSKEY_MAGIC
  202 };
  203 char cmdsection[1] =	{ CMD_SECTION };
  204 char editsection[1] =	{ EDIT_SECTION };
  205 char varsection[1] =	{ VAR_SECTION };
  206 char endsection[1] =	{ END_SECTION };
  207 
  208 char *infile = NULL;
  209 char *outfile = NULL ;
  210 
  211 int linenum;
  212 int errors;
  213 
  214 extern char version[];
  215 
  216 	void
  217 usage()
  218 {
  219 	fprintf(stderr, "usage: lesskey [-o output] [input]\n");
  220 	exit(1);
  221 }
  222 
  223 	char *
  224 mkpathname(dirname, filename)
  225 	char *dirname;
  226 	char *filename;
  227 {
  228 	char *pathname;
  229 
  230 	pathname = calloc(strlen(dirname) + strlen(filename) + 2, sizeof(char));
  231 	strcpy(pathname, dirname);
  232 	strcat(pathname, PATHNAME_SEP);
  233 	strcat(pathname, filename);
  234 	return (pathname);
  235 }
  236 
  237 /*
  238  * Figure out the name of a default file (in the user's HOME directory).
  239  */
  240 	char *
  241 homefile(filename)
  242 	char *filename;
  243 {
  244 	char *p;
  245 	char *pathname;
  246 
  247 	if ((p = getenv("HOME")) != NULL && *p != '\0')
  248 		pathname = mkpathname(p, filename);
  249 #if OS2
  250 	else if ((p = getenv("INIT")) != NULL && *p != '\0')
  251 		pathname = mkpathname(p, filename);
  252 #endif
  253 	else
  254 	{
  255 		fprintf(stderr, "cannot find $HOME - using current directory\n");
  256 		pathname = mkpathname(".", filename);
  257 	}
  258 	return (pathname);
  259 }
  260 
  261 /*
  262  * Parse command line arguments.
  263  */
  264 	void
  265 parse_args(argc, argv)
  266 	int argc;
  267 	char **argv;
  268 {
  269 	char *arg;
  270 
  271 	outfile = NULL;
  272 	while (--argc > 0)
  273 	{
  274 		arg = *++argv;
  275 		if (arg[0] != '-')
  276 			/* Arg does not start with "-"; it's not an option. */
  277 			break;
  278 		if (arg[1] == '\0')
  279 			/* "-" means standard input. */
  280 			break;
  281 		if (arg[1] == '-' && arg[2] == '\0')
  282 		{
  283 			/* "--" means end of options. */
  284 			argc--;
  285 			argv++;
  286 			break;
  287 		}
  288 		switch (arg[1])
  289 		{
  290 		case '-':
  291 			if (strncmp(arg, "--output", 8) == 0)
  292 			{
  293 				if (arg[8] == '\0')
  294 					outfile = &arg[8];
  295 				else if (arg[8] == '=')
  296 					outfile = &arg[9];
  297 				else
  298 					usage();
  299 				goto opt_o;
  300 			}
  301 			if (strcmp(arg, "--version") == 0)
  302 			{
  303 				goto opt_V;
  304 			}
  305 			usage();
  306 			break;
  307 		case 'o':
  308 			outfile = &argv[0][2];
  309 		opt_o:
  310 			if (*outfile == '\0')
  311 			{
  312 				if (--argc <= 0)
  313 					usage();
  314 				outfile = *(++argv);
  315 			}
  316 			break;
  317 		case 'V':
  318 		opt_V:
  319 			printf("lesskey  version %s\n", version);
  320 			exit(0);
  321 		default:
  322 			usage();
  323 		}
  324 	}
  325 	if (argc > 1)
  326 		usage();
  327 	/*
  328 	 * Open the input file, or use DEF_LESSKEYINFILE if none specified.
  329 	 */
  330 	if (argc > 0)
  331 		infile = *argv;
  332 	else
  333 		infile = homefile(DEF_LESSKEYINFILE);
  334 }
  335 
  336 /*
  337  * Initialize data structures.
  338  */
  339 	void
  340 init_tables()
  341 {
  342 	cmdtable.names = cmdnames;
  343 	cmdtable.pbuffer = cmdtable.buffer;
  344 
  345 	edittable.names = editnames;
  346 	edittable.pbuffer = edittable.buffer;
  347 
  348 	vartable.names = NULL;
  349 	vartable.pbuffer = vartable.buffer;
  350 }
  351 
  352 /*
  353  * Parse one character of a string.
  354  */
  355 	char *
  356 tstr(pp, xlate)
  357 	char **pp;
  358 	int xlate;
  359 {
  360 	register char *p;
  361 	register char ch;
  362 	register int i;
  363 	static char buf[10];
  364 	static char tstr_control_k[] =
  365 		{ SK_SPECIAL_KEY, SK_CONTROL_K, 6, 1, 1, 1, '\0' };
  366 
  367 	p = *pp;
  368 	switch (*p)
  369 	{
  370 	case '\\':
  371 		++p;
  372 		switch (*p)
  373 		{
  374 		case '0': case '1': case '2': case '3':
  375 		case '4': case '5': case '6': case '7':
  376 			/*
  377 			 * Parse an octal number.
  378 			 */
  379 			ch = 0;
  380 			i = 0;
  381 			do
  382 				ch = 8*ch + (*p - '0');
  383 			while (*++p >= '0' && *p <= '7' && ++i < 3);
  384 			*pp = p;
  385 			if (xlate && ch == CONTROL('K'))
  386 				return tstr_control_k;
  387 			buf[0] = ch;
  388 			buf[1] = '\0';
  389 			return (buf);
  390 		case 'b':
  391 			*pp = p+1;
  392 			return ("\b");
  393 		case 'e':
  394 			*pp = p+1;
  395 			buf[0] = ESC;
  396 			buf[1] = '\0';
  397 			return (buf);
  398 		case 'n':
  399 			*pp = p+1;
  400 			return ("\n");
  401 		case 'r':
  402 			*pp = p+1;
  403 			return ("\r");
  404 		case 't':
  405 			*pp = p+1;
  406 			return ("\t");
  407 		case 'k':
  408 			if (xlate)
  409 			{
  410 				switch (*++p)
  411 				{
  412 				case 'u': ch = SK_UP_ARROW; break;
  413 				case 'd': ch = SK_DOWN_ARROW; break;
  414 				case 'r': ch = SK_RIGHT_ARROW; break;
  415 				case 'l': ch = SK_LEFT_ARROW; break;
  416 				case 'U': ch = SK_PAGE_UP; break;
  417 				case 'D': ch = SK_PAGE_DOWN; break;
  418 				case 'h': ch = SK_HOME; break;
  419 				case 'e': ch = SK_END; break;
  420 				case 'x': ch = SK_DELETE; break;
  421 				default:
  422 					error("illegal char after \\k");
  423 					*pp = p+1;
  424 					return ("");
  425 				}
  426 				*pp = p+1;
  427 				buf[0] = SK_SPECIAL_KEY;
  428 				buf[1] = ch;
  429 				buf[2] = 6;
  430 				buf[3] = 1;
  431 				buf[4] = 1;
  432 				buf[5] = 1;
  433 				buf[6] = '\0';
  434 				return (buf);
  435 			}
  436 			/* FALLTHRU */
  437 		default:
  438 			/*
  439 			 * Backslash followed by any other char
  440 			 * just means that char.
  441 			 */
  442 			*pp = p+1;
  443 			buf[0] = *p;
  444 			buf[1] = '\0';
  445 			if (xlate && buf[0] == CONTROL('K'))
  446 				return tstr_control_k;
  447 			return (buf);
  448 		}
  449 	case '^':
  450 		/*
  451 		 * Carat means CONTROL.
  452 		 */
  453 		*pp = p+2;
  454 		buf[0] = CONTROL(p[1]);
  455 		buf[1] = '\0';
  456 		if (buf[0] == CONTROL('K'))
  457 			return tstr_control_k;
  458 		return (buf);
  459 	}
  460 	*pp = p+1;
  461 	buf[0] = *p;
  462 	buf[1] = '\0';
  463 	if (xlate && buf[0] == CONTROL('K'))
  464 		return tstr_control_k;
  465 	return (buf);
  466 }
  467 
  468 /*
  469  * Skip leading spaces in a string.
  470  */
  471 	public char *
  472 skipsp(s)
  473 	register char *s;
  474 {
  475 	while (*s == ' ' || *s == '\t')
  476 		s++;
  477 	return (s);
  478 }
  479 
  480 /*
  481  * Skip non-space characters in a string.
  482  */
  483 	public char *
  484 skipnsp(s)
  485 	register char *s;
  486 {
  487 	while (*s != '\0' && *s != ' ' && *s != '\t')
  488 		s++;
  489 	return (s);
  490 }
  491 
  492 /*
  493  * Clean up an input line:
  494  * strip off the trailing newline & any trailing # comment.
  495  */
  496 	char *
  497 clean_line(s)
  498 	char *s;
  499 {
  500 	register int i;
  501 
  502 	s = skipsp(s);
  503 	for (i = 0;  s[i] != '\n' && s[i] != '\r' && s[i] != '\0';  i++)
  504 		if (s[i] == '#' && (i == 0 || s[i-1] != '\\'))
  505 			break;
  506 	s[i] = '\0';
  507 	return (s);
  508 }
  509 
  510 /*
  511  * Add a byte to the output command table.
  512  */
  513 	void
  514 add_cmd_char(c)
  515 	int c;
  516 {
  517 	if (currtable->pbuffer >= currtable->buffer + MAX_USERCMD)
  518 	{
  519 		error("too many commands");
  520 		exit(1);
  521 	}
  522 	*(currtable->pbuffer)++ = c;
  523 }
  524 
  525 /*
  526  * Add a string to the output command table.
  527  */
  528 	void
  529 add_cmd_str(s)
  530 	char *s;
  531 {
  532 	for ( ;  *s != '\0';  s++)
  533 		add_cmd_char(*s);
  534 }
  535 
  536 /*
  537  * See if we have a special "control" line.
  538  */
  539 	int
  540 control_line(s)
  541 	char *s;
  542 {
  543 #define	PREFIX(str,pat)	(strncmp(str,pat,strlen(pat)-1) == 0)
  544 
  545 	if (PREFIX(s, "#line-edit"))
  546 	{
  547 		currtable = &edittable;
  548 		return (1);
  549 	}
  550 	if (PREFIX(s, "#command"))
  551 	{
  552 		currtable = &cmdtable;
  553 		return (1);
  554 	}
  555 	if (PREFIX(s, "#env"))
  556 	{
  557 		currtable = &vartable;
  558 		return (1);
  559 	}
  560 	if (PREFIX(s, "#stop"))
  561 	{
  562 		add_cmd_char('\0');
  563 		add_cmd_char(A_END_LIST);
  564 		return (1);
  565 	}
  566 	return (0);
  567 }
  568 
  569 /*
  570  * Output some bytes.
  571  */
  572 	void
  573 fputbytes(fd, buf, len)
  574 	FILE *fd;
  575 	char *buf;
  576 	int len;
  577 {
  578 	while (len-- > 0)
  579 	{
  580 		fwrite(buf, sizeof(char), 1, fd);
  581 		buf++;
  582 	}
  583 }
  584 
  585 /*
  586  * Output an integer, in special KRADIX form.
  587  */
  588 	void
  589 fputint(fd, val)
  590 	FILE *fd;
  591 	unsigned int val;
  592 {
  593 	char c;
  594 
  595 	if (val >= KRADIX*KRADIX)
  596 	{
  597 		fprintf(stderr, "error: integer too big (%d > %d)\n",
  598 			val, KRADIX*KRADIX);
  599 		exit(1);
  600 	}
  601 	c = val % KRADIX;
  602 	fwrite(&c, sizeof(char), 1, fd);
  603 	c = val / KRADIX;
  604 	fwrite(&c, sizeof(char), 1, fd);
  605 }
  606 
  607 /*
  608  * Find an action, given the name of the action.
  609  */
  610 	int
  611 findaction(actname)
  612 	char *actname;
  613 {
  614 	int i;
  615 
  616 	for (i = 0;  currtable->names[i].cn_name != NULL;  i++)
  617 		if (strcmp(currtable->names[i].cn_name, actname) == 0)
  618 			return (currtable->names[i].cn_action);
  619 	error("unknown action");
  620 	return (A_INVALID);
  621 }
  622 
  623 	void
  624 error(s)
  625 	char *s;
  626 {
  627 	fprintf(stderr, "line %d: %s\n", linenum, s);
  628 	errors++;
  629 }
  630 
  631 
  632 	void
  633 parse_cmdline(p)
  634 	char *p;
  635 {
  636 	int cmdlen;
  637 	char *actname;
  638 	int action;
  639 	char *s;
  640 	char c;
  641 
  642 	/*
  643 	 * Parse the command string and store it in the current table.
  644 	 */
  645 	cmdlen = 0;
  646 	do
  647 	{
  648 		s = tstr(&p, 1);
  649 		cmdlen += strlen(s);
  650 		if (cmdlen > MAX_CMDLEN)
  651 			error("command too long");
  652 		else
  653 			add_cmd_str(s);
  654 	} while (*p != ' ' && *p != '\t' && *p != '\0');
  655 	/*
  656 	 * Terminate the command string with a null byte.
  657 	 */
  658 	add_cmd_char('\0');
  659 
  660 	/*
  661 	 * Skip white space between the command string
  662 	 * and the action name.
  663 	 * Terminate the action name with a null byte.
  664 	 */
  665 	p = skipsp(p);
  666 	if (*p == '\0')
  667 	{
  668 		error("missing action");
  669 		return;
  670 	}
  671 	actname = p;
  672 	p = skipnsp(p);
  673 	c = *p;
  674 	*p = '\0';
  675 
  676 	/*
  677 	 * Parse the action name and store it in the current table.
  678 	 */
  679 	action = findaction(actname);
  680 
  681 	/*
  682 	 * See if an extra string follows the action name.
  683 	 */
  684 	*p = c;
  685 	p = skipsp(p);
  686 	if (*p == '\0')
  687 	{
  688 		add_cmd_char(action);
  689 	} else
  690 	{
  691 		/*
  692 		 * OR the special value A_EXTRA into the action byte.
  693 		 * Put the extra string after the action byte.
  694 		 */
  695 		add_cmd_char(action | A_EXTRA);
  696 		while (*p != '\0')
  697 			add_cmd_str(tstr(&p, 0));
  698 		add_cmd_char('\0');
  699 	}
  700 }
  701 
  702 	void
  703 parse_varline(p)
  704 	char *p;
  705 {
  706 	char *s;
  707 
  708 	do
  709 	{
  710 		s = tstr(&p, 0);
  711 		add_cmd_str(s);
  712 	} while (*p != ' ' && *p != '\t' && *p != '=' && *p != '\0');
  713 	/*
  714 	 * Terminate the variable name with a null byte.
  715 	 */
  716 	add_cmd_char('\0');
  717 
  718 	p = skipsp(p);
  719 	if (*p++ != '=')
  720 	{
  721 		error("missing =");
  722 		return;
  723 	}
  724 
  725 	add_cmd_char(EV_OK|A_EXTRA);
  726 
  727 	p = skipsp(p);
  728 	while (*p != '\0')
  729 	{
  730 		s = tstr(&p, 0);
  731 		add_cmd_str(s);
  732 	}
  733 	add_cmd_char('\0');
  734 }
  735 
  736 /*
  737  * Parse a line from the lesskey file.
  738  */
  739 	void
  740 parse_line(line)
  741 	char *line;
  742 {
  743 	char *p;
  744 
  745 	/*
  746 	 * See if it is a control line.
  747 	 */
  748 	if (control_line(line))
  749 		return;
  750 	/*
  751 	 * Skip leading white space.
  752 	 * Replace the final newline with a null byte.
  753 	 * Ignore blank lines and comments.
  754 	 */
  755 	p = clean_line(line);
  756 	if (*p == '\0')
  757 		return;
  758 
  759 	if (currtable == &vartable)
  760 		parse_varline(p);
  761 	else
  762 		parse_cmdline(p);
  763 }
  764 
  765 	int
  766 main(argc, argv)
  767 	int argc;
  768 	char *argv[];
  769 {
  770 	FILE *desc;
  771 	FILE *out;
  772 	char line[1024];
  773 
  774 #ifdef WIN32
  775 	if (getenv("HOME") == NULL)
  776 	{
  777 		/*
  778 		 * If there is no HOME environment variable,
  779 		 * try the concatenation of HOMEDRIVE + HOMEPATH.
  780 		 */
  781 		char *drive = getenv("HOMEDRIVE");
  782 		char *path  = getenv("HOMEPATH");
  783 		if (drive != NULL && path != NULL)
  784 		{
  785 			char *env = (char *) calloc(strlen(drive) +
  786 					strlen(path) + 6, sizeof(char));
  787 			strcpy(env, "HOME=");
  788 			strcat(env, drive);
  789 			strcat(env, path);
  790 			putenv(env);
  791 		}
  792 	}
  793 #endif /* WIN32 */
  794 
  795 	/*
  796 	 * Process command line arguments.
  797 	 */
  798 	parse_args(argc, argv);
  799 	init_tables();
  800 
  801 	/*
  802 	 * Open the input file.
  803 	 */
  804 	if (strcmp(infile, "-") == 0)
  805 		desc = stdin;
  806 	else if ((desc = fopen(infile, "r")) == NULL)
  807 	{
  808 #if HAVE_PERROR
  809 		perror(infile);
  810 #else
  811 		fprintf(stderr, "Cannot open %s\n", infile);
  812 #endif
  813 		usage();
  814 	}
  815 
  816 	/*
  817 	 * Read and parse the input file, one line at a time.
  818 	 */
  819 	errors = 0;
  820 	linenum = 0;
  821 	while (fgets(line, sizeof(line), desc) != NULL)
  822 	{
  823 		++linenum;
  824 		parse_line(line);
  825 	}
  826 
  827 	/*
  828 	 * Write the output file.
  829 	 * If no output file was specified, use "$HOME/.less"
  830 	 */
  831 	if (errors > 0)
  832 	{
  833 		fprintf(stderr, "%d errors; no output produced\n", errors);
  834 		exit(1);
  835 	}
  836 
  837 	if (outfile == NULL)
  838 		outfile = getenv("LESSKEY");
  839 	if (outfile == NULL)
  840 		outfile = homefile(LESSKEYFILE);
  841 	if ((out = fopen(outfile, "wb")) == NULL)
  842 	{
  843 #if HAVE_PERROR
  844 		perror(outfile);
  845 #else
  846 		fprintf(stderr, "Cannot open %s\n", outfile);
  847 #endif
  848 		exit(1);
  849 	}
  850 
  851 	/* File header */
  852 	fputbytes(out, fileheader, sizeof(fileheader));
  853 
  854 	/* Command key section */
  855 	fputbytes(out, cmdsection, sizeof(cmdsection));
  856 	fputint(out, cmdtable.pbuffer - cmdtable.buffer);
  857 	fputbytes(out, (char *)cmdtable.buffer, cmdtable.pbuffer-cmdtable.buffer);
  858 	/* Edit key section */
  859 	fputbytes(out, editsection, sizeof(editsection));
  860 	fputint(out, edittable.pbuffer - edittable.buffer);
  861 	fputbytes(out, (char *)edittable.buffer, edittable.pbuffer-edittable.buffer);
  862 
  863 	/* Environment variable section */
  864 	fputbytes(out, varsection, sizeof(varsection));
  865 	fputint(out, vartable.pbuffer - vartable.buffer);
  866 	fputbytes(out, (char *)vartable.buffer, vartable.pbuffer-vartable.buffer);
  867 
  868 	/* File trailer */
  869 	fputbytes(out, endsection, sizeof(endsection));
  870 	fputbytes(out, filetrailer, sizeof(filetrailer));
  871 	return (0);
  872 }