eterm/src/activeconfig.c

624 lines
17 KiB
C

/*--------------------------------*-C-*---------------------------------*
* File: activeconfig.c
*
* Copyright 1997 Nat Friedman, Massachusetts Institute of Technology
* <ndf@mit.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
*----------------------------------------------------------------------*/
/* This file contains all the config file parsing functionality */
static const char cvs_ident[] = "$Id$";
#include "feature.h"
#ifdef USE_ACTIVE_TAGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <ctype.h>
#include <pwd.h>
#include "activeconfig.h"
/* The basic design of the config file parsing routines revolves around the
dispatch table below. The dispatch table relates file tokens with
their parsing functions. In order to add a new config file token
(for example "Foo=something"), simply define TAG_CONFIG_FOO, write
parse_config_tag_foo, and then add the following line to the dispatch
table:
{TAG_CONFIG_FOO, parse_config_tag_foo}
and it will magically work. */
/* This is the actual dispatch table. It should be terminated by a struct
config_entry whose parser field is NULL. */
struct config_entry config_dispatch[] =
{
{TAG_CONFIG_LOAD, parse_config_load},
{TAG_CONFIG_DEFAULT_BINDING, parse_config_default_binding},
{TAG_CONFIG_DEFAULT_HIGHLIGHT, parse_config_default_highlight},
{TAG_CONFIG_DEFAULT_SEARCH_LINES, parse_config_default_search_lines},
{TAG_CONFIG_NEW_TAG, parse_config_tag_begin},
{TAG_CONFIG_END_TAG, parse_config_tag_end},
{TAG_CONFIG_LATENT, parse_config_tag_latent},
{TAG_CONFIG_BINDING, parse_config_tag_binding},
{TAG_CONFIG_HIGHLIGHT, parse_config_tag_highlight},
{TAG_CONFIG_MODES, parse_config_tag_modes},
{TAG_CONFIG_REGEXP, parse_config_tag_regexp},
{TAG_CONFIG_ACTION, parse_config_tag_action},
{TAG_CONFIG_OUTPUT, parse_config_tag_output},
{TAG_CONFIG_SEARCH_LINES, parse_config_tag_search_lines},
{TAG_CONFIG_CLUE, parse_config_tag_clue},
{TAG_CONFIG_ENV, parse_config_tag_env},
{"", NULL}
};
/*
Dispatch Functions
*/
int
parse_config_tag_env(char *envlist, struct config_info *config_info)
{
char *p, *q;
int nenvs = 0;
p = envlist;
do {
printf("Env...\n");
if ((q = strchr(p, ',')) == NULL)
q = p + strlen(p);
strncpy(tag[config_info->curr_tag].env[nenvs], p, q - p);
tag[config_info->curr_tag].env[nenvs][q - p] = '\0';
printf("got env: %s\n", tag[config_info->curr_tag].env[nenvs]);
nenvs++;
if (*q != ',')
p = NULL;
else
p = q + 1;
} while (p != NULL);
tag[config_info->curr_tag].num_envs = nenvs;
printf("Got envs for tag:\n");
for (nenvs = 0; nenvs < tag[config_info->curr_tag].num_envs; nenvs++)
printf("Env %d: (%s)\n", nenvs, tag[config_info->curr_tag].env[nenvs]);
return 1;
}
int
parse_config_load(char *filename, struct config_info *config_info)
{
struct config_info new_config_info;
/* Scope on Defaults is per-file */
set_config_defaults(&new_config_info);
new_config_info.curr_tag = config_info->curr_tag;
/* If it's a relative file name, make it absolute */
if (*filename != '/') {
char new_filename[1024];
char *p, *q;
if ((p = strrchr(config_info->filename, '/')) == NULL)
configerror(config_info, "Could not determine path to file!");
q = config_info->filename;
strcpy(new_filename, q);
new_filename[p - q] = '\0';
strcat(new_filename, "/");
strcat(new_filename, filename);
printf("Filename: %s\n", new_filename);
strcpy(filename, new_filename);
}
/* FIXME: This implementation forces pathnames to be absolute. We should
allow relative pathnames somehow. */
parse_tag_file(filename, &new_config_info);
config_info->curr_tag = new_config_info.curr_tag;
return 1;
}
int
parse_config_default_binding(char *def,
struct config_info *config_info)
{
config_info->default_binding = string_to_binding_mask(config_info, def);
if (!(config_info->default_binding & (TAG_BINDING_BUTTON1 | TAG_BINDING_BUTTON2 |
TAG_BINDING_BUTTON3))) {
configerror(config_info, "Error reading default binding: binding _must_ "
"include either Button1, Button2, or Button3. Reverting to "
"compiled default.");
config_info->default_binding = TAG_DEFAULT_BINDING_MASK;
}
return 1;
}
int
parse_config_default_highlight(char *def,
struct config_info *config_info)
{
string_to_highlight(config_info, &config_info->default_highlight, def);
return 1;
}
int
parse_config_default_search_lines(char *def,
struct config_info *config_info)
{
if ((config_info->default_search_lines = atoi(def)) == 0) {
configerror(config_info, "Invalid default number of search lines. "
"Reverting to compiled default.\n");
config_info->default_search_lines = TAG_DEFAULT_SEARCH_LINES;
}
if (config_info->default_search_lines > MAX_SEARCH_LINES) {
configerror(config_info, "Default number of search lines > maximum. "
"Reverting to compiled default.\n");
config_info->default_search_lines = TAG_DEFAULT_SEARCH_LINES;
}
return 1;
}
int
parse_config_tag_begin(char *s, struct config_info *config_info)
{
fprintf(stderr, "New tag\n");
if (config_info->in_tag) {
configerror(config_info, "Open brace inside braces");
disable_tags();
return -1;
}
if ((config_info->curr_tag + 1) >= MAX_TAGS) {
configerror(config_info, "Too many tags! Increase the maximum number "
"of tags and recompile!\n");
disable_tags();
return -1;
}
config_info->in_tag = 1;
/* Initialize the new tag with all the default values. */
tag[config_info->curr_tag].binding_mask = config_info->default_binding;
tag[config_info->curr_tag].search_lines = config_info->default_search_lines;
tag[config_info->curr_tag].highlight.attributes =
config_info->default_highlight.attributes;
tag[config_info->curr_tag].highlight.fg_color =
config_info->default_highlight.fg_color;
tag[config_info->curr_tag].highlight.bg_color =
config_info->default_highlight.bg_color;
tag[config_info->curr_tag].num_modes = 0;
tag[config_info->curr_tag].latent = 0;
tag[config_info->curr_tag].output_type = TAG_OUTPUT_NULL;
#ifdef ACTIVE_TAGS_SPENCER_REGEXP
tag[config_info->curr_tag].rx = NULL;
#endif
return 1;
}
int
parse_config_tag_end(char *s, struct config_info *config_info)
{
if (!config_info->in_tag) {
configerror(config_info, "close brace without open brace");
disable_tags();
return -1;
}
/* Make sure that the tag was setup properly before advancing to the next
one in the list */
/* FIXME: we need a way of figuring this out if we're using POSIX regex */
#ifdef ACTIVE_TAGS_SPENCER_REGEXP
if (tag[config_info->curr_tag].rx == NULL) {
configerror("No regular epxression supplied for tag. Tag ignored.");
config_info->curr_tag--;
}
#endif
config_info->in_tag = 0;
config_info->curr_tag++;
return 1;
}
int
parse_config_tag_latent(char *latent, struct config_info *config_info)
{
if (!strcasecmp(latent, "true"))
tag[config_info->curr_tag].latent = 1;
return 1;
}
int
parse_config_tag_search_lines(char *sl, struct config_info *config_info)
{
if (atoi(sl))
tag[config_info->curr_tag].search_lines = atoi(sl);
D_TAGS(("==> Setting tag %d's search lines to %d\n", config_info->curr_tag, atoi(sl)));
return 1;
}
int
parse_config_tag_binding(char *s, struct config_info *config_info)
{
tag[config_info->curr_tag].binding_mask = string_to_binding_mask(config_info, s);
return 1;
}
int
parse_config_tag_highlight(char *s, struct config_info *config_info)
{
string_to_highlight(config_info, &tag[config_info->curr_tag].highlight, s);
return 1;
}
int
parse_config_tag_modes(char *s, struct config_info *config_info)
{
char *mode, *p;
mode = s;
while (mode != NULL) {
if ((p = strchr(mode, ',')) != NULL)
*p = '\0';
strcpy(tag[config_info->curr_tag].mode[tag[config_info->curr_tag].num_modes], mode);
if (p != NULL)
mode = p + 1;
else
mode = NULL;
tag[config_info->curr_tag].num_modes++;
}
return 1;
}
int
parse_config_tag_regexp(char *regexp, struct config_info *config_info)
{
#ifdef ACTIVE_TAGS_SPENCER_REGEXP
if ((tag[config_info->curr_tag].rx = regcomp(regexp)) == NULL)
#else
if (regcomp(&tag[config_info->curr_tag].rx, regexp, REG_EXTENDED) != 0)
#endif
{
configerror(config_info, "Couldn't compile regular expression");
return -1;
}
return 1;
}
int
parse_config_tag_action(char *action, struct config_info *config_info)
{
strcpy(tag[config_info->curr_tag].action, action);
return 1;
}
int
parse_config_tag_output(char *output, struct config_info *config_info)
{
if (!strcasecmp(output, "null"))
tag[config_info->curr_tag].output_type = TAG_OUTPUT_NULL;
else if (!strcasecmp(output, "loop"))
tag[config_info->curr_tag].output_type = TAG_OUTPUT_LOOP;
else if (!strcasecmp(output, "replace"))
tag[config_info->curr_tag].output_type = TAG_OUTPUT_REPL;
else if (!strcasecmp(output, "popup"))
tag[config_info->curr_tag].output_type = TAG_OUTPUT_POPUP;
else {
configerror(config_info, "Unknown output method; defaulting to NULL");
tag[config_info->curr_tag].output_type = TAG_OUTPUT_NULL;
}
return 1;
}
int
parse_config_tag_clue(char *clue, struct config_info *config_info)
{
strcpy(tag[config_info->curr_tag].clue, clue);
return 1;
}
void
set_config_defaults(struct config_info *config_info)
{
config_info->default_binding = TAG_DEFAULT_BINDING_MASK;
config_info->default_search_lines = TAG_DEFAULT_SEARCH_LINES;
config_info->default_highlight.bg_color = TAG_DEFAULT_HIGHLIGHT_BG;
config_info->default_highlight.fg_color = TAG_DEFAULT_HIGHLIGHT_FG;
config_info->default_highlight.attributes = TAG_DEFAULT_HIGHLIGHT_ATT;
config_info->curr_tag = 0;
config_info->line_num = 0;
config_info->in_tag = 0;
}
/* parse_tag_config actually reads the file and calls the dispatch functions
where appropriate */
void
parse_tag_config(char *tag_config_file)
{
char file_name[1024];
struct passwd *user;
struct config_info config_info;
/* Set the defaults */
set_config_defaults(&config_info);
if (tag_config_file != NULL)
if (!parse_tag_file(tag_config_file, &config_info)) {
fprintf(stderr, "parse_tag_config: Couldn't open tag config"
"file: %s\n", tag_config_file);
tag_config_file = NULL;
}
if (tag_config_file == NULL) {
user = getpwuid(getuid());
sprintf(file_name, "%s/%s", user->pw_dir, TAG_CONFIG_USER_FILENAME);
if (!parse_tag_file(file_name, &config_info)) {
fprintf(stderr, "parse_tag_config: Couldn't open user tag config "
"file: %s\n", file_name);
fprintf(stderr, "parse_tag_config: Trying system config file\n");
/* Try the system-wide config file */
if (!parse_tag_file(TAG_CONFIG_SYSTEM_FILENAME, &config_info)) {
fprintf(stderr, "parse_tag_config: Error parsing config file: "
"%s\n", TAG_CONFIG_SYSTEM_FILENAME);
disable_tags();
return;
}
}
}
num_tags = config_info.curr_tag;
printf("Num tags: %d\n", num_tags);
{
int i;
for (i = 0; i < num_tags; i++)
printf("Tag action(%d): %s\n", i, tag[i].action);
}
}
int
parse_tag_file(const char *filename, struct config_info *config_info)
{
FILE *tag_file;
char line[1024];
int i;
if ((tag_file = fopen(filename, "r")) == NULL)
return 0;
strcpy(config_info->filename, filename);
/* Loop through the config file lines */
while (!feof(tag_file)) {
fgets(line, sizeof(line), tag_file);
config_info->line_num++;
if (feof(tag_file))
break;
if (line[strlen(line) - 1] != '\n') {
configerror(config_info, "line too long?");
exit(1);
}
line[strlen(line) - 1] = '\0';
/* Loop through the config file lines, calling the appropriate
functions from the dispatch table as we go. If there is no
corresponding function, flag a warning and try to continue. */
if ((line[0] != '#') && (!isspace(line[0])) && (strlen(line) != 0)) {
for (i = 0; config_dispatch[i].parser != NULL; i++)
if (TAG_CONFIG(config_dispatch[i].token)) {
if ((strchr(line, '=') == NULL) && (*line != '{') &&
(*line != '}') && !TAG_CONFIG(TAG_CONFIG_LOAD))
configerror(config_info, "'=' not found");
else {
char *p;
p = line + strlen(config_dispatch[i].token) + 1;
if (strchr(line, '=') != NULL) {
p = strchr(line, '=') + 1;
while (isspace(*p))
p++;
}
if (!((config_dispatch[i].parser) (p, config_info)))
return 0;
break;
}
}
if (config_dispatch[i].parser == NULL)
configerror(config_info, "Unrecognized token");
}
}
fclose(tag_file);
return 1;
}
/*
Internal Functions
*/
/* Use this function to display errors encountered while parsing the config
file to keep them looking uniform */
void
configerror(struct config_info *config_info, char *message)
{
fprintf(stderr, "active tags: error on line %d of config file %s: %s\n",
config_info->line_num, config_info->filename, message);
}
void
string_to_color(struct config_info *config_info, tag_highlight_t * highlight,
char *c)
{
#if 0
int color;
#endif
int color = 0;
int bg = 0;
char *p;
p = c;
/* Background colors are prefaced by a '*' */
if (*p == '*') {
bg = 1;
p++;
}
if (!strcasecmp(p, "Black"))
color = TAG_HIGHLIGHT_BLACK;
else if (!strcasecmp(p, "White"))
color = TAG_HIGHLIGHT_WHITE;
else if (!strcasecmp(p, "Red"))
color = TAG_HIGHLIGHT_RED;
else if (!strcasecmp(p, "Green"))
color = TAG_HIGHLIGHT_GREEN;
else if (!strcasecmp(p, "Yellow"))
color = TAG_HIGHLIGHT_YELLOW;
else if (!strcasecmp(p, "Blue"))
color = TAG_HIGHLIGHT_BLUE;
else if (!strcasecmp(p, "Magenta"))
color = TAG_HIGHLIGHT_MAGENTA;
else if (!strcasecmp(p, "Cyan"))
color = TAG_HIGHLIGHT_CYAN;
else if (!strcasecmp(p, "Normal"))
color = TAG_HIGHLIGHT_NORMAL;
else
configerror(config_info, "Unrecognized highlight token");
if (bg)
highlight->bg_color = color;
else
highlight->fg_color = color;
}
void
string_to_highlight(struct config_info *config_info, tag_highlight_t *
highlight, char *s)
{
char *h_bit;
/* att_set is 0 if we've set an attribute value and 1 otherwise. We have to
keep track of this because setting an attribute value should override the
default, so we can't blindly OR the new values with whatever was in
highlight->attribute before. So, if we've already overriden the value,
we OR. If we haven't yet overriden it, then we do so and set att_set to
1. */
int att_set = 0;
h_bit = strtok(s, "&");
while (h_bit != NULL) {
if (!strcasecmp(h_bit, "Underline")) {
if (att_set)
highlight->attributes |= TAG_HIGHLIGHT_ULINE;
else {
highlight->attributes = TAG_HIGHLIGHT_ULINE;
highlight->fg_color = TAG_HIGHLIGHT_NORMAL;
highlight->bg_color = TAG_HIGHLIGHT_NORMAL;
att_set = 1;
}
} else if (!strcasecmp(h_bit, "Bold")) {
if (att_set)
highlight->attributes |= TAG_HIGHLIGHT_BOLD;
else {
highlight->attributes = TAG_HIGHLIGHT_BOLD;
highlight->fg_color = TAG_HIGHLIGHT_NORMAL;
highlight->bg_color = TAG_HIGHLIGHT_NORMAL;
att_set = 1;
}
} else if (!strcasecmp(h_bit, "RVid")) {
if (att_set)
highlight->attributes |= TAG_HIGHLIGHT_RVID;
else {
highlight->attributes = TAG_HIGHLIGHT_RVID;
highlight->fg_color = TAG_HIGHLIGHT_NORMAL;
highlight->bg_color = TAG_HIGHLIGHT_NORMAL;
att_set = 1;
}
} else if (!strcasecmp(h_bit, "Blink")) {
if (att_set)
highlight->attributes |= TAG_HIGHLIGHT_BLINK;
else {
highlight->attributes = TAG_HIGHLIGHT_BLINK;
highlight->fg_color = TAG_HIGHLIGHT_NORMAL;
highlight->bg_color = TAG_HIGHLIGHT_NORMAL;
att_set = 1;
}
} else {
if (att_set)
string_to_color(config_info, highlight, h_bit);
else {
att_set = 1;
highlight->fg_color = TAG_HIGHLIGHT_NORMAL;
highlight->bg_color = TAG_HIGHLIGHT_NORMAL;
highlight->attributes = 0;
string_to_color(config_info, highlight, h_bit);
}
}
h_bit = strtok(NULL, "&");
}
}
unsigned int
string_to_binding_mask(struct config_info *config_info, char *s)
{
char *b_bit;
unsigned int mask = 0;
b_bit = strtok(s, "&");
while (b_bit != NULL) {
if (!strcasecmp(b_bit, "Button1"))
mask |= TAG_BINDING_BUTTON1;
else if (!strcasecmp(b_bit, "Button2"))
mask |= TAG_BINDING_BUTTON2;
else if (!strcasecmp(b_bit, "Button3"))
mask |= TAG_BINDING_BUTTON3;
else if (!strcasecmp(b_bit, "Shift"))
mask |= TAG_BINDING_SHIFT;
else if (!strcasecmp(b_bit, "Control"))
mask |= TAG_BINDING_CONTROL;
else if (!strcasecmp(b_bit, "Meta"))
mask |= TAG_BINDING_META;
else
configerror(config_info, "Unknown binding token");
b_bit = strtok(NULL, "&");
}
return mask;
}
#endif /* USE_ACTIVE_TAGS */