/* * This file contains the utilities to read in a ".tdecfg" file. * * Most of this stuff is duplicated from the cfgfile.c functions. In * Linux, this utility searches the CWD first then it searches the * HOME directory for the ".tdecfg" file. * * Many thanks to for the idea and sample code * for this function. * * New editor name: TDE, the Thomson-Davis Editor. * Author: Frank Davis * Date: June 5, 1991, version 1.0 * Date: July 29, 1991, version 1.1 * Date: October 5, 1991, version 1.2 * Date: January 20, 1992, version 1.3 * Date: February 17, 1992, version 1.4 * Date: April 1, 1992, version 1.5 * Date: June 5, 1992, version 2.0 * Date: October 31, 1992, version 2.1 * Date: April 1, 1993, version 2.2 * Date: June 5, 1993, version 3.0 * Date: August 29, 1993, version 3.1 * Date: November 13, 1993, version 3.2 * Date: June 5, 1994, version 4.0 * * This code is released into the public domain, Frank Davis. * You may distribute it freely. */ #include "tdestr.h" /* tde types */ #include "common.h" #include "define.h" #include "tdefunc.h" #if defined( __UNIX__ ) #include "cfgfile.h" #endif #if !defined( __UNIX__ ) #include /* for renaming files */ #include /* for direct BIOS keyboard input */ #include /* for file attribute code */ #if defined( __MSC__ ) #include #include /* S_IWRITE etc */ #endif #include /* S_IWRITE etc */ #endif #include /* open flags */ #if defined( __UNIX__ ) char *line_in; /* line buffer */ char *line_out; /* line buffer */ int stroke_count; /* global variable for macro strokes */ unsigned int line_no; /* global variable for line count */ int need_a_redraw; /* if we redefined colors, then redraw screen */ int need_mode_line; /* if we redefined modes, then redraw line */ int need_rulers; /* if we redefined rulers, then redraw ruler */ /* * UNIX stuff: let's make us an array of colors used in curses. */ int curse_col[8] = { COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE }; #endif /* * Name: tdecfgfile * Date: June 5, 1994 * Notes: read in a configuration file at any time. * read in a configuration file when we first fire up TDE in * a linux (unix) environment. */ int tdecfgfile( TDE_WIN *window ) { FILE *config; int rc; int prompt_line; int *clr; #if defined( __UNIX__ ) char fname[PATH_MAX]; /* new name for file */ char *home; #else char fname[MAX_COLS]; /* new name for file */ #endif rc = OK; #if defined( __UNIX__ ) if (window != NULL) prompt_line = window->bottom_line; else prompt_line = g_display.nlines; /* * first, make sure we can alloc space for line buffers. * line buffers are needed for reading the config file. */ line_in = my_malloc( MAX_LINE_LENGTH - 1, &rc ); if (rc == OK) line_out = my_malloc( g_display.ncols + 2, &rc ); else line_out = NULL; if (rc == OK) { /* * prompt for the configuration file name. */ #if defined( __UNIX__ ) /* * in Linux, we search for a ".tdecfg" file in * 1) current working directory == "." * 2) the user's home directory. * * CONFIGFILE is defined in tdestr.h */ strcpy( fname, "." ); strcat( fname, "/" ); strcat( fname, CONFIGFILE ); if (access( fname, F_OK ) != 0) { /* * could not find config file in cwd. try user's home directory. */ home = (char *)getenv( "HOME" ); if (home == NULL) rc = ERROR; else { strcpy( fname, home ); strcat( fname, "/" ); strcat( fname, CONFIGFILE ); } } #else *fname = '\0'; rc = get_name( config1, prompt_line, fname, g_display.message_color ); #endif if (rc == OK) { if ((config = fopen( fname, "r" )) == NULL) { rc = ERROR; if (window != NULL) { combine_strings( line_out, main7a, fname, main7b ); error( WARNING, prompt_line, line_out ); } } /* * if everything is everthing so far, get the current editor settings. */ if (rc == OK) { need_a_redraw = FALSE; need_mode_line = FALSE; need_rulers = FALSE; stroke_count = get_stroke_count( ); line_no = 1; while (!feof( config )) { if (fgets( line_in, 1500, config ) == NULL) break; /* * for convenience, let's remove the pair from * from MSDOS-type text files. */ #if defined( __UNIX__ ) remove_cr( line_in ); #endif parse_line( line_in, prompt_line ); ++line_no; } fclose( config ); if (need_a_redraw && g_display.adapter != MDA) { clr = &colour.clr[1][0]; g_display.head_color = *clr++; g_display.text_color = *clr++; g_display.dirty_color = *clr++; g_display.mode_color = *clr++; g_display.block_color = *clr++; g_display.message_color = *clr++; g_display.help_color = *clr++; g_display.diag_color = *clr++; g_display.eof_color = *clr++; g_display.curl_color = *clr++; g_display.ruler_color = *clr++; g_display.ruler_pointer = *clr++; g_display.hilited_file = *clr++; g_display.overscan = *clr; if (window != NULL) redraw_screen( window ); } if (need_mode_line && window != NULL) show_modes( ); if (need_rulers && window != NULL) show_all_rulers( ); } } } else { /* * not enough memory */ error( WARNING, prompt_line, main4 ); rc = ERROR; } if (line_in != NULL) my_free( line_in ); if (line_out != NULL) my_free( line_out ); #endif return( rc ); } #if defined( __UNIX__ ) /* * Name: remove_cr * Purpose: get rid of * Date: June 5, 1994 * Passed: line: line of text * Notes: UNIX don't like . if we read MSDOS config files, * we need to get rid of the thing. change to . */ void remove_cr( char *line ) { if (line != NULL) { while (*line) { if (*line == '\r') *line = '\n'; ++line; } } } /* * Name: parse_line * Purpose: real work horse of the configuration utility, figure out what * we need to do with each line of the config file. * Date: June 5, 1994 * Passed: line: line that contains the text to parse */ void parse_line( char *line, int prompt_line ) { char key[1042]; /* buffer to hold any token that we parse */ char *residue; /* pointer to next item in line, if it exists */ int key_no; /* index into key array */ int parent_key; /* 1st of two-combination keys */ int color; /* color field */ int mode_index; /* index in mode array */ int func_no; /* function number we want to assign to a key */ int color_no; /* attribute we want to assign to a color field */ int mode_no; /* mode number we want to assign to a mode */ int found; /* boolean, did we find a valid key, color, or mode? */ int i; /* * find the first token and put it in key. residue points to next token. */ residue = parse_token( line, key ); if (*key != '\0' && *key != ';') { if (strlen( key ) > 1) { /* * try to find a valid key */ found = FALSE; key_no = search( key, valid_keys, AVAIL_KEYS-1 ); if (key_no != ERROR) { /* * find the function assignment */ found = TRUE; if (residue != NULL) { residue = parse_token( residue, key ); /* * if this is not a comment, find the function to assign * to key. clear any previous macro or key assignment. */ if (*key != '\0' && *key != ';') { func_no = search( key, valid_func, NUM_FUNCS ); if (func_no != ERROR) { clear_previous_twokey( key_no ); clear_previous_macro( key_no ); key_func.key[key_no] = func_no; if (func_no == PlayBack) parse_macro( key_no, residue, prompt_line ); } else { parent_key = key_no; /* * was the second key one letter? */ if (strlen( key ) == 1) { key_no = *key; residue = parse_token( residue, key ); if (*key != '\0' && *key != ';') { func_no = search( key, valid_func, NUM_FUNCS ); if (func_no != ERROR && func_no != PlayBack) { if (insert_twokey( parent_key+256, key_no, func_no ) == ERROR) { combine_strings( line_out, config2, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } } else { if ( func_no == ERROR) { combine_strings( line_out, config3, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } else { combine_strings( line_out, config4, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } } } } else { residue = parse_token( residue, key ); key_no = search( key, valid_keys, AVAIL_KEYS-1 ); if (key_no != ERROR && *key != '\0' && *key != ';') { func_no = search( key, valid_func, NUM_FUNCS ); if (func_no != ERROR && func_no != PlayBack) { if (insert_twokey( parent_key+256, key_no+256, func_no ) == ERROR) { combine_strings( line_out, config5, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } } else { if ( func_no == ERROR) { combine_strings( line_out, config3, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } else { combine_strings( line_out, config4, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } } } else { combine_strings( line_out, config3, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } } } } } } /* * valid key not found, now try a valid color */ if (!found) { color = search( key, valid_colors, (NUM_COLORS * 2) - 1 ); if (color != ERROR) { if (*key == 'm') i = 0; else i = 1; found = TRUE; if (residue != NULL) { residue = parse_token( residue, key ); /* * we found a color field and attribute. now, make sure * everything is everything before we assign the attribute * to the color field. */ if (*key != '\0' && *key != ';') { color_no = atoi( key ); if (color_no >= 0 && color_no <= 127) { colour.clr[i][color] = color_no; need_a_redraw = TRUE; } else { combine_strings( line_out, config6, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } } } } else { /* * see if this is a color pair for Linux curses. */ mode_no = search( key, valid_pairs, 7 ); if (mode_no != ERROR) { #if defined( __UNIX__ ) if (residue != NULL) { residue = parse_token( residue, key ); color = search( key, valid_curse, 7 ); if (color != ERROR) { if (residue != NULL) { /* * so far, we got pairx COLOR_y. * now get the "on". */ residue = parse_token( residue, key ); if (residue != NULL) { /* * now, get the background color. */ residue = parse_token( residue, key ); color_no = search( key, valid_curse, 7 ); /* * we just parsed a color pair line: * pairx COLOR_y on COLOR_z. */ if (color_no != ERROR) { found = TRUE; need_a_redraw = TRUE; init_pair( mode_no, color, color_no ); for (i=0; i<8; i++) tde_color_table[mode_no*16 + i] = COLOR_PAIR( mode_no ); for (i=8; i<16; i++) tde_color_table[mode_no*16 + i] = COLOR_PAIR( mode_no ) | A_BOLD; } } } } if (found == FALSE) { combine_strings( line_out, config24, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); found = TRUE; } } #else /* * if we are in MSDOS, don't even bother with parsing * curses junk. */ found = TRUE; #endif } } } /* * valid color not found, now try a valid mode */ if (!found) { mode_index = search( key, valid_modes, NUM_MODES-1 ); if (mode_index != ERROR) { found = TRUE; /* * if we find a valid mode, we need to search different * option arrays before we find a valid assignment. */ if (residue != NULL) { residue = parse_token( residue, key ); if (*key != '\0' && *key != ';') { mode_no = ERROR; switch ( mode_index ) { case Ins : case Ind : case Smart : case Trim : case Eol : case Backup : case Ruler : case InflateTabs : case JustRM : mode_no = search( key, off_on, 1 ); if (mode_no == ERROR) { combine_strings( line_out, config7, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } else { switch ( mode_index ) { case Ins : mode.insert = mode_no; break; case Ind : mode.indent = mode_no; break; case Smart : mode.smart_tab = mode_no; break; case Trim : mode.trailing = mode_no; break; case Eol : mode.show_eol = mode_no; break; case Backup : mode.do_backups = mode_no; break; case Ruler : mode.ruler = mode_no; break; case InflateTabs : mode.inflate_tabs = mode_no; break; case JustRM : mode.right_justify = mode_no; break; } need_mode_line = TRUE; } break; case LTAB : case PTAB : mode_no = atoi( key ); if (mode_no > 520 || mode_no < 1) { mode_no = ERROR; combine_strings( line_out, config8, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } else { if (mode_index == LTAB) mode.ltab_size = mode_no; else mode.ptab_size = mode_no; need_mode_line = TRUE; } break; case Left : mode_no = atoi( key ); if (mode_no < 1 || mode_no > mode.right_margin) { mode_no = ERROR; combine_strings( line_out, config9, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } else { mode.left_margin = --mode_no; need_rulers = TRUE; } break; case Para : mode_no = atoi( key ); if (mode_no < 1 || mode_no > mode.right_margin) { mode_no = ERROR; combine_strings( line_out, config10, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } else { mode.parg_margin = --mode_no; need_rulers = TRUE; } break; case Right : mode_no = atoi( key ); if (mode_no < mode.left_margin || mode_no > 1040) { mode_no = ERROR; combine_strings( line_out, config11, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } else { mode.right_margin = --mode_no; need_rulers = TRUE; } break; case Crlf : mode_no = search( key, valid_crlf, 1 ); if (mode_no == ERROR) { combine_strings( line_out, config12, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } else mode.crlf = mode_no; break; case WW : mode_no = search( key, valid_wraps, 2 ); if (mode_no == ERROR) { combine_strings( line_out, config13, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } else { mode.word_wrap = mode_no; need_mode_line = TRUE; } break; case Size : mode_no = search( key, valid_cursor, 1 ); if (mode_no == ERROR) { combine_strings( line_out, config14, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } else mode.cursor_size = mode_no; break; case Write_Z : mode_no = search( key, valid_z, 1 ); if (mode_no == ERROR) { combine_strings( line_out, config15, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } else { mode.control_z = mode_no; need_mode_line = TRUE; } break; case Date : mode_no = search( key, valid_dates, 5 ); if (mode_no == ERROR) { combine_strings( line_out, config16, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } else mode.date_style = mode_no; break; case Time : mode_no = search( key, valid_times, 1 ); if (mode_no == ERROR) { combine_strings( line_out, config17, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } else mode.time_style = mode_no; break; case Initcase : mode_no = search( key, init_case_modes, 1 ); if (mode_no == ERROR) { combine_strings( line_out, config18, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } else { mode.search_case = mode_no; need_mode_line = TRUE; } break; case Match : mode_no = OK; for (i=0; i<256; i++) sort_order.match[i] = (char)i; new_sort_order( key, sort_order.match ); break; case Ignore : mode_no = OK; for (i=0; i<256; i++) sort_order.ignore[i] = (char)i; for (i=65; i<91; i++) sort_order.ignore[i] = (char)(i + 32); new_sort_order( key, sort_order.ignore ); break; } if (mode_no == ERROR) { combine_strings( line_out, config19, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } } } } } if (!found) { combine_strings( line_out, config20, my_ltoa( line_no, key, 10 ), "" ); error( WARNING, prompt_line, line_out ); } } } } /* * Name: parse_token * Purpose: given an input line, find the first token * Date: June 5, 1994 * Passed: line: line that contains the text to parse * token: buffer to hold token * Returns: pointer in line to start next token search. * Notes: assume tokens are delimited by spaces. */ char *parse_token( char *line, char *token ) { /* * skip over any leading spaces. */ while (*line == ' ') ++line; /* * put the characters into the token array until we run into a space * or the terminating '\0'; */ while (*line != ' ' && *line != '\0' && *line != '\n') *token++ = *line++; *token = '\0'; /* * return what's left on the line, if anything. */ if (*line != '\0' && *line != '\n') return( line ); else return( NULL ); } /* * Name: search * Purpose: binary search a CONFIG_DEFS structure * Date: June 5, 1994 * Passed: token: token to search for * list: list of valid tokens * num: number of valid tokens in list * Returns: value of token assigned to matching token. * Notes: do a standard binary search. * instead of returning mid, lets return the value of the token * assigned to mid. */ int search( char *token, CONFIG_DEFS list[], int num ) { int bot; int mid; int top; int rc; bot = 0; top = num; while (bot <= top) { mid = (bot + top) / 2; #if defined( __UNIX__ ) rc = strcasecmp( token, list[mid].key ); #else rc = stricmp( token, list[mid].key ); #endif if (rc == 0) return( list[mid].key_index ); else if (rc < 0) top = mid - 1; else bot = mid + 1; } return( ERROR ); } /* * Name: parse_macro * Purpose: separate literals from keys in a macro definition * Date: June 5, 1994 * Passed: macro_key: key that we are a assigning a macro to * residue: pointer to macro defs * Notes: for each token in macro def, find out if it's a literal or a * function key. * a literal begins with a ". to put a " in a macro def, precede * a " with a ". */ void parse_macro( int macro_key, char *residue, int prompt_line ) { int rc; char literal[1042]; char temp[42]; char *l; int key_no; /* * reset any previous macro def. */ initialize_macro( macro_key ); while (residue != NULL) { /* * skip over any leading spaces. */ while (*residue == ' ') ++residue; /* * done if we hit a comment */ if (*residue == ';') residue = NULL; /* * check for a literal. */ else if (*residue == '\"') { rc = parse_literal( macro_key, residue, literal, &residue ); if (rc == OK) { l = literal; while (*l != '\0' && rc == OK) { rc = cfg_record_keys( macro_key, *l, prompt_line ); ++l; } } else { combine_strings( line_out, config21, my_ltoa( line_no, temp, 10 ), "" ); error( WARNING, prompt_line, line_out ); } /* * check for a function key. */ } else { residue = parse_token( residue, literal ); key_no = search( literal, valid_keys, AVAIL_KEYS ); if (key_no != ERROR) cfg_record_keys( macro_key, key_no+256, prompt_line ); else { combine_strings( line_out, config22, my_ltoa( line_no, temp, 10 ), "" ); error( WARNING, prompt_line, line_out ); } } } check_macro( macro_key ); } /* * Name: parse_literal * Purpose: get all letters in a literal * Date: June 5, 1994 * Passed: macro_key: key that we are a assigning a macro to * line: current line position * literal: buffer to hold literal * residue: pointer to next token in line * Notes: a literal begins with a ". to put a " in a macro def, precede * a " with a ". */ int parse_literal( int macro_key, char *line, char *literal, char **residue ) { int quote_state = 1; /* we've already seen one " before we get here */ line++; /* * put the characters into the literal array until we run into the * end of literal or terminating '\0'; */ while (*line != '\0' && *line != '\n') { if (*line != '\"') *literal++ = *line++; else { if (*(line+1) == '\"') { *literal++ = '\"'; line++; line++; } else { line++; --quote_state; break; } } } *literal = '\0'; /* * return what's left on the line, if anything. */ if (*line != '\0' && *line != '\n') *residue = line; else *residue = NULL; if (quote_state != 0) { *residue = NULL; return( ERROR ); } else return( OK ); } /* * Name: initialize_macro * Purpose: initialize the first key of a macro def * Date: June 5, 1994 * Passed: macro_key: key that we are a assigning a macro to * Notes: this function is ported directly from tde. */ void initialize_macro( int macro_key ) { register int next; int prev; next = macro.first_stroke[macro_key]; /* * initialize the first key in a macro def */ if (next != STROKE_LIMIT+1) { do { prev = next; next = macro.strokes[next].next; macro.strokes[prev].key = MAX_KEYS+1; macro.strokes[prev].next = STROKE_LIMIT+1; ++stroke_count; } while (next != -1); } /* * find the first open space and initialize */ for (next=0; macro.strokes[next].next != STROKE_LIMIT+1;) next++; macro.first_stroke[macro_key] = next; macro.strokes[next].key = -1; macro.strokes[next].next = -1; } /* * Name: clear_previous_macro * Purpose: clear any macro previously assigned to a key * Date: June 5, 1994 * Passed: macro_key: key that we are a assigning a macro to * Notes: this function is ported directly from tde. */ void clear_previous_macro( int macro_key ) { register int next; int prev; next = macro.first_stroke[macro_key]; /* * if key has already been assigned to a macro, clear macro def. */ if (next != STROKE_LIMIT+1) { do { prev = next; next = macro.strokes[next].next; macro.strokes[prev].key = MAX_KEYS+1; macro.strokes[prev].next = STROKE_LIMIT+1; } while (next != -1); } macro.first_stroke[macro_key] = STROKE_LIMIT+1; } /* * Name: check_macro * Purpose: see if macro def has any valid key. if not, clear macro * Date: June 5, 1994 * Passed: macro_key: key that we are a assigning a macro to * Notes: this function is ported directly from tde. */ void check_macro( int macro_key ) { register int next; register int key; /* * see if the macro has any keystrokes. if not, then wipe it out. */ key = macro_key; if (key != 0) { next = macro.first_stroke[key]; if (macro.strokes[next].key == -1) { macro.strokes[next].key = MAX_KEYS+1; macro.strokes[next].next = STROKE_LIMIT+1; macro.first_stroke[key-256] = STROKE_LIMIT+1; if (key_func.key[key] == PlayBack) key_func.key[key] = 0; } } } /* * Name: cfg_record_keys * Purpose: save keystrokes in keystroke buffer * Date: June 5, 1994 * Passed: line: line to display prompts * Notes: -1 in .next field indicates the end of a recording * STROKE_LIMIT+1 in .next field indicates an unused space. * Recall, return codes are for macros. Since we do not allow * keys to be assigned to macro functions, let's just return OK; */ int cfg_record_keys( int macro_key, int key, int prompt_line ) { register int next; register int prev; int func; int rc; char temp[42]; rc = OK; if (stroke_count == 0) { combine_strings( line_out, config23, my_ltoa( line_no, temp, 10 ), "" ); error( WARNING, prompt_line, line_out ); rc = ERROR; } else { func = cfg_getfunc( key ); if (func != RecordMacro && func != SaveMacro && func != LoadMacro && func != ClearAllMacros) { /* * a -1 in the next field marks the end of the keystroke recording. */ next = macro.first_stroke[macro_key]; if (macro.strokes[next].next != STROKE_LIMIT+1) { while (macro.strokes[next].next != -1) next = macro.strokes[next].next; } prev = next; /* * now find an open space to record the current key. */ if (macro.strokes[next].key != -1) { for (; next < STROKE_LIMIT && macro.strokes[next].next != STROKE_LIMIT+1;) next++; if (next == STROKE_LIMIT) { for (next=0; next < prev && macro.strokes[next].next != STROKE_LIMIT+1;) next++; } } if (next == prev && macro.strokes[prev].key != -1) { rc = ERROR; } else { /* * next == prev if we are recording the initial macro node. */ macro.strokes[prev].next = next; macro.strokes[next].next = -1; macro.strokes[next].key = key; stroke_count--; } } } return( rc ); } /* * Name: new_sort_order * Purpose: change the sort order * Date: October 31, 1992 * Notes: New sort oder starts at the @ sign */ void new_sort_order( unsigned char *residue, unsigned char *sort ) { int i; sort += 33; for (i=33; *residue != '\0' && *residue != '\n' && i <= 255; i++) *sort++ = *residue++; } /* * Name: get_stroke_count * Purpose: count unassigned nodes in macro buff * Date: June 5, 1992 * Returns: number of strokes left in macro buffer. */ int get_stroke_count( void ) { int count = 0; int i; for (i=0; i