This is command.c in view mode; [Download] [Up]
/* Command table manipulation functions and vectors. Copyright (C) 1993 Sebastiano Vigna This file is part of ne, the nice editor. 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, 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. In other words, you are welcome to use, share and improve this program. You are forbidden to forbid anyone else to use, share and improve what you give them. Help stamp out software-hoarding! */ #include "ne.h" #include "help.h" /* The standard macro descriptor allocation dimension. */ #define STD_MACRO_DESC_SIZE 1024 /* The maximum width for a command (used for displaying the command names with the string requester). It allows four columns on an 80x25 screen. */ #define MAX_COMMAND_WIDTH 18 /* This structure represents a command. It includes a long and a short name, a NULL-terminated vector of help strings (of specified length) and some flags which are related to the syntax and the semantics of the arguments. */ typedef struct { const char *name, *short_name; const char * const *help; int help_len; int flags; } command; #define POSSIBLY_NO_ARGS (1<<0) /* This command can be called without argument. */ #define NO_ARGS (1<<1) /* This command must be called without argument. */ #define ARG_IS_STRING (1<<2) /* The argument is a string (default is a number). */ #define IS_OPTION (1<<3) /* The command controls an option, and can be played while exec_only_options is true. */ #define DO_NOT_RECORD (1<<4) /* Never record this command. */ #define EMPTY_STRING_OK (1<<5) /* This command can accept an empty string ("") as an argument. */ /* This macro makes the following vector more readable. */ #define HELP_LEN(x) (sizeof(x ## _HELP)/sizeof(char *)-1) /* This is the command vector. Is *has* to be in the same order as the action enum in ne.h. I.e., each action identifier indexes exactly and directly its command. Note that the command names come from names.h, and the help names come from help.h. */ static const command commands[ACTION_COUNT] = { { NOP_NAME, NOP_ABBREV, NOP_HELP, HELP_LEN(NOP), NO_ARGS }, { OPEN_NAME, OPEN_ABBREV, OPEN_HELP, HELP_LEN(OPEN), POSSIBLY_NO_ARGS | ARG_IS_STRING }, { OPENNEW_NAME, OPENNEW_ABBREV, OPENNEW_HELP, HELP_LEN(OPENNEW), POSSIBLY_NO_ARGS | ARG_IS_STRING }, { SAVE_NAME, SAVE_ABBREV, SAVE_HELP, HELP_LEN(SAVE), NO_ARGS }, { SAVEAS_NAME, SAVEAS_ABBREV, SAVEAS_HELP, HELP_LEN(SAVEAS), POSSIBLY_NO_ARGS | ARG_IS_STRING }, { CLEAR_NAME, CLEAR_ABBREV, CLEAR_HELP, HELP_LEN(CLEAR), NO_ARGS }, { QUIT_NAME, QUIT_ABBREV, QUIT_HELP, HELP_LEN(QUIT), NO_ARGS }, { EXIT_NAME, EXIT_ABBREV, EXIT_HELP, HELP_LEN(EXIT), NO_ARGS }, { ABOUT_NAME, ABOUT_ABBREV, ABOUT_HELP, HELP_LEN(ABOUT), NO_ARGS }, { NEWDOC_NAME, NEWDOC_ABBREV, NEWDOC_HELP, HELP_LEN(NEWDOC), NO_ARGS }, { CLOSEDOC_NAME, CLOSEDOC_ABBREV, CLOSEDOC_HELP, HELP_LEN(CLOSEDOC), NO_ARGS }, { NEXTDOC_NAME, NEXTDOC_ABBREV, NEXTDOC_HELP, HELP_LEN(NEXTDOC), POSSIBLY_NO_ARGS }, { PREVDOC_NAME, PREVDOC_ABBREV, PREVDOC_HELP, HELP_LEN(PREVDOC), POSSIBLY_NO_ARGS }, { SELECTDOC_NAME, SELECTDOC_ABBREV, SELECTDOC_HELP, HELP_LEN(SELECTDOC), POSSIBLY_NO_ARGS }, { MARK_NAME, MARK_ABBREV, MARK_HELP, HELP_LEN(MARK), POSSIBLY_NO_ARGS | IS_OPTION }, { CUT_NAME, CUT_ABBREV, CUT_HELP, HELP_LEN(CUT), POSSIBLY_NO_ARGS }, { COPY_NAME, COPY_ABBREV, COPY_HELP, HELP_LEN(COPY), POSSIBLY_NO_ARGS }, { PASTE_NAME, PASTE_ABBREV, PASTE_HELP, HELP_LEN(PASTE), POSSIBLY_NO_ARGS }, { ERASE_NAME, ERASE_ABBREV, ERASE_HELP, HELP_LEN(ERASE), POSSIBLY_NO_ARGS }, { MARKVERT_NAME, MARKVERT_ABBREV, MARKVERT_HELP, HELP_LEN(MARKVERT), POSSIBLY_NO_ARGS | IS_OPTION }, { PASTEVERT_NAME, PASTEVERT_ABBREV, PASTEVERT_HELP, HELP_LEN(PASTEVERT), POSSIBLY_NO_ARGS }, { OPENCLIP_NAME, OPENCLIP_ABBREV, OPENCLIP_HELP, HELP_LEN(OPENCLIP), POSSIBLY_NO_ARGS | ARG_IS_STRING }, { SAVECLIP_NAME, SAVECLIP_ABBREV, SAVECLIP_HELP, HELP_LEN(SAVECLIP), POSSIBLY_NO_ARGS | ARG_IS_STRING }, { FIND_NAME, FIND_ABBREV, FIND_HELP, HELP_LEN(FIND), POSSIBLY_NO_ARGS | ARG_IS_STRING }, { FINDREGEXP_NAME, FINDREGEXP_ABBREV, FINDREGEXP_HELP, HELP_LEN(FINDREGEXP), POSSIBLY_NO_ARGS | ARG_IS_STRING }, { REPLACE_NAME, REPLACE_ABBREV, REPLACE_HELP, HELP_LEN(REPLACE), POSSIBLY_NO_ARGS | ARG_IS_STRING | EMPTY_STRING_OK }, { REPLACEONCE_NAME, REPLACEONCE_ABBREV, REPLACEONCE_HELP, HELP_LEN(REPLACEONCE), POSSIBLY_NO_ARGS | ARG_IS_STRING | EMPTY_STRING_OK }, { REPLACEALL_NAME, REPLACEALL_ABBREV, REPLACEALL_HELP, HELP_LEN(REPLACEALL), POSSIBLY_NO_ARGS | ARG_IS_STRING | EMPTY_STRING_OK }, { REPEATLAST_NAME, REPEATLAST_ABBREV, REPEATLAST_HELP, HELP_LEN(REPEATLAST), POSSIBLY_NO_ARGS }, { GOTOLINE_NAME, GOTOLINE_ABBREV, GOTOLINE_HELP, HELP_LEN(GOTOLINE), POSSIBLY_NO_ARGS }, { GOTOCOLUMN_NAME, GOTOCOLUMN_ABBREV, GOTOCOLUMN_HELP, HELP_LEN(GOTOCOLUMN), POSSIBLY_NO_ARGS }, { GOTOMARK_NAME, GOTOMARK_ABBREV, GOTOMARK_HELP, HELP_LEN(GOTOMARK), NO_ARGS }, { MATCHBRACKET_NAME, MATCHBRACKET_ABBREV, MATCHBRACKET_HELP, HELP_LEN(MATCHBRACKET), NO_ARGS }, { RECORD_NAME, RECORD_ABBREV, RECORD_HELP, HELP_LEN(RECORD), POSSIBLY_NO_ARGS | IS_OPTION | DO_NOT_RECORD }, { PLAY_NAME, PLAY_ABBREV, PLAY_HELP, HELP_LEN(PLAY), POSSIBLY_NO_ARGS }, { OPENMACRO_NAME, OPENMACRO_ABBREV, OPENMACRO_HELP, HELP_LEN(OPENMACRO), POSSIBLY_NO_ARGS | ARG_IS_STRING }, { SAVEMACRO_NAME, SAVEMACRO_ABBREV, SAVEMACRO_HELP, HELP_LEN(SAVEMACRO), POSSIBLY_NO_ARGS | ARG_IS_STRING }, { MACRO_NAME, MACRO_ABBREV, MACRO_HELP, HELP_LEN(MACRO), POSSIBLY_NO_ARGS | ARG_IS_STRING | DO_NOT_RECORD }, { UNLOADMACROS_NAME, UNLOADMACROS_ABBREV, UNLOADMACROS_HELP, HELP_LEN(UNLOADMACROS), NO_ARGS }, { REFRESH_NAME, REFRESH_ABBREV, REFRESH_HELP, HELP_LEN(REFRESH), NO_ARGS }, { UNDELLINE_NAME, UNDELLINE_ABBREV, UNDELLINE_HELP, HELP_LEN(UNDELLINE), POSSIBLY_NO_ARGS }, { CENTER_NAME, CENTER_ABBREV, CENTER_HELP, HELP_LEN(CENTER), POSSIBLY_NO_ARGS }, { PARAGRAPH_NAME, PARAGRAPH_ABBREV, PARAGRAPH_HELP, HELP_LEN(PARAGRAPH), POSSIBLY_NO_ARGS }, { TOUPPER_NAME, TOUPPER_ABBREV, TOUPPER_HELP, HELP_LEN(TOUPPER), POSSIBLY_NO_ARGS }, { TOLOWER_NAME, TOLOWER_ABBREV, TOLOWER_HELP, HELP_LEN(TOLOWER), POSSIBLY_NO_ARGS }, { CAPITALIZE_NAME, CAPITALIZE_ABBREV, CAPITALIZE_HELP, HELP_LEN(CAPITALIZE), POSSIBLY_NO_ARGS }, { MOVELEFT_NAME, MOVELEFT_ABBREV, MOVELEFT_HELP, HELP_LEN(MOVELEFT), POSSIBLY_NO_ARGS }, { MOVERIGHT_NAME, MOVERIGHT_ABBREV, MOVERIGHT_HELP, HELP_LEN(MOVERIGHT), POSSIBLY_NO_ARGS }, { LINEUP_NAME, LINEUP_ABBREV, LINEUP_HELP, HELP_LEN(LINEUP), POSSIBLY_NO_ARGS }, { LINEDOWN_NAME, LINEDOWN_ABBREV, LINEDOWN_HELP, HELP_LEN(LINEDOWN), POSSIBLY_NO_ARGS }, { PREVPAGE_NAME, PREVPAGE_ABBREV, PREVPAGE_HELP, HELP_LEN(PREVPAGE), POSSIBLY_NO_ARGS }, { NEXTPAGE_NAME, NEXTPAGE_ABBREV, NEXTPAGE_HELP, HELP_LEN(NEXTPAGE), POSSIBLY_NO_ARGS }, { MOVEEOL_NAME, MOVEEOL_ABBREV, MOVEEOL_HELP, HELP_LEN(MOVEEOL), NO_ARGS }, { MOVESOL_NAME, MOVESOL_ABBREV, MOVESOL_HELP, HELP_LEN(MOVESOL), NO_ARGS }, { MOVEEOF_NAME, MOVEEOF_ABBREV, MOVEEOF_HELP, HELP_LEN(MOVEEOF), NO_ARGS }, { MOVESOF_NAME, MOVESOF_ABBREV, MOVESOF_HELP, HELP_LEN(MOVESOF), NO_ARGS }, { TOGGLESEOF_NAME, TOGGLESEOF_ABBREV, TOGGLESEOF_HELP, HELP_LEN(TOGGLESEOF), NO_ARGS }, { TOGGLESEOL_NAME, TOGGLESEOL_ABBREV, TOGGLESEOL_HELP, HELP_LEN(TOGGLESEOL), NO_ARGS }, { NEXTWORD_NAME, NEXTWORD_ABBREV, NEXTWORD_HELP, HELP_LEN(NEXTWORD), POSSIBLY_NO_ARGS }, { PREVWORD_NAME, PREVWORD_ABBREV, PREVWORD_HELP, HELP_LEN(PREVWORD), POSSIBLY_NO_ARGS }, { MOVEEOW_NAME, MOVEEOW_ABBREV, MOVEEOW_HELP, HELP_LEN(MOVEEOW), POSSIBLY_NO_ARGS }, { SETBOOKMARK_NAME, SETBOOKMARK_ABBREV, SETBOOKMARK_HELP, HELP_LEN(SETBOOKMARK), POSSIBLY_NO_ARGS }, { GOTOBOOKMARK_NAME, GOTOBOOKMARK_ABBREV, GOTOBOOKMARK_HELP, HELP_LEN(GOTOBOOKMARK), POSSIBLY_NO_ARGS }, { TABSIZE_NAME, TABSIZE_ABBREV, TABSIZE_HELP, HELP_LEN(TABSIZE), POSSIBLY_NO_ARGS | IS_OPTION }, { INSERT_NAME, INSERT_ABBREV, INSERT_HELP, HELP_LEN(INSERT), POSSIBLY_NO_ARGS | IS_OPTION}, { FREEFORM_NAME, FREEFORM_ABBREV, FREEFORM_HELP, HELP_LEN(FREEFORM), POSSIBLY_NO_ARGS | IS_OPTION }, { STATUSBAR_NAME, STATUSBAR_ABBREV, STATUSBAR_HELP, HELP_LEN(STATUSBAR), POSSIBLY_NO_ARGS | IS_OPTION }, { CASESEARCH_NAME, CASESEARCH_ABBREV, CASESEARCH_HELP, HELP_LEN(CASESEARCH), POSSIBLY_NO_ARGS | IS_OPTION }, { SEARCHBACK_NAME, SEARCHBACK_ABBREV, SEARCHBACK_HELP, HELP_LEN(SEARCHBACK), POSSIBLY_NO_ARGS | IS_OPTION }, { ESCAPETIME_NAME, ESCAPETIME_ABBREV, ESCAPETIME_HELP, HELP_LEN(ESCAPETIME), POSSIBLY_NO_ARGS | IS_OPTION }, { FASTGUI_NAME, FASTGUI_ABBREV, FASTGUI_HELP, HELP_LEN(FASTGUI), POSSIBLY_NO_ARGS | IS_OPTION }, { WORDWRAP_NAME, WORDWRAP_ABBREV, WORDWRAP_HELP, HELP_LEN(WORDWRAP), POSSIBLY_NO_ARGS | IS_OPTION }, { RIGHTMARGIN_NAME, RIGHTMARGIN_ABBREV, RIGHTMARGIN_HELP, HELP_LEN(RIGHTMARGIN), POSSIBLY_NO_ARGS | IS_OPTION }, { AUTOINDENT_NAME, AUTOINDENT_ABBREV, AUTOINDENT_HELP, HELP_LEN(AUTOINDENT), POSSIBLY_NO_ARGS | IS_OPTION }, { AUTOPREFS_NAME, AUTOPREFS_ABBREV, AUTOPREFS_HELP, HELP_LEN(AUTOPREFS), POSSIBLY_NO_ARGS | IS_OPTION }, { BINARY_NAME, BINARY_ABBREV, BINARY_HELP, HELP_LEN(BINARY), POSSIBLY_NO_ARGS | IS_OPTION }, { TURBO_NAME, TURBO_ABBREV, TURBO_HELP, HELP_LEN(TURBO), POSSIBLY_NO_ARGS | IS_OPTION }, { NOFILEREQ_NAME, NOFILEREQ_ABBREV, NOFILEREQ_HELP, HELP_LEN(NOFILEREQ), POSSIBLY_NO_ARGS | IS_OPTION }, { VERBOSEMACROS_NAME, VERBOSEMACROS_ABBREV, VERBOSEMACROS_HELP, HELP_LEN(VERBOSEMACROS), POSSIBLY_NO_ARGS | IS_OPTION }, { DOUNDO_NAME, DOUNDO_ABBREV, DOUNDO_HELP, HELP_LEN(DOUNDO), POSSIBLY_NO_ARGS | IS_OPTION }, { READONLY_NAME, READONLY_ABBREV, READONLY_HELP, HELP_LEN(READONLY), POSSIBLY_NO_ARGS | IS_OPTION }, { CLIPNUMBER_NAME, CLIPNUMBER_ABBREV, CLIPNUMBER_HELP, HELP_LEN(CLIPNUMBER), POSSIBLY_NO_ARGS | IS_OPTION }, { LOADPREFS_NAME, LOADPREFS_ABBREV, LOADPREFS_HELP, HELP_LEN(LOADPREFS), POSSIBLY_NO_ARGS | ARG_IS_STRING }, { SAVEPREFS_NAME, SAVEPREFS_ABBREV, SAVEPREFS_HELP, HELP_LEN(SAVEPREFS), POSSIBLY_NO_ARGS | ARG_IS_STRING }, { LOADAUTOPREFS_NAME, LOADAUTOPREFS_ABBREV, LOADAUTOPREFS_HELP, HELP_LEN(LOADAUTOPREFS), NO_ARGS }, { SAVEAUTOPREFS_NAME, SAVEAUTOPREFS_ABBREV, SAVEAUTOPREFS_HELP, HELP_LEN(SAVEAUTOPREFS), NO_ARGS }, { SAVEDEFPREFS_NAME, SAVEDEFPREFS_ABBREV, SAVEDEFPREFS_HELP, HELP_LEN(SAVEDEFPREFS), NO_ARGS }, { INSERTCHAR_NAME, INSERTCHAR_ABBREV, INSERTCHAR_HELP, HELP_LEN(INSERTCHAR) }, { DELETECHAR_NAME, DELETECHAR_ABBREV, DELETECHAR_HELP, HELP_LEN(DELETECHAR), POSSIBLY_NO_ARGS }, { BACKSPACE_NAME, BACKSPACE_ABBREV, BACKSPACE_HELP, HELP_LEN(BACKSPACE), POSSIBLY_NO_ARGS }, { INSERTLINE_NAME, INSERTLINE_ABBREV, INSERTLINE_HELP, HELP_LEN(INSERTLINE), POSSIBLY_NO_ARGS }, { DELETELINE_NAME, DELETELINE_ABBREV, DELETELINE_HELP, HELP_LEN(DELETELINE), POSSIBLY_NO_ARGS }, { DELETEEOL_NAME, DELETEEOL_ABBREV, DELETEEOL_HELP, HELP_LEN(DELETEEOL), NO_ARGS }, { UNDO_NAME, UNDO_ABBREV, UNDO_HELP, HELP_LEN(UNDO), POSSIBLY_NO_ARGS }, { REDO_NAME, REDO_ABBREV, REDO_HELP, HELP_LEN(REDO), POSSIBLY_NO_ARGS }, { HELP_NAME, HELP_ABBREV, HELP_HELP, HELP_LEN(HELP), POSSIBLY_NO_ARGS | ARG_IS_STRING | DO_NOT_RECORD }, { BEEP_NAME, BEEP_ABBREV, BEEP_HELP, HELP_LEN(BEEP), NO_ARGS }, { FLASH_NAME, FLASH_ABBREV, FLASH_HELP, HELP_LEN(FLASH), NO_ARGS }, { EXEC_NAME, EXEC_ABBREV, EXEC_HELP, HELP_LEN(EXEC), POSSIBLY_NO_ARGS | ARG_IS_STRING | DO_NOT_RECORD }, { ESCAPE_NAME, ESCAPE_ABBREV, ESCAPE_HELP, HELP_LEN(ESCAPE), POSSIBLY_NO_ARGS | DO_NOT_RECORD }, { SYSTEM_NAME, SYSTEM_ABBREV, SYSTEM_HELP, HELP_LEN(SYSTEM), POSSIBLY_NO_ARGS | ARG_IS_STRING }, { THROUGH_NAME, THROUGH_ABBREV, THROUGH_HELP, HELP_LEN(THROUGH), POSSIBLY_NO_ARGS | ARG_IS_STRING } }; /* This function checks if the command line m starts with the command c. Return 0 on success, non-zero on failure. */ int cmdcmp(const char *c, const char *m) { assert(c != NULL); assert(m != NULL); while (*c && up_case[*(const unsigned char *)c] == up_case[*(const unsigned char *)m]) { c++ ; m++ ; } return(*c || *m && !isspace(*m)) ; } /* These vectors are hash tables with no conflicts. For each command, the element indexed by the hashed name of the command contains the command number plus one. Thus, only one strcmp() is necessary when analyzing the command line. This technique offers a light speed comparison against the command names, with a very small memory usage. The tables are precompiled, so they can be moved to the text segment. */ extern const unsigned char hash_table[HASH_TABLE_SIZE]; extern const unsigned char short_hash_table[HASH_TABLE_SIZE]; /* This table *can* have conflicts, so that we can keep its size much smaller. */ static macro_desc *macro_hash_table[MACRO_HASH_TABLE_SIZE]; /* This is the command name hashing function. Note that we consider a string as a number in base 26 (A=0, etc.), and then we take the number modulo HASH_TABLE_SIZE. We consider only the 5 least significant bits because they are the bits which distinguish characters, independently of their case. We are not interested in strings which contain non-alphabetical characters, because they will certainly generate an error (the only exception notably being "R1"). We should subtract 1 to s[i], but this doesn't seem to produce any improvement. hash_macro() act as hash(), but uses MACRO_HASH_TABLE_SIZE for its modulo. */ static int hash(const char *s, int len) { int i, h; for(h=i=0; i<len; i++) h = (h*26+(((const unsigned char *)s)[i] & 0x1F)) % HASH_TABLE_SIZE; return(h); } static int hash_macro(const char *s, int len) { int i, h; for(h=i=0; i<len; i++) h = (h*26+(((const unsigned char *)s)[i] & 0x1F)) % MACRO_HASH_TABLE_SIZE; return(h); } /* This function parses a command line. It has an interface which is slightly varied with respect to the other functions of ne. In case of a parsing error, an error index *with sign inverted* is passed back. In case parsing succeeds, an (greater or equal to zero) action is returned, and the numerical or string argument is passed in the variables pointed to by num_arg or string_arg, respectively, if the are non-NULL. Otherwise, the argument is not passed back. The string argument is free()able. -1 and NULL denote the lack of an optional numerical or string argument, respectively. NOP is returned on a NOP command, or on a comment line (any line whose first non-space character is a non alphabetic character). Note that the various syntax flags are used here. */ action parse_command_line(const char *command_line, int *num_arg, char **string_arg, int exec_only_options) { int h; action a; const char *p = command_line; if (num_arg) *num_arg = -1; if (string_arg) *string_arg = NULL; if (!command_line) return(NOP); if (!*p) return(NOP); while(isspace(*p)) p++; command_line = p; if (!isalpha(*p)) return(NOP); while(*p && !isspace(*p)) p++; h = hash(command_line, p-command_line); if ((a = hash_table[h]) && !cmdcmp(commands[--a].name, command_line) || (a = short_hash_table[h]) && !cmdcmp(commands[--a].short_name, command_line)) { while(isspace(*p)) p++; if (*p || (commands[a].flags & (NO_ARGS | POSSIBLY_NO_ARGS))) { if (!*p || !(commands[a].flags & NO_ARGS)) { if (!*p || (commands[a].flags & ARG_IS_STRING) || (*p>='0' && *p<='9')) { if ((commands[a].flags & IS_OPTION) || !exec_only_options) { if (*p) { if ((commands[a].flags & ARG_IS_STRING) && string_arg) { int len = strlen(p); if (len>1 && *p == '"' && p[len-1] == '"') { p++; len -= 2; } if (len == 0 && !(commands[a].flags & EMPTY_STRING_OK)) return(-STRING_IS_EMPTY); if (!(*string_arg = malloc(len+1))) return(-OUT_OF_MEMORY); memcpy(*string_arg, p, len); (*string_arg)[len] = 0; } else if (num_arg) *num_arg = atoi(p); } return(a); } return(-CAN_EXECUTE_ONLY_OPTIONS); } return(-HAS_NUMERIC_ARGUMENT); } return(-HAS_NO_ARGUMENT); } return(-REQUIRES_ARGUMENT); } return(-NO_SUCH_COMMAND); } /* This function parses and executes a command line. Standard error codes are returned. If the search for a standard command fails, we try to execute a macro in ~/.ne with the same name. */ int execute_command_line(buffer *b, const char *command_line) { int n; action a; char *p; if ((a = parse_command_line(command_line, &n, &p, b->exec_only_options)) >= 0) return(do_action(b, a, n, p)); a = -a; if ((a == NO_SUCH_COMMAND) && (a = execute_macro(b, command_line)) == CANT_OPEN_MACRO) a = NO_SUCH_COMMAND; return(a); } /* This function allocates a macro descriptor. It does not allocate the internal character stream, which has to be allocated and stuffed in separately. */ macro_desc *alloc_macro_desc(void) { return(calloc(1, sizeof(macro_desc))); } /* This function frees a macro descriptors. */ void free_macro_desc(macro_desc *md) { if (!md) return; assert_macro_desc(md); free(md->name); free_char_stream(md->cs); free(md); } /* Here we record an action in a character stream. The action name is expanded in a short or long name, depending on the value of the verbose parameter. A numerical or string argument are expanded and copied, too. If the command should not be recorded (for instance, ESCAPE) we return. */ void record_action(char_stream *cs, action a, int c, char *p, int verbose) { char t[MAX_INT_LEN+2]; if (commands[a].flags & DO_NOT_RECORD) return; if (verbose) add_to_stream(cs, commands[a].name, strlen(commands[a].name)); else add_to_stream(cs, commands[a].short_name, strlen(commands[a].short_name)); if (c>=0) { sprintf(t, " %d", c); add_to_stream(cs, t, strlen(t)); } else if (p) { add_to_stream(cs, " ", 1); if (!*p || isspace(*p)) add_to_stream(cs, "\"", 1); add_to_stream(cs, p, strlen(p)); if (!*p || isspace(*p)) add_to_stream(cs, "\"", 1); } add_to_stream(cs, "", 1); } /* This function is the ultimate goal of this file. It plays a character stream, considering each line as a command line. It polls the global stop variable in order to check for the user interrupting. Note that the macro is duplicated before execution: this is absolutely necessary, for otherwise a call to CloseDoc, Record or UnloadMacros could free() the block of memory which we are executing. */ int play_macro(buffer *b, char_stream *cs) { int error = OK, len; char *p, *stream; if (!cs) return(ERROR); /* If len is 0 or 1, the character stream does not contain anything. */ if ((len = cs->len) < 2) return(OK); if (!(p = stream = malloc(len))) return(OUT_OF_MEMORY); memcpy(stream, cs->stream, len); stop = FALSE; while(!stop && p - stream < len) { if (error = execute_command_line(b, p)) break; p += strlen(p)+1; } free(stream); return(stop ? STOPPED : error); } /* This function loads a macro, and puts it in the global macro hash table. file_part is applied to the name argument before storing it and hashing it. Note that if the macro can't be opened, we retry prefixing its name with the preferences directory name (~/.ne/). Thus, for instance, all autopreferences file whose name does not conflict with internal commands can be executed transparently just by typing their name. */ macro_desc *load_macro(const char *name) { int h; char *macro_dir, *prefs_dir; char_stream *cs; macro_desc *md, **m; assert(name != NULL); if (!(md = alloc_macro_desc())) return(NULL); cs = load_stream(md->cs, name); if (!cs && (prefs_dir = exists_prefs_dir()) && (macro_dir = malloc(strlen(prefs_dir)+2+strlen(name)))) { strcat(strcpy(macro_dir, prefs_dir), name); cs = load_stream(md->cs, macro_dir); free(macro_dir); } if (cs) { md->cs = cs; md->name = str_dup(file_part(name)); h = hash_macro(md->name, strlen(md->name)); m = ¯o_hash_table[h]; while(*m) m = &((*m)->next); *m = md; return(md); } free_macro_desc(md); return(NULL); } /* This function executes a named macro. If the macro is not in the global macro list, it is loaded. A standard error code is returned. */ int execute_macro(buffer *b, const char *name) { const char *p; macro_desc *md; int h; p = file_part(name); h = hash_macro(p, strlen(p)); md = macro_hash_table[h]; while(md && cmdcmp(md->name, p)) md = md->next; if (!md) md = load_macro(name); assert_macro_desc(md); if (md) return(play_macro(b, md->cs)); return(CANT_OPEN_MACRO); } void unload_macros(void) { int i; macro_desc *m, *n; for(i=0; i<MACRO_HASH_TABLE_SIZE; i++) { m = macro_hash_table[i]; macro_hash_table[i] = NULL; while(m) { n = m->next; free_macro_desc(m); m = n; } } } /* This function helps. The help text relative to the command name pointed to by p is displayed (p can also contain arguments). If p is NULL, the alphabetically ordered list of commands is displayed with the string requester. The help finishes when the user escapes. */ void help(char *p) { action a; int i; do { if (p || (i = request_strings(command_names, ACTION_COUNT, MAX_COMMAND_WIDTH)) >= 0) { if (p) { for(i=0; i<strlen(p); i++) if (isspace(p[i])) break; i = hash(p, i); if ((a = hash_table[i]) && !cmdcmp(commands[--a].name, p) || (a = short_hash_table[i]) && !cmdcmp(commands[--a].short_name, p)) i = a; else i = -1; p = NULL; } else i = parse_command_line(command_names[i], NULL, NULL, FALSE); if (i < 0) { i = 0; continue; } assert(i>=0 && i<ACTION_COUNT); i = request_strings(commands[i].help, commands[i].help_len, columns); } } while(i>=0); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.