diff options
| author | Jon duSaint | 2022-04-30 16:16:25 -0700 |
|---|---|---|
| committer | Jon duSaint | 2022-04-30 16:16:25 -0700 |
| commit | 3160d814a1a088cfbcbd3c48c02d36273fd56383 (patch) | |
| tree | ee703f562c870ee7ea675b8b682a48da2750ecfa /tap | |
| parent | 659f12ede69726f46487d6e44aa79f48c2bd2aae (diff) | |
Commit a bunch of old software
Diffstat (limited to 'tap')
| -rwxr-xr-x | tap/BUGS | 0 | ||||
| -rwxr-xr-x | tap/INSTALL | 0 | ||||
| -rwxr-xr-x | tap/Makefile | 69 | ||||
| -rwxr-xr-x | tap/NEWS | 0 | ||||
| -rwxr-xr-x | tap/README | 8 | ||||
| -rwxr-xr-x | tap/command_struct.h | 122 | ||||
| -rwxr-xr-x | tap/commands/Makefile | 18 | ||||
| -rwxr-xr-x | tap/commands/alias.c | 185 | ||||
| -rwxr-xr-x | tap/commands/commands.h | 30 | ||||
| -rwxr-xr-x | tap/commands/serial.c | 110 | ||||
| -rwxr-xr-x | tap/commands/set.c | 315 | ||||
| -rwxr-xr-x | tap/commands/translate.c | 431 | ||||
| -rwxr-xr-x | tap/commands/user.c | 378 | ||||
| -rwxr-xr-x | tap/memmgr.c | 257 | ||||
| -rwxr-xr-x | tap/readline/Makefile | 38 | ||||
| -rwxr-xr-x | tap/readline/history.c | 244 | ||||
| -rwxr-xr-x | tap/readline/line.c | 340 | ||||
| -rwxr-xr-x | tap/readline/readline.h | 243 | ||||
| -rwxr-xr-x | tap/readline/ring.c | 114 | ||||
| -rwxr-xr-x | tap/readline/tab.c | 530 | ||||
| -rwxr-xr-x | tap/readline/text.c | 354 | ||||
| -rwxr-xr-x | tap/serial.c | 87 | ||||
| -rwxr-xr-x | tap/tap.c | 274 | ||||
| -rwxr-xr-x | tap/tap.h | 370 | ||||
| -rwxr-xr-x | tap/tap.texinfo | 826 | ||||
| -rwxr-xr-x | tap/ui.c | 369 | ||||
| -rwxr-xr-x | tap/version.h | 1 |
27 files changed, 5713 insertions, 0 deletions
diff --git a/tap/BUGS b/tap/BUGS new file mode 100755 index 0000000..e69de29 --- /dev/null +++ b/tap/BUGS diff --git a/tap/INSTALL b/tap/INSTALL new file mode 100755 index 0000000..e69de29 --- /dev/null +++ b/tap/INSTALL diff --git a/tap/Makefile b/tap/Makefile new file mode 100755 index 0000000..4f993f3 --- /dev/null +++ b/tap/Makefile @@ -0,0 +1,69 @@ +# Makefile for Tap + +CC = gcc +CFLAGS = -O2 -g -Wall +LIBS = -lncurses + +TAP = tap + +PREFIX = /usr/local +BINDIR = $(PREFIX)/bin +INFODIR = $(PREFIX)/info +MANDIR = $(PREFIX)/man/man1 +DOCDIR = $(PREFIX)/doc/tap +INSTALL-INFO = /sbin/install-info + +SRCFILES = memmgr.c serial.c tap.c ui.c +HDRFILES = tap.h version.h command_struct.h +OBJFILES = $(SRCFILES:%.c=%.o) + +READLINE = readline/readline.o +READLINESRC = readline/*.c + +COMMANDS = commands/commands.o +COMMANDSSRC = commands/*.c + +DOCEXTRAS = tap.cp tap.fn tap.ky tap.pg tap.tp tap.vr \ + tap.aux tap.log tap.cps tap.toc +DOCFILES = tap.dvi tap.ps tap.html tap.info + + +.PHONY : all doc ps dvi info html install clean + + +all : $(TAP) + +$(TAP) : $(READLINE) $(COMMANDS) $(SERIAL) $(OBJFILES) $(HDRFILES) + $(CC) $(CFLAGS) $(LIBS) -o $@ $(OBJFILES) $(READLINE) $(COMMANDS) + +$(READLINE) : $(READLINESRC) + make -C readline + +$(COMMANDS) : $(COMMANDSSRC) + make -C commands + + +doc : tap.ps tap.html tap.info tap.texinfo + +ps : tap.ps +dvi : tap.dvi +info : tap.info +html : tap.html + +tap.ps : tap.dvi tap.texinfo + dvips tap.dvi -o tap.ps +tap.dvi : tap.texinfo + texi2dvi tap.texinfo +tap.info : tap.texinfo + makeinfo tap.texinfo +tap.html : tap.texinfo + makeinfo --html tap.texinfo + + +install : tap info html + + +clean : + make -C readline clean + make -C commands clean + rm -f $(OBJFILES) $(TAP) $(DOCFILES) $(DOCEXTRAS) core *~ diff --git a/tap/NEWS b/tap/NEWS new file mode 100755 index 0000000..e69de29 --- /dev/null +++ b/tap/NEWS diff --git a/tap/README b/tap/README new file mode 100755 index 0000000..6d9a9a8 --- /dev/null +++ b/tap/README @@ -0,0 +1,8 @@ +tap - a serial line tapper + +Started sometime around 17 July 1999. + +This is not an official release. Some sections of code aren't yet +complete, and a lot hasn't been thoroughly tested. Also, although it +should be totally portable (among Linuxes), it has only been tested on +Linux/Alpha, so there might be some 64-bit-isms (though not likely). diff --git a/tap/command_struct.h b/tap/command_struct.h new file mode 100755 index 0000000..db9e0a6 --- /dev/null +++ b/tap/command_struct.h @@ -0,0 +1,122 @@ +/* command_struct.h + * This file contains all of the external commands for Tap. + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of Tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +struct command commands[] = { + /* help */ + { "help", help, + "unalias,set,quit,print,out,open,help,close,clear,capture,alias", + "print help for a function", + "help [function]\n \ +print a summary of functions or help for a specific function\n" }, + + /* set */ + { "set", set, "of{ascii-cooked,ascii-raw,hex,oct,bin}," + "if{ascii-cooked,ascii-raw,hex,oct,bin},regsize{\\d}," + "mode{passive,active},echo{off,on},baud{\\d}", + "set various properties", + "set <properties> [values]\n \ +where properties are\n \ +if <bin|oct|hex|ascii-raw|ascii-cooked> - data entry format\n \ +In ascii-cooked mode, certain escapes are recognized:\n \ +\\a - 0x07 alert\n \ +\\b - 0x08 backspace\n \ +\\f - 0x0C form feed\n \ +\\n - 0x0A new line\n \ +\\r - 0x0D carriage return\n \ +\\v - 0x0B vertical tab\n \ +\\\\ - literal backslash\n \ +\\num - the character whose ASCII code is NUM (octal).\n \ +of <bin|oct|hex|ascii-raw|ascii-cooked> - data output format\n \ +The difference between ascii-raw and ascii-cooked is that cooked\n \ +mode converts non-printing characters to hexidecimal escape codes.\n \ +regsize <value> - the default register size in bytes\n \ +baud <value> - the speed of the port\n \ +echo <on|off> - whether or not terminal echo is on\n \ +mode <active|passive>\n \ +Active mode is default. In passive mode, more space is devoted\n \ +to the output window.\n" }, + + /* out */ + { "out", out, "\\m", + "write data out to the serial port", + "out <data>\n \ +write data with the input format controlled by <set if> to the\n \ +serial port\n \ +see also <set> for a summary of input formats\n" }, + + /* quit */ + { "quit", quit, "", + "quit the program", + "quit\n \ +quit the program\n" }, + + /* capture */ + { "capture", capture, "register{\\m},file{\\f}", + "capture data from the serial port", + "capture <file|register> [file_name|register_number|off]\n \ +capture data from the serial port to a file, and continue\n \ +until the program is closed or <capture off> is entered\n \ +or to a register 1-64, and only capture <set regsize> bytes\n" }, + + /* open */ + { "open", open_p, "\\f", + "open a serial device", + "open <serial port>\n \ +open a serial connection with <serial port>" }, + + /* close */ + { "close", close_p, "", + "close a serial device", + "close\n \ +close the currently open serial connection\n" }, + + /* clear */ + { "clear", clear_scr, "", + "clear the screen", + "clear\n \ +clear the screen\n" }, + + /* print */ + { "print", print, "register{\\m{\\m}},file{\\f{\\m}}", + "print contents of a register or file", + "print <file|register> [file name|register number] [number of bytes]\n \ +print the contents of a file or a register\n \ +if n_bytes is specified print that many bytes\n" }, + + /* alias */ + { "alias", make_alias, "\\m{\\c}", + "specify another name to use for a command", + "alias <new name> <command name>\n \ +assign an alias to <command name>\n" }, + + /* unalias */ + { "unalias", unalias, "\\m", + "remove an alias", + "unalias <alias>\n \ +remove an alias from the alias list.\n" }, + + /* the end */ + { NULL, NULL, NULL, NULL, NULL } +}; diff --git a/tap/commands/Makefile b/tap/commands/Makefile new file mode 100755 index 0000000..e265a19 --- /dev/null +++ b/tap/commands/Makefile @@ -0,0 +1,18 @@ +# makefile for commands + +CC = gcc +CFLAGS = -Wall -g -O2 -I.. + +SRCFILES = serial.c set.c user.c translate.c alias.c +OBJFILES = $(SRCFILES:%.c=%.o) +INCLUDEFILES = commands.h + +.PHONY : all clean + +all : commands.o + +commands.o : $(OBJFILES) $(INCLUDEFILES) + ld -r -o commands.o $(OBJFILES) + +clean : + rm -f commands.o $(OBJFILES) *~ core diff --git a/tap/commands/alias.c b/tap/commands/alias.c new file mode 100755 index 0000000..55d82ea --- /dev/null +++ b/tap/commands/alias.c @@ -0,0 +1,185 @@ +/* commands/alias.c + * Implementation of the alias command. + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of Tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "commands.h" + +struct alias_ { + + /* the actual command name */ + char *command; + + /* the alias for the command */ + char *alias; + + /* the alias list is linked */ + struct alias_ *next; +}; + + +/* the alias list -- these are stored alphabetically + * as determined by strcmp () + */ +struct alias_ *alias_list = NULL; + + +/* If NAME is on the alias list, return the command to which it + * was aliased, otherwise just return NAME. + */ +char * +resolve_alias (char *name) +{ + char *copy; + struct alias_ *current = alias_list; + + while (current != NULL) + { + if (!strcmp (name, current->alias)) + { + copy = xmalloc (strlen (current->command) + 1); + strcpy (copy, current->command); + + return (copy); + } + + current = current->next; + } + + /* NAME isn't on the list */ + return (name); +} + +/* This command has three forms: + * alias + * will print all of the aliases + * alias foo + * will print what foo is the alias for + * alias foo bar + * will asign the name foo to the command bar + */ +int +make_alias (char *line) +{ + char *alias, *command; + int len, next; + struct alias_ *current = alias_list, *prev = NULL; + + alias = get_token (line, &next, &len); + command = get_token_only (line + next); + + if (alias[0] == 0 && command[0] == 0) + { /* first form */ + page_init (); + + while (current != NULL) + { + wprintw (input_window, "%s -> %s\n", + current->alias, current->command); + current = current->next; + + if (page_line_printed ()) break; + } + } + else if (command[0] == 0) + { /* second form */ + command = resolve_alias (alias); + + if (strcmp (alias, command)) + { /* if alias != command */ + wprintw (input_window, "%s -> %s\n", alias, command); + wrefresh (input_window); + } + } + else + { + /* search for the insertion point */ + for (;;) + { + /* pointing at the end of the list */ + if (current == NULL) break; + + /* pointing to the insertion point */ + if (strcmp (current->alias, alias) > 0) break; + + /* the alias already exists */ + if (strcmp (current->alias, alias) == 0) + { + inp_error ("alias already exists"); + return (0); + } + + prev = current; + current = current->next; + } + + if (prev != NULL) + { + prev->next = xmalloc (sizeof (struct alias_)); + prev->next->next = current; + prev->next->alias = alias; + prev->next->command = command; + } + else + { /* at the start of the list */ + alias_list = xmalloc (sizeof (struct alias_)); + alias_list->next = current; + alias_list->alias = alias; + alias_list->command = command; + } + } + + return (0); +} + +/* Remove an alias from the alias list. */ +int +unalias (char *line) +{ + char *alias; + struct alias_ *current = alias_list, *prev = NULL; + + alias = get_token_only (line); + + while (current != NULL) + { + if (!strcmp (alias, current->alias)) + { + if (prev == NULL) alias_list = current->next; + else prev->next = current->next; + + xfree (current->alias); + xfree (current->command); + xfree (current); + + return (0); + } + + prev = current; + current = current->next; + } + + inp_error ("alias not on list"); + + return (0); +} diff --git a/tap/commands/commands.h b/tap/commands/commands.h new file mode 100755 index 0000000..653833d --- /dev/null +++ b/tap/commands/commands.h @@ -0,0 +1,30 @@ +/* commands.h + * This is the main header file for the Commands modules of Tap. Which + * contains all of the user level commmands. + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of Tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "tap.h" + +/* needed by serial.c */ +#include <termios.h> diff --git a/tap/commands/serial.c b/tap/commands/serial.c new file mode 100755 index 0000000..15aaf3e --- /dev/null +++ b/tap/commands/serial.c @@ -0,0 +1,110 @@ +/* serial.c + * functions specifically for the serial port + */ + +#include "commands.h" + +/* global variables */ + +/* file identifier for the serial port */ + +int serial_port_fd; + +/* incoming serial data */ + +char *serial_buffer = NULL; + +/* the size of the serial buffer */ +int serial_buffer_size = DEFAULT_MAXIMUM_ONE_TIME_SERIAL_DATA; + +/* Sends everything on the command line out to the serial port + * (if one is open) after any necessary translation. + */ +int +out (char *line) +{ + char *outb = NULL; + + if (port == NULL) + { + inp_error ("no serial port open"); + return (0); + } + + switch (input_format) + { + case BIN: + outb = translate_from_bin (line); + break; + case OCT: + outb = translate_from_oct (line); + break; + case HEX: + outb = translate_from_hex (line); + break; + case ASCII_RAW: + outb = translate_from_ascii_raw (line); + break; + case ASCII_COOKED: + outb = translate_from_ascii_cooked (line); + break; + } + + if (outb == NULL) return (0); + + /* write the whole line at once */ + write (serial_port_fd, outb, strlen (outb)); + + xfree (outb); + + return (0); +} + +/* This opens a serial port for reading/writing. */ +int +open_p (char *line) +{ + char *n_port; + struct termios tio; + + if (port != NULL) { + inp_error("serial port already open"); + return(0); + } + + n_port = get_token_only (line); + + serial_port_fd = open (n_port, O_RDWR | O_NONBLOCK | O_NOCTTY); + if (serial_port_fd == -1) + { + inp_error ("unable to open serial device"); + return (0); + } + + /* set to non-canonical and turn off echo */ + tcgetattr (serial_port_fd, &tio); + tio.c_lflag &= ~ (ICANON | ECHO); + tcflush (serial_port_fd, TCIFLUSH); + tcsetattr (serial_port_fd, TCSANOW, &tio); + + port = xmalloc (strlen (n_port)+1); + strcpy (port, n_port); + + /* make sure that the serial buffer exists */ + if (serial_buffer == NULL) serial_buffer = xmalloc (serial_buffer_size); + + return (0); +} + +/* This closes a serial port. */ +int +close_p (char *line) +{ + if (port == NULL) return (0); + + close (serial_port_fd); + xfree (port); + port = NULL; + + return (0); +} diff --git a/tap/commands/set.c b/tap/commands/set.c new file mode 100755 index 0000000..e4a3516 --- /dev/null +++ b/tap/commands/set.c @@ -0,0 +1,315 @@ +/* set.c + * set () and its helper functions + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of Tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "commands.h" + +/* control of active or passive mode */ + +enum winmode { ACTIVE, PASSIVE } wmode = ACTIVE; + +/* the array of valid baud rates */ + +struct baud_rates_ { + speed_t rate; + long speed; +} baud_rates[] = { + { 0, -1 }, + { B0, 0, }, /* this actually is not allowec */ + { B50, 50 }, + { B75, 75 }, + { B110, 110 }, + { B134, 134 }, + { B150, 150 }, + { B200, 200 }, + { B300, 300 }, + { B600, 600 }, + { B1200, 1200 }, + { B1800, 1800 }, + { B2400, 2400 }, + { B4800, 4800 }, + { B9600, 9600 }, + { B19200, 19200 }, + { B38400, 38400 }, + { B57600, 57600 }, + { B115200, 115200 }, + { B230400, 230400 }, + { B460800, 460800 }, + { B500000, 500000 }, + { B576000, 576000 }, + { B921600, 921600 }, + { B1000000, 1000000 }, + { B1152000, 1152000 }, + { B1500000, 1500000 }, + { B2000000, 2000000 }, + { B2500000, 2500000 }, + { B3000000, 3000000 }, + { B3500000, 3500000 }, + { B4000000, 4000000 }, + { B4000000, 4000000 }, + { 0, -1 } +}; + +/* This tests the validity of a baud rate and + * returns an index into the baud_rates array. + */ +int +valid_baud_rate (long rate) +{ + int index; + + for (index = 1; baud_rates[index].speed != -1; index++) + if (baud_rates[index].speed == rate) return (index); + + /* invalid baud rate */ + return (0); +} + +/* This converts an internal representation of the baud rate (B??) + * into an actual number which can be printed out. + */ +long +get_baud_rate (speed_t rate) +{ + struct baud_rates_ *ptr = baud_rates; + + while ((++ptr)->speed != -1) + if (ptr->rate == rate) return (ptr->speed); + + /* this should never be reached */ + return (0); +} + +/* These functions change modes. */ + +void +switch_to_active_mode (void) +{ + if (wmode == ACTIVE) return; + + wmode = ACTIVE; + ui_expand_input_window (); +} + +void +switch_to_passive_mode (void) +{ + if (wmode == PASSIVE) return; + + wmode = PASSIVE; + ui_shrink_input_window (); +} + +/* The set command itself */ +int +set (char *line) +{ + char *tok, *val; + int nxt = 0, cmd_len, pr_info = 0; + + if (strlen (line) == 0) + { + inp_error ("nothing specified"); + return (0); + } + + tok = get_token (line, &nxt, &cmd_len); + val = get_token (line+nxt, &nxt, &cmd_len); + + if (strlen (val) == 0) pr_info = 1; + + if (!strcmp (tok, "if") || !strcmp (tok, "of")) + { + enum io_format tformat = (!strcmp (tok, "if")) + ? input_format : output_format; + + if (pr_info) + { + switch (tformat) + { + case BIN: + val = "bin"; + break; + case OCT: + val = "oct"; + break; + case HEX: + val = "hex"; + break; + case ASCII_RAW: + val = "ascii-raw"; + break; + case ASCII_COOKED: + val = "ascii-cooked"; + break; + } + } + else + { + if (!strcmp (val, "bin")) + tformat = BIN; + else if (!strcmp (val, "oct")) + tformat = OCT; + else if (!strcmp (val, "hex")) + tformat = HEX; + else if (!strcmp (val, "ascii-raw")) + tformat = ASCII_RAW; + else if (!strcmp (val, "ascii-cooked")) + tformat = ASCII_COOKED; + else + { + inp_error ("invalid value"); + return (0); + } + + if (!strcmp (tok, "if")) + input_format = tformat; + else + output_format = tformat; + } + } + else if (!strcmp (tok, "regsize")) + { + if (pr_info) + { + val = xmalloc (11); + sprintf (val, "%d", register_size); + } + else + { + long tval; + + tval = strtol (val, NULL, 0); + + if (tval < 1) + inp_error ("invalid value"); + else + register_size = tval; + } + } + else if (!strcmp (tok, "echo")) + { + struct termios tio; + + if (port == NULL) + { + inp_error ("no serial port open"); + return (0); + } + + tcdrain (serial_port_fd); + tcgetattr (serial_port_fd, &tio); + + if (val[0] == 0) + { + pr_info = 1; + val = (tio.c_lflag & ECHO) ? "on" : "off"; + } + else + { + if (!strcmp (val, "on")) + tio.c_lflag |= ECHO; + else if (!strcmp (val, "off")) + tio.c_lflag &= ~ECHO; + else + { + inp_error ("echo can only be on or off"); + return (0); + } + tcsetattr (serial_port_fd, TCSANOW, &tio); + } + } + else if (!strcmp (tok, "baud")) + { + speed_t baud_rate; + struct termios tio; + long rate; + + if (port == NULL) + { + inp_error ("no serial port open"); + return (0); + } + + tcdrain (serial_port_fd); + tcgetattr (serial_port_fd, &tio); + + if (val[0] == 0) + { + xfree (val); + val = xmalloc (8); /* largest baud rate is 4000000 */ + + baud_rate = cfgetispeed (&tio); + rate = get_baud_rate (baud_rate); + if (rate != 134) /* the rate is actually 134.5 */ + sprintf (val, "%ld", rate); + else + sprintf (val, "%4.1f", 134.5); + } + else + { + baud_rate = baud_rates[valid_baud_rate (strtol (val, NULL, 0))].rate; + + if (!baud_rate || baud_rate == B0) + { + inp_error ("invalid baud rate"); + return (0); + } + + cfsetispeed (&tio, baud_rate); + cfsetospeed (&tio, baud_rate); + tcsetattr (serial_port_fd, TCSANOW, &tio); + } + } + else if (!strcmp (tok, "mode")) + { + if (val[0] == 0) + { + pr_info = 1; + val = (wmode == ACTIVE) ? "active" : "passive"; + } + else if (!strcmp (val, "active")) + switch_to_active_mode (); + else if (!strcmp (val, "passive")) + switch_to_passive_mode (); + else + { + inp_error ("unknown mode"); + return (0); + } + } + else + { + inp_error ("unknown property"); + return (0); + } + + if (pr_info) + wprintw (input_window, "%s = %s\n", tok, val); + + xfree (tok); + if (!pr_info) xfree (val); + + return (0); +} diff --git a/tap/commands/translate.c b/tap/commands/translate.c new file mode 100755 index 0000000..34dc993 --- /dev/null +++ b/tap/commands/translate.c @@ -0,0 +1,431 @@ +/* translate.c + * functions which translate a string from one format into another + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of Tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* !!! WARNING !!! + * The contents of this file should not be viewed less than an + * hour after eating or in the presence of small children. + */ + +#include "commands.h" + +#define TOUP(ch) (isalpha (ch) ? toupper (ch) : (ch)) + +/* Check that a user-entered string is valid for the given mode. If + * STRING has characters which aren't in VALID, returns 1. + */ +int +check_validity (char *string, char *valid) +{ + char *tptr; + + string--; + while (*(++string)) + { + tptr = valid - 1; + while (*(++tptr)) if (*string == *tptr) goto NO_ERROR; + return (1); + NO_ERROR: + ; + } + + return (0); +} + +/* translate_from_?? translates user input from ?? into usable format + * to be written out of the serial port. + */ + +char * +translate_from_bin (char *input) +{ + unsigned char *output; + long byte, bit, length = strlen (input)/8, diff; + + if (check_validity (input, "01")) + { + inp_error ("invalid_character"); + return (NULL); + } + + output = xmalloc (length + 2); + memset (output, 0, length + 2); + + for (byte = 0; byte < length; byte++) + { + for (bit = 0; bit < 8; bit++) + { + output[byte] ^= + ((unsigned char) ((input[byte*8 + bit] == '1') ? 1 : 0)) << (7 - bit); + } + } + + diff = strlen (input) - length*8; + + if (diff) + { + for (bit = 0; bit < diff; bit++) + { + output[byte] ^= + ((unsigned char)((input[byte*8 + bit] == '1') ? 1 : 0)) << (7 - bit); + } + } + + return (output); +} + +char * +translate_from_oct (char *input) +{ + unsigned char *output = NULL; + int length = (strlen (input) * 3) / 8 + 1, k; + + if (check_validity (input, "01234567")) + { + inp_error ("invalid_character"); + return (NULL); + } + + output = xmalloc (length + 1); + memset (output, 0, length + 1); + + for (k = 0; k < strlen (input) / 8; k++) + { + output[k*3] = ((input[k*8] << 5) & 0xE0) + | ((input[k*8+1] << 2) & 0x1C) + | ((input[k*8+2] >> 1) & 0x03); + output[k*3+1] = ((input[k*8+2] << 7) & 0x80) + | ((input[k*8+3] << 4) & 0x70) + | ((input[k*8+4] << 1) & 0x0E) + | ((input[k*8+5] >> 2) & 0x01); + output[k*3+2] = ((input[k*8+5] << 6) & 0xC0) + | ((input[k*8+6] << 3) & 0x38) + | (input[k+8+7] & 0x07); + } + + /* take care of trailers */ + switch (strlen (input) % 8) + { + case 7: + output[k*3+2] = (input[k*8+6] << 3) & 0x38; + case 6: + output[k*3+2] |= (input[k*8+5] << 6) & 0xC0; + output[k*3+1] = (input[k*8+5] >> 2) & 0x01; + case 5: + output[k*3+1] |= (input[k*8+4] << 1) & 0x0E; + case 4: + output[k*3+1] |= (input[k*8+3] << 4) & 0x70; + case 3: + output[k*3+1] |= (input[k*8+2] << 7) & 0x80; + output[k*3] = (input[k*8+2] >> 1) & 0x03; + case 2: + output[k*3] |= (input[k*8+1] << 2) & 0x1C; + case 1: + output[k*3] |= (input[k*8] << 5) & 0xE0; + default: + } + + return (output); +} + +char * +translate_from_hex (char *input) +{ + unsigned char *output = NULL; + int length = strlen (input) / 2, k; + char translate_table[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* placeholders */ + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F + }; + + if (check_validity (input, "0123456789ABCDEFabcdef")) + { + inp_error ("invalid_character"); + return (NULL); + } + + output = xmalloc (length + 1); + memset (output, 0, length + 1); + + for (k = 0; k < length; k++) + { + output[k] = (translate_table[TOUP (input[k*2]) - '0'] << 4) + | translate_table[TOUP (input[k*2+1]) - '0']; + } + + return (output); +} + +char * +translate_from_ascii_raw (char *input) +{ + char *output; + + output = xmalloc (strlen (input) + 1); + memcpy (output, input, strlen (input) + 1); + + return (output); +} + +char * +translate_from_ascii_cooked (char *input) +{ + char *output; + int amt = 0; + + output = xmalloc (strlen (input) + 1); + memset (output, 0, strlen (input) + 1); + + for (--input; * (++input); amt++) + { + if (*input == '\\') + { + switch (* (++input)) + { + case 'a': + output[amt] = 0x07; + break; + case 'b': + output[amt] = 0x08; + break; + case 'f': + output[amt] = 0x0C; + break; + case 'n': + output[amt] = 0x0A; + break; + case 'r': + output[amt] = 0x0D; + break; + case 'v': + output[amt] = 0x0B; + break; + case '\\': + output[amt] = '\\'; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + unsigned char val = 0; + + val = *input & 0x07; + input++; + if (*input <= '7' && *input >= '0') + { + val <<= 3; + val |= *input & 0x07; + + input++; + if (*input <= '7' && *input >= '0') + { + val <<= 3; + val |= *input & 0x07; + } + else + input--; + } + else + input--; + + output[amt] = val; + } + break; + default: + inp_error ("invalid escape code"); + xfree (output); + return (NULL); + } + } + else + { + output[amt] = *input; + } + } + + return (output); +} + +/* translate_to_?? () takes input from the serial port and translates + * it into the format ?? for display in OUTPUT_WINDOW. + */ + +char * +translate_to_bin (char *input) +{ + char *output; + int current, offset; + + output = xmalloc (8 * strlen (input) + 1); + + for (current = 0; current < strlen (input); current++) + { + for (offset = 0; offset < 8; offset++) + output[8 * current + offset] = + (input[current] & (0x01 << (7 - offset))) ? '1' : '0'; + } + + output[8 * strlen (input)] = '\0'; + + return (output); +} + +char * +translate_to_oct (char *input) +{ + char *output = NULL; + int length = (strlen (input) * 8) / 3 + 1, k; + char translate_table[] = { + '0', '1', '2', '3', '4', '5', '6', '7' + }; + + output = xmalloc (length + 1); + + memset (output, 0, length + 1); + + /* convert 3 bytes of input at a time */ + for (k = 0; k < strlen (input) / 3; k++) + { + output[k*8] = translate_table[ (input[k*3] & 0xE0) >> 5]; + output[k*8+1] = translate_table[ (input[k*3] & 0x1C) >> 2]; + output[k*8+2] = translate_table[ ((input[k*3] & 0x03) << 1) | ((input[k*3+1] & 0x80) >> 7)]; + output[k*8+3] = translate_table[ (input[k*3+1] & 0x70) >> 4]; + output[k*8+4] = translate_table[ (input[k*3+1] & 0x0E) >> 1]; + output[k*8+5] = translate_table[ ((input[k*3+1] & 0x01) << 2) | ((input[k*3+2] & 0xC0) >> 6)]; + output[k*8+6] = translate_table[ (input[k*3+2] & 0x38) >> 3]; + output[k*8+7] = translate_table[input[k*3+2] & 0x07]; + } + + /* handle any trailers -- ugly, yeah, but it works... */ + switch (strlen (input) % 3) + { + case 2: /* 6 additional */ + output[k*8+3] = translate_table[ (input[k*3+1] & 0x70) >> 4]; + output[k*8+4] = translate_table[ (input[k*3+1] & 0x0E) >> 1]; + output[k*8+5] = translate_table[ ((input[k*3+1] & 0x01) << 2) + | ((input[k*3+2] & 0xC0) >> 6)]; + /* fall through */ + case 1: /* 3 additional */ + output[k*8] = translate_table[ (input[k*3] & 0xE0) >> 5]; + output[k*8+1] = translate_table[ (input[k*3] & 0x1C) >> 2]; + output[k*8+2] = translate_table[ ((input[k*3] & 0x03) << 1) + | ((input[k*3+1] & 0x80) >> 7)]; + } + + return (output); +} + +char * +translate_to_hex (char *input) +{ + char *output; + char hex_table[] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + long k, length = strlen (input)*2; + + output = xmalloc (length + 1); + memset (output, 0, length + 1); + + for (k = 0; k < length/2; k++) + { + output[2 * k] = hex_table[ (input[k] >> 4) & 0x0F]; + output[2 * k + 1] = hex_table[input[k] & 0x0F]; + } + + return (output); +} + +char * +translate_to_ascii_raw (char *input) +{ + char *output; + + output = xmalloc (strlen (input) + 1); + memcpy (output, input, strlen (input) + 1); + + return (output); +} + +/* This is a helper function for translate_to_ascii_cooked(). */ +void +add_character (char **pstring, char ch) +{ + static int buffer_size = 0, chars_in_buffer = 0; + + if (pstring == NULL) + { + buffer_size = chars_in_buffer = 0; + return; + } + + if (chars_in_buffer == buffer_size) + { + char *temp; + + buffer_size += GROW_SIZE; + temp = xrealloc (*pstring, buffer_size); + + *pstring = temp; + } + + (*pstring)[chars_in_buffer] = ch; + chars_in_buffer++; +} + +#define not_escaped(ch) (isalnum (ch) || ispunct (ch) || isspace (ch)) + +char * +translate_to_ascii_cooked (char *input) +{ + char *output, hex_table[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + /* initialize */ + add_character (NULL, 0); + output = xmalloc (0); + + for (--input; *(++input);) + { + if (not_escaped (*input)) add_character (&output, *input); + else + { + add_character (&output, '<'); + add_character (&output, hex_table[(*input >> 4) & 0x0F]); + add_character (&output, hex_table[*input & 0x0F]); + add_character (&output, '>'); + } + } + + add_character (&output, '\0'); + + return (output); +} diff --git a/tap/commands/user.c b/tap/commands/user.c new file mode 100755 index 0000000..14a6969 --- /dev/null +++ b/tap/commands/user.c @@ -0,0 +1,378 @@ +/* commands/user.c + * User support functions + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of Tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "commands.h" + +/* the current register number */ + +int capture_register = 0; + +/* the file to capture to */ + +FILE *capture_fid = NULL; + +/* the name of that file */ + +char *capture_fname = NULL; + + +/* Get help on a command. */ +int +help (char *line) +{ + char *cmd; + int current = 0; + + page_init (); + + if (strlen (line) == 0) + { + /* print help on all of the topics */ + while (commands[current].name != NULL) + { + wprintw (input_window, "%-*s: %s\n", MAXIMUM_COMMAND_SIZE, + commands[current].name, commands[current].blurb_help); + current++; + + if (page_line_printed ()) break; + } + } + else + { + /* help on a selected topic */ + cmd = get_token_only (line); + if (cmd == NULL) return (0); + + while (commands[current].name != NULL) + { + if (!strcmp (commands[current].name, cmd)) + { + wprintstr (input_window, commands[current].full_help); + return (0); + } + current++; + } + + inp_error ("unknown command"); + } + + return (0); +} + +/* Quit the program. */ +int +quit (char *line) +{ + return (1); +} + +/* Write incoming data to a register without translating. */ +void +write_to_capture_register (int how_much) +{ + if (capture_register == 0) return; + + if (how_much > registers[capture_register].size + - registers[capture_register].offset) + how_much = registers[capture_register].size + - registers[capture_register].offset; + + memcpy (registers[capture_register].data + + registers[capture_register].offset, serial_buffer, how_much); + registers[capture_register].offset += how_much; + + /* if the register is full, stop capturing */ + if (registers[capture_register].offset == registers[capture_register].size) + capture_register = 0; +} + +/* Write incoming data to a file without translating. */ +void +write_to_capture_file (int how_much) +{ + if (capture_fid == NULL) return; + + fwrite (serial_buffer, how_much, sizeof (char), capture_fid); +} + +/* This function turns capturing on and off for registers and + * files. + */ +int +capture (char *line) +{ + char *where, *name; + int next, len; + + where = get_token (line, &next, &len); + name = get_token_only (line + next); + + if (!strcmp (where, "file")) + { + if (name[0] == 0) + { + if (capture_fname != NULL) + wprintw (input_window, "%s\n", capture_fname); + return (0); + } + + if (!strcmp (name, "off")) + { + if (capture_fid != NULL) + { + fclose (capture_fid); + capture_fid = NULL; + + xfree (capture_fname); + capture_fname = NULL; + } + return (0); + } + + if (capture_fid != NULL) + { + inp_error ("already capturing to a file"); + return (0); + } + + name = tilde_expand (name); + + capture_fid = fopen (name, "a+b"); + + if (capture_fid == NULL) + { + xfree (name); + + inp_error ("unable to open file for capture"); + return (0); + } + + capture_fname = name; + + } + else if (!strcmp (where, "register")) + { + if (name[0] == 0) + { + if (capture_register != 0) + wprintw (input_window, "%d\n", capture_register); + return (0); + } + + if (!strcmp (name, "off")) + { + registers[capture_register].size + = registers[capture_register].offset; + registers[capture_register].offset = 0; + + capture_register = 0; + return (0); + } + + /* if one is already in use, close it */ + if (capture_register != 0) + { + registers[capture_register].size + = registers[capture_register].offset; + registers[capture_register].offset = 0; + } + + capture_register = strtol (name, NULL, 0); + + if (capture_register < 1 || capture_register > MAX_REGISTERS) + { + inp_error ("invalid register number"); + capture_register = 0; + return (0); + } + + if (registers[capture_register].data != (void *)0) + xfree (registers[capture_register].data); + + registers[capture_register].size = register_size; + registers[capture_register].offset = 0; + registers[capture_register].data = xmalloc (register_size); + + } + else + inp_error ("invalid capture location"); + + return (0); +} + +/* This function prints the contents of a file with + * translation. Use is made of the serial buffer. + */ +void +print_file (FILE *fid, long data_printed) +{ + long amount_read = 0; + long amount_to_read = serial_buffer_size; + + if (serial_buffer == NULL) serial_buffer = xmalloc (serial_buffer_size); + + if (data_printed != -1) + amount_to_read = MIN (serial_buffer_size, data_printed); + + for (;;) + { + amount_read = fread (serial_buffer, amount_to_read, sizeof (char), fid); + + if (amount_read == 0) break; + + write_to_output_window (amount_to_read); + + if (data_printed != -1) + { + data_printed -= amount_to_read; + amount_to_read = MIN (serial_buffer_size, data_printed); + + if (amount_to_read == 0) break; + } + } +} + +/* This function prints the contents of a file + * or a register. + */ +int +print (char *line) +{ + int next, len, pr_register; + long amount, printed = 0, amount_to_print; + char *what, *where, *size; + FILE *fid; + + what = get_token (line, &next, &len); + line += next; + where = get_token (line, &next, &len); + size = get_token_only (line+next); + + if (size[0] == 0) + amount = -1; + else + { + amount = strtol (size, NULL, 0); + if (amount == 0) + amount = -1; + } + + if (!strcmp (what, "file")) + { + if (where[0] == 0) + { + if (capture_fname == NULL) + inp_error ("no open capture file"); + else + { + rewind (capture_fid); + print_file (capture_fid, -1); + fseek (capture_fid, 0, SEEK_END); + } + + return (0); + } + + if (capture_fname != NULL) + { + if (!strcmp (where, "current") || !strcmp (where, capture_fname)) + { + rewind (capture_fid); + print_file (capture_fid, amount); + fseek (capture_fid, 0, SEEK_END); + + return (0); + } + } + + fid = fopen (where, "rb"); + if (fid == NULL) + { + inp_error ("unable to open file"); + return (0); + } + + print_file (fid, amount); + + fclose (fid); + + } + else if (!strcmp (what, "register")) + { + if (where[0] == 0) + { + if (capture_register == 0) + { + inp_error ("no register specified"); + return (0); + } + pr_register = capture_register; + } + else + { + pr_register = strtol (where, NULL, 0); + + if (pr_register < 1 || pr_register > MAX_REGISTERS) + { + inp_error ("invalid register"); + return (0); + } + } + + /* if the register is empty */ + if (registers[pr_register].data == (void *)0) return (0); + + /* print until there is no more data */ + if (amount == -1) amount = registers[pr_register].size; + + for (;;) + { + amount_to_print = MIN (serial_buffer_size, amount); + + memcpy (serial_buffer, registers[pr_register].data+printed, + amount_to_print); + + write_to_output_window (amount_to_print); + + printed += amount_to_print; + if (printed >= amount) break; + } + } + else + inp_error ("nothing specified"); + + return (0); +} + +/* Clear the input window. */ +int +clear_scr (char *line) +{ + werase (input_window); + wmove (input_window, 0, 0); + wrefresh (input_window); + + return (0); +} diff --git a/tap/memmgr.c b/tap/memmgr.c new file mode 100755 index 0000000..04b0e49 --- /dev/null +++ b/tap/memmgr.c @@ -0,0 +1,257 @@ +/* memmgr.c + * This is the memory manager for Tap. The visible functions are + * xmalloc - allocate a block of memory + * xrealloc - change the size of a block of memory + * xfree - free a block of memory + * free_alloc_list - called at exit to free all memory + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of Tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "tap.h" + +/* minimum size block to allocate */ + +#define MIN_ALLOC 8 + + +/* the alloc / free list */ + +struct memory_block *alloc_list = NULL; + +/* the total available slots (the size of the alloc list) */ + +long number_of_blocks = 0; + +/* the number of blocks that have been filled */ + +long block_pointers_allocated = 0; + + +/* This is called if malloc or realloc return NULL + * + * Give the user a choice to continue (and possibly + * close other memory-intensive applications) or to + * just exit. If this gets called more than + * MAX_TIMES_OUT_OF_MEMORY then just abort. + */ +void +out_of_memory (void) +{ + char key; + static int times_out_of_memory = 0; + + if (times_out_of_memory > MAX_TIMES_OUT_OF_MEMORY) + { + fprintf (stderr, "%s: virtual memory exhausted\n", pname); + abort (); + } + + waddstr (input_window, "!! out of virtual memory !!\n\ +press <c> to attempt to continue (and possibly close other applications)\n\ +or any other key to abort: "); + + /* don't use sgetch () here */ + key = wgetch (input_window); + + if (tolower (key) == 'c') + { + times_out_of_memory++; + longjmp (start_of_input_loop, 1); + } + + abort (); +} + +/* This is passed a pointer to memory (the same as passed to xrealloc + * or xfree). If it isn't found on the alloc list (using the mark) + * then abort is called. + */ +long +get_block_index (void *mem) +{ + long mark; + + /* in other words, mark = (long) mem[-MARK_SIZE] */ + mark = ((long)* ((long *) ((mem) - MARK_SIZE))); + + /* just check that it's somewhere on the list */ + if (mark < block_pointers_allocated) return (mark); + + /* this should never be reached */ + fprintf (stderr, + "\n\r%s: tried to free or realloc block not on alloc list\n\r\ +block index %ld\n\r", pname, mark); + abort (); + +} + +/* Search for an unused block of memory with + * size >= min_size. If nothing is found return -1 otherwise + * return the index of a suitable block. + */ +long +find_free_block (size_t min_size) +{ + long index; + + /* scan through the whole list */ + for (index = 0; index < block_pointers_allocated; index++) + { + if (alloc_list[index].in_use == FREE) + { + if (alloc_list[index].size >= min_size) return (index); /* got one */ + } + } + + /* nothing */ + return (-1); +} + +/* This function acts just like malloc except for using the + * alloc list to keep track of used and unused memory and + * checking for errors. + */ +void * +xmalloc (size_t how_much) +{ + long block_index; + + /* try to find a suitable block */ + block_index = find_free_block (how_much); + + if (block_index == -1) + { /* nothing found */ + + /* if the alloc list needs to be expanded */ + if (block_pointers_allocated == number_of_blocks) + { + struct memory_block *temp; + + temp = realloc (alloc_list, sizeof (struct memory_block) + * (number_of_blocks + GROW_SIZE)); + + if (temp == NULL) out_of_memory (); + + alloc_list = temp; + number_of_blocks += GROW_SIZE; + } + + /* now allocate a new block */ + + block_index = block_pointers_allocated; + + block_pointers_allocated++; + + alloc_list[block_index].memory = malloc ((how_much < MIN_ALLOC + ? MIN_ALLOC : how_much) + + MARK_SIZE); + if (alloc_list[block_index].memory == NULL) out_of_memory (); + + alloc_list[block_index].size = (how_much < MIN_ALLOC) + ? MIN_ALLOC : how_much; + + /* mark the block with the index */ + * ((long *)alloc_list[block_index].memory) = block_index; + } + + alloc_list[block_index].in_use = USED; + alloc_list[block_index].size_used = how_much; + + return (alloc_list[block_index].memory + MARK_SIZE); +} + +/* This function is just like realloc except that it uses the + * alloc list and checks the validity of realloc'ed memory. + */ +void * +xrealloc (void *mem, size_t how_much) +{ + long index, orig_index; + void *new; + + orig_index = get_block_index (mem); + + /* first check if the block is already big enough */ + if (alloc_list[orig_index].size >= how_much) + { + alloc_list[orig_index].size_used = how_much; + return (mem); + } + + /* not big enough - try to find another one */ + + /* scan the alloc list */ + for (index = 0; index < block_pointers_allocated; index++) + { + if (alloc_list[index].in_use == FREE) + { + if (alloc_list[index].size >= how_much) + { + /* found suitable block */ + memcpy (alloc_list[index].memory + MARK_SIZE, + alloc_list[orig_index].memory + MARK_SIZE, + alloc_list[orig_index].size_used); + + alloc_list[index].in_use = USED; + alloc_list[orig_index].in_use = FREE; + + return (alloc_list[index].memory + MARK_SIZE); + } + } + } + + /* have to actually realloc */ + new = realloc (alloc_list[orig_index].memory, how_much + MARK_SIZE); + + if (new == NULL) out_of_memory (); + + alloc_list[orig_index].memory = new; + + alloc_list[orig_index].size = how_much; + alloc_list[orig_index].size_used = how_much; + + return (alloc_list[orig_index].memory + MARK_SIZE); +} + +/* This function acts just like free except that it uses the alloc list. */ +void +xfree (void *mem) +{ + long index; + + index = get_block_index (mem); + + alloc_list[index].in_use = FREE; +} + +/* This is called at exit and it frees every block of memory allocated. */ +void +free_alloc_list (void) +{ + long index; + + for (index = 0; index < block_pointers_allocated; index++) + free (alloc_list[index].memory); + + free (alloc_list); +} diff --git a/tap/readline/Makefile b/tap/readline/Makefile new file mode 100755 index 0000000..a83987c --- /dev/null +++ b/tap/readline/Makefile @@ -0,0 +1,38 @@ +# makefile for the Readline module of Tap. +# Copyright (C) 1999 Jonathan duSaint +# +# This file is part of Tap, an interactive program for reading and +# writing data to the serial port to facilitate the reverse +# engineering of communication protocols. +# +# Tap 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. +# +# Tap 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. +# +# The GNU General Public License is generally kept in a file called +# COPYING or LICENSE. If you do not have a copy of the license, write +# to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + +CC = gcc +CFLAGS = -Wall -g -O2 -I.. + +SRCFILES = history.c line.c ring.c tab.c text.c +OBJFILES = $(SRCFILES:%.c=%.o) +INCLUDEFILES = readline.h + +.PHONY : all clean + +all : readline.o + +readline.o : $(OBJFILES) $(INCLUDEFILES) + ld -r -o readline.o $(OBJFILES) + +clean : + rm -f readline.o $(OBJFILES) *~ core diff --git a/tap/readline/history.c b/tap/readline/history.c new file mode 100755 index 0000000..a833e1a --- /dev/null +++ b/tap/readline/history.c @@ -0,0 +1,244 @@ +/* history.c + * Part of the Readline module for Tap. This is a simpler + * implementation of the GNU History library. + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of Tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "readline.h" + +/* the history list itself + * The first node is not dynamically allocated. That way, no + * initialization function needs to be called. + */ + +struct history_node the_history_list = { NULL, NULL, NULL, NULL }; + + +/* pointers into the history list */ + +/* the last item in the history list */ + +struct history_node *last_history = &the_history_list; + +/* the current item when the list is being traversed */ + +struct history_node *current_history = &the_history_list; + + +/* the most current line (which isn't itself on the history list) */ + +char *saved_line = NULL; + + +/* Get the next entry on the history list. If there isn't another node, + * return the saved line, or if that is the current line, return NULL. + */ +char * +next_history (void) +{ + char *line_to_return; + + /* already at the last entry */ + if (current_history == last_history) + line_to_return = NULL; + else + { + current_history = current_history->next; + + /* at the last entry, but still not on the most current line */ + if (current_history == last_history) + { + line_to_return = saved_line; + saved_line = NULL; + } + else + { + /* (re)allocate scratch area if necessary */ + if (current_history->saved_data == NULL) + { + current_history->saved_data = + xmalloc (ROUND_UP_SIZE (strlen (current_history->data))); + + strcpy (current_history->saved_data, current_history->data); + } + + line_to_return = current_history->saved_data; + } + } + + return (line_to_return); +} + +/* Get the previous line on the history list. If the current node is + * the first one, return NULL. + */ +char * +prev_history (void) +{ + char *line_to_return; + + /* at the oldest entry already */ + if (current_history == &the_history_list) + line_to_return = NULL; + else + { + /* about to start traversing the list proper -- + * need to save the most recent line + */ + if (current_history == last_history) + saved_line = current_line; + + current_history = current_history->prev; + + /* (re)allocate scratch area if necessary */ + if (current_history->saved_data == NULL) + { + current_history->saved_data = + xmalloc (ROUND_UP_SIZE (strlen (current_history->data))); + + strcpy (current_history->saved_data, current_history->data); + } + + line_to_return = current_history->saved_data; + } + + return (line_to_return); +} + +/* Add an entry into the history list. Don't save empty lines or + * lines that are exactly the same as the line immediately above. + */ +void +add_history (char *line) +{ + /* don't save empty lines */ + if (strlen (line) == 0) return; + + /* if this is a modification of an earlier line, restore the original */ + if (current_history != last_history) current_history->saved_data = NULL; + + if (last_history->prev != NULL) + { + /* don't save copies */ + if (!strcmp (last_history->prev->data, line)) + { + current_history = last_history; + return; + } + } + + /* save the line in the list */ + last_history->data = xmalloc (ROUND_UP_SIZE (strlen (line))); + strcpy (last_history->data, line); + + /* create a new node ... */ + last_history->next = xmalloc (sizeof (struct history_node)); + + /* ... and fill it in */ + last_history->next->data = NULL; + last_history->next->saved_data = NULL; + last_history->next->prev = last_history; + last_history->next->next = NULL; + + /* update all pointers */ + last_history = last_history->next; + current_history = last_history; +} + +/* Expand a path if it contains a tilde */ +char * +tilde_expand (char *file) +{ + char *expansion, *home; + + home = getenv ("HOME"); + + if (file[0] == '~' && home != NULL) + { + expansion = xmalloc (strlen(file) + strlen(home)); + + sprintf (expansion, "%s%s", home, file + 1); + } + else + { + /* If there is no HOME or ~ is not in the string, just + * return a pointer which can be freed. + */ + expansion = xmalloc (strlen(file) + 1); + strcpy (expansion, file); + } + + return (expansion); +} + +/* save the history list to a file -- usually ~/.tap_history */ +void +save_history_to_file (char *file) +{ + char *path; + FILE *fd; + + path = tilde_expand (file); + + fd = fopen (path, "wt"); + if (fd == NULL) return; + + current_history = &the_history_list; + + while (current_history != last_history) + { + fprintf (fd, "%s\n", current_history->data); + + current_history = current_history->next; + } + + fclose (fd); + xfree (path); +} + +#define MAX_LINE_SIZE 1024 + +void +read_history_from_file (char *file) +{ + FILE *fd; + char *line, *path; + + line = xmalloc (MAX_LINE_SIZE); + + path = tilde_expand (file); + + fd = fopen (path, "rt"); + if (fd == NULL) return; + + while (fgets (line, MAX_LINE_SIZE, fd) != NULL) + { + if (line[strlen (line) - 1] == '\n') line[strlen (line) - 1] = '\0'; + + add_history (line); + } + + fclose (fd); + xfree (line); + xfree (path); +} diff --git a/tap/readline/line.c b/tap/readline/line.c new file mode 100755 index 0000000..55ac482 --- /dev/null +++ b/tap/readline/line.c @@ -0,0 +1,340 @@ +/* line.c + * Part of the Readline module for Tap. This is the main file. + * The readline module was designed in a similar spirit as the GNU + * Readline module, but to work with ncurses, and, more specifically, + * with Tap. The functionality is similar, and the bindings are the + * same as in Emacs, however it is not as complete as in GNU Readline. + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of Tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "readline.h" + +/* global variables which are important to the readline module */ + +/* prompt to use */ + +char *prompt; + +/* the line under construction */ + +char *current_line; + +/* its actual size */ + +long line_size; + +/* number of characters active */ + +long chars_in_line; + +/* where the point is */ + +long current_position; + +/* for when C-<spc> is pressed + * -1 means mark not set + */ + +long mark_start; + +/* start position of the cursor */ + +int start_y; + +/* repeat value from C-U */ + +int repeat_value = 1; + +/* the kill ring */ + +struct kill_ring *the_kill_ring = NULL; + +/* Clear the screen and reprint the current line */ +void +clear_screen (void) +{ + clear_scr (NULL); + + start_y = 0; + + wprintw (input_window, "%s%s", prompt, current_line); +} + +/* readchar () returns either an ASCII character, or a control + * code, which is a value >0xFF. All values are defined in readline.h. + */ +int +readchar (void) +{ + int ch; + + /* sgetch() is in serial/char.c */ + ch = sgetch (); + /*ch = wgetch (input_window);*/ + + if (((char)ch) == '\n') return (ECQUIT); + + if (isgraph (ch) || ch == ' ') return (ch); + + switch (ch) + { + case RCUP: + return (ECUP); + case RCDOWN: + return (ECDOWN); + case RCLEFT: + return (ECLEFT); + case RCRIGHT: + return (ECRIGHT); + case RCSTART: + return (ECSTART); + case RCEND: + return (ECEND); + case RCDEL: + case RCDELK: + return (ECDEL); + case RCERASE: + return (ECERASE); + case RCMARK: + return (ECMARK); + case RCKILLL: + return (ECKILLL); + case RCKILLR: + return (ECKILLR); + case RCYANK: + return (ECYANK); + case RCTRANS: + return (ECTRANS); + case RCCLEAR: + return (ECCLEAR); + case RCCOUNT: + return (ECCOUNT); + case RCTAB: + return (ECTAB); + case RCESC: + ch = wgetch (input_window); + + if (ch == EARROW) + { + int tch; + + tch = wgetch (input_window); + + switch (tch) + { + case AUP: + return (ECUP); + case ADOWN: + return (ECDOWN); + case ALEFT: + return (ECLEFT); + case ARIGHT: + return (ECRIGHT); + } + + ungetch (tch); + } + /* fall through if one of the arrow keys wasn't recieved */ + } + + return (ECUNKNOWN); +} + +/* If a control code was returned by readchar (), this function + * is called. + */ +void +dispatch_special (int code) +{ + switch (code) + { + case ECLEFT: + move_cursor_left (); + break; + case ECRIGHT: + move_cursor_right (); + break; + case ECSTART: + move_cursor_to_start (); + break; + case ECEND: + move_cursor_to_end (); + break; + case ECDEL: + delete_char (RIGHT); + break; + case ECERASE: + delete_char (LEFT); + break; + case ECMARK: + mark_start = current_position; + break; + case ECKILLR: + kill_region (); + break; + case ECKILLL: + kill_to_eol (); + break; + case ECYANK: + yank_text (); + break; + case ECTRANS: + transpose_chars (); + break; + case ECCOUNT: + do_count (); + break; + case ECTAB: + do_tab_completion (); + break; + case ECUP: + { + char *line; + + line = prev_history (); + + if (line == NULL) warning_beep (); + else + { + move_cursor_to_start (); + + current_line = line; + chars_in_line = strlen (line); + line_size = ROUND_UP_SIZE (chars_in_line); + + move_cursor_to_end (); + + mark_start = -1; + repeat_value = 1; + } + break; + } + case ECDOWN: + { + char *line; + + line = next_history (); + + if (line == NULL) warning_beep (); + else + { + move_cursor_to_start (); + + current_line = line; + chars_in_line = strlen (line); + line_size = ROUND_UP_SIZE (chars_in_line); + + move_cursor_to_end (); + + mark_start = -1; + repeat_value = 1; + } + + break; + } + case ECCLEAR: + { + int y_offset = input_window->_cury - start_y; + int x_pos = input_window->_curx; + + clear_screen (); + + input_window->_curx = x_pos; + input_window->_cury = y_offset; + + break; + } + default: + warning_beep (); + } +} + +/* Make sure that the screen is clear of junk after it has been + * modified (i.e. from inserting/removing a character). + */ +void +refresh_screen (void) +{ + int x, y; + + x = input_window->_curx; + y = input_window->_cury; + + input_window->_cury = start_y; + input_window->_curx = 0; + + wclrtobot (input_window); + + wprintw (input_window, "%s%s", prompt, current_line); + + input_window->_curx = x; + input_window->_cury = y; + + wrefresh (input_window); +} + +/* Given the prompt P, read a line of text in a user friendly manner. */ +char * +readline (char *p) +{ + int ch; + + /* initialize the kill ring if needed */ + if (the_kill_ring == NULL) ring_init (); + + /* initialize globals */ + prompt = p; + chars_in_line = 0; + current_position = 0; + mark_start = -1; + + start_y = input_window->_cury; + + current_line = xmalloc (GROW_SIZE); + line_size = GROW_SIZE; + + current_line[0] = 0; + + waddstr (input_window, prompt); + + while (1) + { + ch = readchar (); + + if (ch == ECQUIT) break; + + if (ch > 0xFF) + dispatch_special (ch); + else + insert_char (ch); + + refresh_screen (); + } + + add_history (current_line); + + move_cursor_to_end (); + waddch (input_window, '\n'); + + return (current_line); +} diff --git a/tap/readline/readline.h b/tap/readline/readline.h new file mode 100755 index 0000000..5a4cd69 --- /dev/null +++ b/tap/readline/readline.h @@ -0,0 +1,243 @@ +/* readline.h + * This is the header file for the Readline module for Tap. + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of Tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "tap.h" + +/* extended character codes returned by readchar() */ + +#define ECQUIT 0x100 /* \n */ + +#define ECUP 0x101 /* ^P */ +#define ECDOWN 0x102 /* ^N */ +#define ECLEFT 0x103 /* ^B */ +#define ECRIGHT 0x104 /* ^F */ +#define ECSTART 0x105 /* ^A */ +#define ECEND 0x106 /* ^E */ + +#define ECDEL 0x107 /* ^D */ +#define ECERASE 0x108 /* ^H */ + +#define ECMARK 0x109 /* ^<spc> */ +#define ECKILLR 0x10A /* ^W */ +#define ECKILLL 0x10B /* ^K */ +#define ECYANK 0x10C /* ^Y */ +#define ECTRANS 0x10D /* ^T */ +#define ECCLEAR 0x10E /* ^L */ + +#define ECCOUNT 0x10F /* ^U */ + +#define ECTAB 0x110 /* \t */ + +#define ECUNKNOWN 0xFFF /* unrecognized control code */ + + +/* raw character codes */ + +#define RCUP 0x10 +#define RCDOWN 0x0E +#define RCLEFT 0x02 +#define RCRIGHT 0x06 +#define RCSTART 0x01 +#define RCEND 0x05 + +#define RCDEL 0x04 +#define RCDELK 0x7F +#define RCERASE 0x08 + +#define RCMARK 0x00 +#define RCKILLR 0x17 +#define RCKILLL 0x0B +#define RCYANK 0x19 +#define RCTRANS 0x14 +#define RCCLEAR 0x0C + +#define RCCOUNT 0x15 + +#define RCTAB '\t' + +/* arrow key escape sequence */ + +#define RCESC 0x1B + +#define EARROW 0x4F + +#define AUP 0x41 +#define ADOWN 0x42 +#define ARIGHT 0x43 +#define ALEFT 0x44 + +/* used by do_tab_completion () when creating internal structures */ + +#define INCR_VALUE 8 + +/* the default number of entries in the kill ring */ + +#define DEFAULT_KILL_RING_SIZE 32 + +/* the minimum amount of padding to use when printing partial matches */ + +#define PADDING 2 + + +/* used for determing the actual amount of memory used by a line of text */ + +#define ROUND_UP_SIZE(nom) (((nom + 1) / GROW_SIZE) * GROW_SIZE + GROW_SIZE) + + +/* specifies to delete_char() whether to delete the + * character to the left or the right of the point + */ + +enum direction +{ + LEFT, RIGHT +}; + + +/* kill ring entries */ + +struct kill_ring +{ + long length; /* the length of TEXT */ + char *text; /* the actual text in the ring */ + + struct kill_ring *prev; /* the previous entry in the kill ring */ + struct kill_ring *next; /* the next entry in the kill ring */ +}; + +/* history list entries */ + +struct history_node +{ + char *data; /* the actual history entry */ + char *saved_data; /* temporary entry used for when an + earlier line is being edited */ + struct history_node *prev; /* the previous entry in the history list */ + struct history_node *next; /* the next entry in the history list */ +}; + + +/* global variables */ + +/* prompt to use */ + +extern char *prompt; + +/* the line under construction */ + +extern char *current_line; + +/* its actual size */ + +extern long line_size; + +/* number of characters active */ + +extern long chars_in_line; + +/* where the point is */ + +extern long current_position; + +/* for when C-<spc> is pressed */ + +extern long mark_start; + +/* start position of the cursor */ + +extern int start_y; + +/* cursor position after the prompt has been printed */ + +extern int start_x; + +/* the last repeat value -- should be 1 unless C-U has just been typed */ + +extern int repeat_value; + +/* the kill ring */ + +extern struct kill_ring *the_kill_ring; + +/* a zero length string */ + +extern char zero_length_string[]; + + +/* function prototypes */ + +/* the main function -- read a line of text */ + +extern char *readline (char *); + +/* character functions used by readline () */ + +extern int readchar (void); + +extern void dispatch_special (int); + +/* keystroke functions */ + +extern void kill_region (void); + +extern void kill_to_eol (void); + +extern void insert_char (char); + +extern void delete_char (enum direction); + +extern void move_cursor_left (void); + +extern void move_cursor_right (void); + +extern void move_cursor_to_start (void); + +extern void move_cursor_to_end (void); + +extern void transpose_chars (void); + +extern void yank_text (void); + +extern void do_count (void); + +/* functions for the kill ring */ + +extern void kill_to_ring (int, int); + +extern char *get_ring_entry (int); + +extern void ring_init (void); + +/* tab completion */ + +extern void do_tab_completion (void); + +/* history functions */ + +extern void add_history(char *); + +extern char *next_history (void); + +extern char *prev_history (void); diff --git a/tap/readline/ring.c b/tap/readline/ring.c new file mode 100755 index 0000000..474cc27 --- /dev/null +++ b/tap/readline/ring.c @@ -0,0 +1,114 @@ +/* ring.c + * Part of the Readline module for Tap. This is the implementation + * of the kill ring. + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of Tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "readline.h" + +/* If the requested entry in the kill ring is NULL, + * this is returned instead. + */ +char zero_length_string[] = ""; + +/* Returns text previously yanked, with the most recent + * having an index of 1. If the entry is NULL, returns + * a pointer to a zero length string. Memory returned by + * this call should never be freed + */ +char * +get_ring_entry (int which) +{ + struct kill_ring *current; + int k; + + which--; + current = the_kill_ring; + + for (k = 0; k < which; k++) current = current->prev; + + if (current->text == NULL) return (zero_length_string); + + return (current->text); +} + +/* Add TEXT to the kill ring and advance the ring. The + * memory passed to this function should never be freed. + */ +void +add_ring_entry (char *text) +{ + the_kill_ring = the_kill_ring->next; + + if (the_kill_ring->text != NULL) xfree (the_kill_ring->text); + + the_kill_ring->text = text; + the_kill_ring->length = strlen (text); +} + +/* Copy text from START to FINISH to a new block + * and add block to kill ring + */ +void +kill_to_ring (int start, int finish) +{ + char *text; + + text = xmalloc (finish - start + 1); + + strncpy (text, current_line + start, finish - start); + text[finish - start] = 0; + + add_ring_entry (text); +} + +/* Initialize the kill ring. + */ +void +ring_init (void) +{ + int k; + struct kill_ring *current; + + /* allocate first separately */ + the_kill_ring = xmalloc (sizeof (struct kill_ring)); + the_kill_ring->length = 0; + the_kill_ring->text = NULL; + + current = the_kill_ring; + + for (k = 0; k < DEFAULT_KILL_RING_SIZE - 2; k++) + { + current->next = xmalloc (sizeof (struct kill_ring)); + + current->next->length = 0; + current->next->text = NULL; + current->next->prev = current; + + current = current->next; + } + + /* complete the ring */ + current->next = the_kill_ring; + the_kill_ring->prev = current; +} diff --git a/tap/readline/tab.c b/tap/readline/tab.c new file mode 100755 index 0000000..dea8e15 --- /dev/null +++ b/tap/readline/tab.c @@ -0,0 +1,530 @@ +/* tab.c + * Part of the Readline module for Tap. This is where command line + * completion is implemented. + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of Tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "readline.h" +#include <glob.h> + +/* returned to do_tab_completion () when a successful match hasn't been made */ +char *null_match[] = { NULL, NULL }; + +/* find the number of matching characters in the two strings */ +int +strmatch (char *first, char *second) +{ + int highest; + + for (highest = 0; highest < MAX (strlen (first), strlen (second)); highest++) + if (first[highest] != second[highest]) break; + + return (highest); +} + +/* Scan an array of strings looking for the largest + * number of matching characters. For the array + * + * "dobri" + * "dyen" + * "dinamacheskiye" + * "dudayevtsi" + * + * the result would be 1. + */ +int +find_matching_chars (char *matches[]) +{ + int current_match, max_match = INT_MAX, index, last = 0; + + while (matches[last + 1] != NULL) last++; + + if (!last) return (strlen (matches[0])); /* one item in array */ + + for (index = 1; index <= last; index++) + { + current_match = strmatch (matches[index - 1], matches[index]); + + max_match = MIN (max_match, current_match); + } + + return (max_match); +} + +/* Return an array of strings containing the main command names */ +char ** +command_names (void) +{ + static char **names = NULL; + int index = 0, max_commands; + + if (names != NULL) return (names); + + names = xmalloc (sizeof (char *) * INCR_VALUE); + max_commands = INCR_VALUE; + + while (commands[index].name != NULL) + { + if (index == max_commands - 1) + { + names = xrealloc (names, sizeof (char *)*(max_commands+INCR_VALUE)); + max_commands += INCR_VALUE; + } + + names[index] = commands[index].name; + index++; + } + + names[index] = NULL; + + return (names); +} + +/* Return the `number' of the current token */ +int +find_current_token_number (void) +{ + int token_number = 1, pos = current_position + 1; + + while (pos--) + { /* scan backward counting whitespace regions */ + + if (current_line[pos] == ' ') /* found one */ + { + token_number++; + + /* skip the rest of the region */ + while (current_line[pos] == ' ' && pos) pos--; + + /* if the first char is ' ' */ + if (!pos && current_line[pos] == ' ') token_number--; + } + } + + return (token_number); +} + +/* move the point to the end of the current token */ +void +move_to_token_end (void) +{ + while (current_line[current_position] != ' ' + && current_position != chars_in_line) move_cursor_right (); +} + +/* If a match string for a command is \f, this function is + * called. Uses the POSIX.2 function glob() to match files + * and returns all files matched by SEARCH with a '*' tacked + * onto the end. + */ +char ** +match_file_esc (char *search) +{ + glob_t file_list; + int k; + char **ret_list, *pattern; + + pattern = xmalloc (strlen (search) + 2); + sprintf (pattern, "%s*", search); + + if (glob (pattern, GLOB_MARK, NULL, &file_list)) return (null_match); + + ret_list = xmalloc ((file_list.gl_pathc + 1) * sizeof (char *)); + + for (k = 0; k < file_list.gl_pathc; k++) + { + ret_list[k] = xmalloc (strlen (file_list.gl_pathv[k]) + 1); + strcpy (ret_list[k], file_list.gl_pathv[k]); + } + + ret_list[file_list.gl_pathc] = NULL; + + xfree (pattern); + return (ret_list); +} + +/* Returns an array of strings which match the escape. Valid + * escapes are \f - a filename, \c - a command name, and \m - anything. + */ +char ** +find_esc_matches (char *search, char *esc_code) +{ + switch (esc_code[1]) + { + case 'f': + return (match_file_esc (search)); + + /* this will be made functional later -- it isn't + * used for anything now anyways */ + case 'c': + + case 'm': /* this doesn't match anything specific */ + return (null_match); + } + + return (null_match); +} + +/* Given a search string and an array of strings, returns an + * array of strings which match + */ +char ** +find_matches (char *search, char **potential_matches) +{ + char **actual_matches; + int possible_completions, length = strlen (search), last_completion = -1, k; + + /* count the entries in potential_matches */ + for (possible_completions = 0; + potential_matches[possible_completions] != NULL; + possible_completions++); + + /* check for escapes */ + if (possible_completions == 1 && potential_matches[0][0] == '\\') + return (find_esc_matches (search, potential_matches[0])); + + actual_matches = xmalloc ((possible_completions + 1) * sizeof (char *)); + + /* zero out the output array */ + for (k = 0; k < possible_completions + 1; k++) actual_matches[k] = NULL; + + /* loop backward looking for matches */ + while (possible_completions--) + { + /* found a match */ + if (!strncmp (search, potential_matches[possible_completions], length)) + { + /* add to match list */ + actual_matches[++last_completion] = + potential_matches[possible_completions]; + } + } + + return (actual_matches); +} + +/* Expands a token to the maximum possible size using + * partial matches. Assumes that the point is at the end of the token + * to be expanded, that token contains the partial token, and + * that matches is a NULL terminated array with strings that + * are matched by token. + */ +void +expand_text (char *token, char *matches[]) +{ + int match_length, tok_length, match_pos; + + match_pos = tok_length = strlen (token); + + match_length = find_matching_chars (matches) - tok_length; + + if (match_length < 1) return; + + while (match_length--) insert_char (matches[0][match_pos++]); + + if (current_line[current_position] != ' ' + && matches[1] == NULL + && current_line[current_position - 1] != '/') + insert_char (' '); +} + +/* Retrieves the match string for COMMAND. In other words + * returns commands[n].tab_completion when + * strcmp (command, commands[n].name) == 0 + */ +char * +get_match_string (char *command) +{ + int index = -1, tok_len = strlen (command); + + while (commands[++index].name != NULL) + { + if (!strncmp (command, commands[index].name, tok_len)) + return (commands[index].tab_completion); + } + + /* this should never be reached */ + return (zero_length_string); +} + +/* print all of the strings in MATCHES */ +void +print_partial_matches (char *matches[]) +{ + int index, command_width = 0, commands_per_line; + int x_pos = input_window->_curx, y, y_offset; + + y = input_window->_cury; + move_cursor_to_end (); + y_offset = input_window->_cury - y; + + waddch (input_window, '\n'); + + /* find longest string */ + index = -1; + while (matches[++index] != NULL) + command_width = MAX (command_width, strlen (matches[index])); + + command_width += PADDING; + + commands_per_line = window_width / command_width; + + /* print strings */ + index = -1; + while (matches[++index] != NULL) + { + wprintw (input_window, "%-*s", command_width, matches[index]); + if (!((index + 1) % commands_per_line)) waddch (input_window, '\n'); + } + + if (input_window->_curx) waddch (input_window, '\n'); + + start_y = input_window->_cury; + + wprintw (input_window, "%s%s", prompt, current_line); + + input_window->_curx = x_pos; + input_window->_cury -= y_offset; +} + +/* Returns the NUMBERth token in current_line */ +char * +get_token_by_number (int number) +{ + int pos = 0; + + while (number--) + { + if (number) + while (current_line[pos] != ' ' && pos < chars_in_line) pos++; + + while (current_line[pos] == ' ' && pos < chars_in_line) pos++; + } + + return (get_token_only (current_line + pos)); +} + +/* Returns the position of the next level one match_string + */ +int +next_level_one (char *match_string, int pos) +{ + int depth = 0; + + pos--; + + while (match_string[++pos] != '\0') + { + switch (match_string[pos]) + { + case '{': + depth++; + break; + case '}': + depth--; + break; + case ',': + if (!depth) + return (pos + 1); + } + } + + return (-1); +} + +/* Returns the sub-match_string for TOKEN. If TOKEN isn't in + * MATCH_STRING, returns "" + */ +char * +get_sublist (char *token, char *match_string) +{ + int pos = 0, len; + char *sublist; + + while (1) + { + /* got a match */ + if (!strncmp (token, match_string + pos, strlen (token))) break; + + pos = next_level_one (match_string, pos); + + if (pos == -1) return (zero_length_string); + } + + /* skip to end of token */ + while ((match_string[pos] != ',') && (match_string[pos] != '{') + && (match_string[pos] != '\0')) pos++; + + if (match_string[pos] != '{') return (zero_length_string); + + /* find substring */ + len = next_level_one (match_string, pos); + + if (len == -1) + len = strlen (match_string) - pos + 1; + else + len -= pos; + + len -= 3; + + /* extract */ + sublist = xmalloc (len + 1); + strncpy (sublist, match_string + pos + 1, len); + sublist[len] = '\0'; + + return (sublist); +} + +/* Returns an array with the top level entries in SEARCH_TEXT */ +char ** +get_search_strings (char *search_text) +{ + char **search_strings = NULL; + int offset, pos = 0, current = 0, max_str; + + search_strings = xmalloc (INCR_VALUE * sizeof (char *)); + max_str = INCR_VALUE; + + while (1) + { + offset = 0; + while ((search_text[pos + offset] != ',') + && (search_text[pos + offset] != '{') + && (search_text[pos + offset] != '\0')) offset++; + + if (offset == 0) break; + + if (current == max_str - 1) + { + search_strings = xrealloc (search_strings, + sizeof (char *) * (max_str + INCR_VALUE)); + max_str += INCR_VALUE; + } + + search_strings[current] = xmalloc (offset + 1); + strncpy (search_strings[current], search_text + pos, offset); + search_strings[current++][offset] = '\0'; + + switch (search_text[pos + offset]) + { + case ',': + pos = pos + offset + 1; + break; + case '{': + pos = next_level_one (search_text, pos + offset); + break; + case '\0': + goto END; + } + } + + END: + search_strings[current] = NULL; + return (search_strings); +} + +/* Frees the array returned by get_search_strings () */ +void +free_search_strings (char *search_strings[]) +{ + int index = -1; + + while (search_strings[++index] != NULL) xfree (search_strings[index]); + + xfree (search_strings); +} + +/* Completes the current token as much as possible */ +void +do_tab_completion (void) +{ + char *first, **matches, **search_strings, *match_string, *token = NULL; + int token_number = find_current_token_number (), tok; + + search_strings = command_names (); + + first = get_token_by_number (1); + + matches = find_matches (first, search_strings); + + if (matches[0] == NULL) + { + warning_beep (); + return; + } + + if (token_number == 1) + { + /* just matching the first token */ + move_to_token_end (); + expand_text (first, matches); + + if (matches[1] != NULL) print_partial_matches (matches); + + return; + } + else if (matches[1] != NULL) + { + /* trying to match other than the first token w/o a unique + * first token -- bad news + */ + warning_beep (); + return; + } + + /* matching other than the first token */ + match_string = get_match_string (first); + + /* match all preceding tokens to find out what is trying to be matched */ + for (tok = 2; tok < token_number; tok++) + { + token = get_token_by_number (tok); + match_string = get_sublist (token, match_string); + + if (strlen (match_string) == 0) + { + warning_beep (); + return; + } + } + + token = get_token_by_number (token_number); + + /* got a matchlist for this token */ + search_strings = get_search_strings (match_string); + + matches = find_matches (token, search_strings); + + if (matches[0] == NULL) + { + warning_beep (); + return; + } + + move_to_token_end (); + expand_text (token, matches); + + if (matches[1] != NULL) print_partial_matches (matches); + + free_search_strings (search_strings); +} diff --git a/tap/readline/text.c b/tap/readline/text.c new file mode 100755 index 0000000..2cb7adf --- /dev/null +++ b/tap/readline/text.c @@ -0,0 +1,354 @@ +/* text.c + * Part of the Readline module for Tap. These are functions which + * manipulate the text in some way. + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of Tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "readline.h" + +/* Move the cursor one space to the left, + * don't perform range check and don't update + * position in line + */ +void +cursor_left (void) +{ + if (input_window->_curx == 0) + { + input_window->_cury--; + input_window->_curx = window_width - 1; + } + else + input_window->_curx--; +} + +/* Move the cursor one space to the right, + * don't perform range check and don't update + * position in line + */ +void +cursor_right (void) +{ + if (input_window->_curx + 1 == window_width) + { + input_window->_cury++; + input_window->_curx = 0; + } + else + input_window->_curx++; +} + +/* Kill the region from the mark to the point; + * bound to C-w + */ +void +kill_region (void) +{ + long kstart, kend; + + /* find the region to be killed */ + if (mark_start == -1) + { + warning_beep (); + return; + } + else if (mark_start == current_position) + { + mark_start = -1; + return; + } + else if (mark_start < current_position) + { + int k; + + kstart = mark_start; + kend = current_position; + + for (k = 0; k < kend - kstart; k++) cursor_left (); + } + else + { + kstart = current_position; + kend = mark_start; + } + + kill_to_ring (kstart, kend); + + memmove (current_line + kstart, current_line + kend, + chars_in_line - kend + 1); + + chars_in_line -= kend - kstart; + mark_start = -1; + + current_position = kstart; +} + +/* Kill from the point to the end of the line; + * bound to C-k + */ +void +kill_to_eol (void) +{ + kill_to_ring (current_position, chars_in_line); + + current_line[current_position] = 0; + chars_in_line = current_position; +} + +/* Move the cursor left, doing range check and + * updating position in line; + * bound to C-b and the left arrow key + */ +void +move_cursor_left (void) +{ + if (current_position == 0) + { + warning_beep (); + return; + } + + current_position--; + + cursor_left (); +} + +/* Move the cursor right, doing range check and + * updating position in line; + * bound to C-f and the right arrow key + */ +void +move_cursor_right (void) +{ + if (current_position == chars_in_line) + { + warning_beep (); + return; + } + + current_position++; + + cursor_right (); +} + +/* Move the cursor to the start of the line; + * bound to C-a + */ +void +move_cursor_to_start (void) +{ + int k; + + for (k = 0; k < current_position; k++) cursor_left (); + + current_position = 0; +} + +/* Move the cursor to the end of the line; + * bound to C-e + */ +void +move_cursor_to_end (void) +{ + int k; + + for (k = 0; k < chars_in_line - current_position; k++) cursor_right (); + + current_position = chars_in_line; +} + +/* Transpose the character under the point with the one + * to the left performing range checking; + * bound to C-t + */ +void +transpose_chars (void) +{ + char ch; + + if (current_position == 0 || current_position == chars_in_line) + { + warning_beep (); + return; + } + + ch = current_line[current_position]; + current_line[current_position] = current_line[current_position - 1]; + current_line[current_position - 1] = ch; + + move_cursor_right (); +} + +/* Insert CH into the line at CURRENT_POSITION + */ +void +insert_char (char ch) +{ + if (chars_in_line + 1 == line_size) + { + current_line = xrealloc (current_line, line_size + GROW_SIZE); + line_size += GROW_SIZE; + } + + memmove (current_line + current_position + 1, + current_line + current_position, + chars_in_line - current_position + 1); + + current_line[current_position] = ch; + + cursor_right (); + + current_position++; + chars_in_line++; +} + +/* Delete the character either under the point + * or to the left depending on the direction; + * bound to C-d and DEL as RIGHT and C-h and + * BACKSPACE as LEFT + */ +void +delete_char (enum direction which) +{ + /* check if there is a char to delete */ + if ((which == LEFT && current_position == 0) + || (which == RIGHT && current_position == chars_in_line)) + { + warning_beep (); + return; + } + + if (which == LEFT) + { + current_position--; + cursor_left (); + } + + memmove (current_line + current_position, + current_line + current_position + 1, + chars_in_line - current_position); + + chars_in_line--; +} + +/* Yank text out of the kill ring and insert into the line; + * bound to C-y + */ +void +yank_text (void) +{ + int k; + char *insert_text; + + insert_text = get_ring_entry (repeat_value); + + for (k = 0; k < strlen (insert_text); k++) + { + insert_char (insert_text[k]); + } +} + +/* Read in a number and repeat the following command that + * many times, or if the next command is yank, uses the + * count as an index into the kill ring; + * bound to C-u + */ +void +do_count (void) +{ + int k, y_save, ok, ch = 0, x0, y0, line_pos; + + /* save the sojourn */ + x0 = input_window->_curx; + y0 = input_window->_cury; + line_pos = current_position; + + move_cursor_to_end (); + + waddstr (input_window, "\nC-u> "); wrefresh (input_window); + + y_save = input_window->_cury; + + repeat_value = 0; + + /* read in a number */ + while (1) + { + ch = wgetch (input_window); + if (ch >= '0' && ch <= '9') + { + repeat_value *= 10; + repeat_value += ch - '0'; + + waddch (input_window, ch); wrefresh (input_window); + } + else + { + /* if ok == 1 , the number has been read */ + ok = (ch == '\n') ? 1 : 0; + break; + } + } + + if (ok) /* got a \n */ + { + waddch (input_window, ' '); wrefresh (input_window); + ch = readchar (); + } + + /* restore the old positions */ + input_window->_cury = y_save; + input_window->_curx = 0; + + waddnstr (input_window, " ", window_width); wrefresh (input_window); + + input_window->_curx = x0; + input_window->_cury = y0; + current_position = line_pos; + + if (!ok) /* if a valid number wasn't read, just return */ + { + repeat_value = 1; + warning_beep (); + return; + } + + if (ch == ECYANK) + yank_text (); + else if (ch == ECQUIT) + ungetch ('\n'); + else if (ch == ECCOUNT) + warning_beep (); + else + { + for (k = 0; k < repeat_value; k++) + { + if (ch > 0xFF) + dispatch_special (ch); + else + insert_char (ch); + } + } + + repeat_value = 1; +} diff --git a/tap/serial.c b/tap/serial.c new file mode 100755 index 0000000..2963897 --- /dev/null +++ b/tap/serial.c @@ -0,0 +1,87 @@ +/* serial.c + * This file contains the function sgetch () which reads a + * character from stdin while listening to the serial port. + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of Tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "tap.h" + +/* This function reads a fixed number of chars from the serial port + * and then writes it to wherever it needs to go. */ +void +read_serial_data (void) +{ + int data_read; + + data_read = read (serial_port_fd, serial_buffer, serial_buffer_size); + + write_to_output_window (data_read); + write_to_capture_register (data_read); + write_to_capture_file (data_read); +} + +/* This function reads a character from stdin while listening to + * the serial port (if one is open). + */ +int +sgetch (void) +{ + fd_set readfs; + int ret, highest_port = 1, fds; + + for (;;) + { + /* set focus to input_window */ + wrefresh (input_window); + + FD_ZERO (&readfs); + FD_SET (0, &readfs); /* always listen to stdin */ + + if (port != NULL) + { /* if a serial port is open */ + FD_SET (serial_port_fd, &readfs); + highest_port = serial_port_fd + 1; + } + + fds = select (highest_port, &readfs, NULL, NULL, NULL); + + if (fds < 0) + { /* error */ + /* for now just ignore it */ + } + else if (fds > 0) + { + if (FD_ISSET (0, &readfs)) + { + ret = wgetch (input_window); + return (ret); + } + else if (FD_ISSET (serial_port_fd, &readfs)) + { + read_serial_data (); /* and reloop */ + } + } + } + + return (ret); +} diff --git a/tap/tap.c b/tap/tap.c new file mode 100755 index 0000000..dd55ff7 --- /dev/null +++ b/tap/tap.c @@ -0,0 +1,274 @@ +/* tap.c + * Main program of Tap. + * + * Usage: + * -p --port <serial port> serial port to open on startup + * -s --save_history [<file name>] whether or not to save the command history + * + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "tap.h" +#include "version.h" +#include "command_struct.h" + + +/* global variables */ + +/* name of the program (argv[0]) */ + +char *pname; + +/* the name of the serial port */ + +char *port = NULL; + +/* the name of the history file */ + +char *history_file_name; + +/* whether or not to save history */ + +int save_history = 0; + +/* the size for the data registers */ + +int register_size = DEFAULT_REGISTER_SIZE; + +/* the registers themselves */ + +struct data_register registers[MAX_REGISTERS]; + +/* jmp_buf for recovery from nearly unrecoverable situations */ + +jmp_buf start_of_input_loop; + +/* the format for displaying output and parsing input */ + +enum io_format input_format = ASCII_COOKED; + +enum io_format output_format = ASCII_COOKED; + + +/* Print useful version information. */ +void +print_version (void) +{ + printf ("Tap %s\n\ +Copyright (C) 1999 Jonathan duSaint <jon@mbayweb.com>\n\ +Tap comes with NO WARRANTY,\n\ +to the extent permitted by law.\n\ +You may redistribute copies of Tap\n\ +under the terms of the GNU General Public License.\n\ +For more information about these matters,\n\ +see the file named COPYING.\n", version); +} + +/* Print some usage information. */ +void +print_help (void) +{ + printf ("Tap %s\n\ +Usage: tap [-p <port>][-s [history file name]]\n\ +Report bugs to jon@mbayweb.com after consulting\n\ +the file BUGS\n", version); +} + +/* Returns the first token in line, but does not return any of + * the other information provided by get_token. + */ +char * +get_token_only (const char *line) +{ + int next, length; + + return (get_token (line, &next, &length)); +} + +/* Returns the first token in LINE. The index of the next token is + * returned in NEXT and the length of the token is returned in + * TOK_LEN. It is assumed that the first char of line is not + * a token delimeter, which is the space character. + */ +char * +get_token (const char *line, int *next, int *tok_len) +{ + int got_name = 0, nxt = 0, cmd_len = 0; + char *cmd = NULL; + + do + { + switch (line[nxt]) + { + case ' ': + case 0: + /* found the end of the token */ + got_name = 1; + cmd_len = nxt; + + /* skip to the next token */ + while (!(isgraph (line[nxt]) || (line[nxt] == 0))) nxt++; + + break; + default: + if (!isgraph (line[nxt])) + { + inp_error ("invalid character"); + return (NULL); + } + } + + nxt++; + } + while (!got_name); + + cmd = xmalloc (cmd_len + 1); + strncpy (cmd, line, cmd_len); + cmd[cmd_len] = 0; + + *next = nxt - 1; + *tok_len = cmd_len; + return (cmd); +} + +/* This function is responsible for taking the line returned by + * readline() and dispatching it off to the proper place. Anything + * other than a zero return value indicates to terminate the program. + */ +int +parse_command_line (char *line) +{ + char *cmd; + int cmd_len, nxt = 0, current = 0; + + /* skip initial whitespace */ + while (*line == ' ') line++; + + cmd = get_token (line, &nxt, &cmd_len); + if (cmd == NULL) return (0); + + cmd = resolve_alias (cmd); + + while (commands[current].function != NULL) + { + if (!strcmp (commands[current].name, cmd)) + return (commands[current].function (line + nxt)); + current++; + } + + inp_error ("unknown command"); + return (0); +} + + +/* Tap itself */ +int +main (int argc, char *argv[]) +{ + int opt, index = 0, have_port = 0; + char *line, *t_port = NULL; + struct option options[] = { + { "port", required_argument, NULL, 'p' }, + { "save-history", optional_argument, NULL, 's' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + { 0, 0, 0, 0 } + }; + + /* initialize global variables */ + pname = argv[0]; + opterr = 0; + history_file_name = "~/.tap_history"; + + /* parse command line for options */ + while (1) + { + opt = getopt_long (argc, argv, "p:s::hv", options, &index); + + if (opt == EOF) break; + + switch (opt) + { + case 'p': + t_port = optarg; + have_port = 1; + break; + case 's': + if (optarg) history_file_name = optarg; + read_history_from_file (history_file_name); + save_history = 1; + break; + case 'h': + print_help (); + return (0); + case 'v': + print_version (); + return (0); + case ':': + exit (1); + case '?': + fprintf (stderr, "%s: unknown option character `%c'\n", + pname, optopt); + break; + default: + fprintf (stderr, "%s: ?? getopt returned character code 0x%x ??\n", + pname, opt); + } + } + + initscr (); + + keypad (stdscr, TRUE); + raw (); + noecho (); + + init_console_ui (); + + /* --port was passed */ + if (have_port) open_p (t_port); + + /* prepare for truly horrendous errors (only from the memory manager) */ + setjmp (start_of_input_loop); + + /* main loop */ + for (;;) + { + refresh_console_status_line (); + + line = readline (">> "); + + if (!strlen (line)) continue; + if (parse_command_line (line)) break; + } + + destroy_console_ui (); + endwin (); + + close_p (NULL); + + if (save_history) save_history_to_file (history_file_name); + + free_alloc_list (); + + return (0); +} diff --git a/tap/tap.h b/tap/tap.h new file mode 100755 index 0000000..5cd778b --- /dev/null +++ b/tap/tap.h @@ -0,0 +1,370 @@ +/* tap.h + * This is the main header file for Tap. In this file are declarations + * of all visible functions and all external datatypes. + */ + +/* all of the includes for all of the source files */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <signal.h> +#include <ctype.h> +#include <setjmp.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <getopt.h> +#include <ncurses.h> + + +/* the default number of user registers */ + +#define MAX_REGISTERS 64 + +/* default size of registers, user may reset size with a call to set */ + +#define DEFAULT_REGISTER_SIZE 64 + +/* by how much command line buffer grows with each realloc */ + +#define GROW_SIZE 8 + +/* the minimum size of an alloc'ed block of memory */ + +#define MINIMUM_ALLOC_SIZE 8 + +/* the number of times out_of_memory may be called + * any more will cause the program to abort + */ + +#define MAX_TIMES_OUT_OF_MEMORY 5 + +/* the size of the mark on each alloc'ed block of memory */ + +#define MARK_SIZE sizeof (long) + +/* The default amount of data to try to read from the serial port + * at one time. This should not be too large, as the more time + * spent reading the serial port, the longer it takes to respond to + * user keystrokes. + */ + +#define DEFAULT_MAXIMUM_ONE_TIME_SERIAL_DATA 16 + +/* This is only used for pretty printing the command names. There + * is no maximum command size, but larger than this may make the + * output from HELP look strange. + */ + +#define MAXIMUM_COMMAND_SIZE 10 + + +/* prototypes for user functions */ + +#define USER_FUNCTION(fname) extern int fname (char *) + +/* max and min macros */ + +#ifndef MAX +# define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef MIN +# define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + + +/* indicate whether or not a block of memory is in use */ +enum mem_in_use + { + USED, FREE + }; + +/* current format for input/output */ + +enum io_format + { + BIN, OCT, HEX, ASCII_RAW, ASCII_COOKED + }; + + +/* data structures */ + +/* the format for a user command */ + +struct command + { + + /* the user-level name */ + char *name; + + /* pointer to the actual function */ + int (*function)(char *); + + /* a string describing how to do command line completion + * of the form + * option1{suboption1,suboption2{subsuboption1}},option2... + * special values are + * \f - a filename + * \c - a command name + * \m - miscellaneous (can't be completed) + */ + char *tab_completion; + + /* a short description of the function */ + char *blurb_help; + + /* the full help text for the function */ + char *full_help; + }; + +/* temporary data storage register */ + +struct data_register + { + + /* size of register (default size may be set with "set regsize ???" */ + long size; + + /* the current write position (active when the register is) */ + long offset; + + /* the data */ + char *data; + }; + +/* storage on the alloc list */ + +struct memory_block + { + + /* whether this chunk can be used or not */ + enum mem_in_use in_use; + + /* the actual chunk size (minus MARK_SIZE) */ + unsigned long size; + + /* the size requested ( <= size ) */ + unsigned long size_used; + + /* pointer to the memory + * the memory returned is actually &memory[MARK_SIZE] */ + void *memory; + }; + + +/* global variables */ + +/* name of the program (argv[0]) */ + +extern char *pname; + +/* currently open port */ + +extern char *port; + +/* the open port as returned by open() */ + +extern int serial_port_fd; + +/* the number of lines per window */ + +extern int window_height; + +/* the number of columns per window */ + +extern int window_width; + +/* the current size of a storage register */ + +extern int register_size; + +/* the actual storage registers */ + +extern struct data_register registers[]; + +/* jmp_buf used by out_of_memory() to recover from near-fatal errors */ + +extern jmp_buf start_of_input_loop; + +/* the data entry format */ + +extern enum io_format input_format; + +/* the data display format */ + +extern enum io_format output_format; + +/* the windows where all of the action takes place */ + +extern WINDOW *input_window; +extern WINDOW *output_window; + +/* all of the user-level commands, their associated functions and the + * helper functions + */ + +extern struct command commands[]; + +/* the buffer which is used for communication with the serial port */ + +extern char *serial_buffer; + +/* the current size of the buffer */ + +extern int serial_buffer_size; + + +/* functions */ + + +/* read a line in a manner similar to the GNU Readline library */ + +extern char *readline (char *); + +/* read a character from the keyboard */ + +extern int sgetch (void); + + +/* memory management functions */ + +/* just like malloc */ + +extern void *xmalloc (size_t); + +/* just like realloc */ + +extern void *xrealloc (void *, size_t); + +/* just like free */ + +extern void xfree (void *); + +/* free everything allocated - called at exit */ + +extern void free_alloc_list (void); + + +/* user interface functions */ + +/* just beep */ + +extern void warning_beep (void); + +/* beep and print a message */ + +extern void inp_error (char *); + +/* initialization */ + +extern void init_console_ui (void); + +/* finalization */ + +extern void destroy_console_ui (void); + +/* called periodically to refresh the status bar */ + +extern void refresh_console_status_line (void); + +/* print and paginate a multiline string */ + +extern void wprintstr (WINDOW *, char *); + +/* change the size of the windows when switching from active mode + * to passive mode and back + */ + +extern void ui_expand_input_window(void); + +extern void ui_shrink_input_window(void); + +/* supply pagination for functions which print several functions, one + * line at a time + */ + +extern void page_init (void); + +extern int page_line_printed (void); + +extern void page_finalize (void); + + +/* utility functions */ + +/* tokenize a string, returning the token length and the position + * of the next token + */ + +extern char *get_token (const char *, int *, int *); + +/* tokenize a string and return just the token */ + +extern char *get_token_only (const char *); + +/* expand a filename with a tilde in it */ + +extern char *tilde_expand (char *); + + +/* history functions */ + +extern void read_history_from_file (char *); + +extern void save_history_to_file (char *); + + +/* get the command to which an alias is assigned */ + +extern char *resolve_alias (char *); + + +/* functions used to write incoming serial port data */ + +extern void write_to_output_window (int); + +extern void write_to_capture_register (int); + +extern void write_to_capture_file (int); + + +/* translation functions for input and output */ + +extern char *translate_to_bin (char *); + +extern char *translate_to_oct (char *); + +extern char *translate_to_hex (char *); + +extern char *translate_to_ascii_raw (char *); + +extern char *translate_to_ascii_cooked (char *); + +extern char *translate_from_bin (char *); + +extern char *translate_from_oct (char *); + +extern char *translate_from_hex (char *); + +extern char *translate_from_ascii_raw (char *); + +extern char *translate_from_ascii_cooked (char *); + + +/* user level functions */ + +/* the user functions */ +USER_FUNCTION (help); +USER_FUNCTION (set); +USER_FUNCTION (out); +USER_FUNCTION (quit); +USER_FUNCTION (capture); +USER_FUNCTION (open_p); +USER_FUNCTION (close_p); +USER_FUNCTION (clear_scr); +USER_FUNCTION (print); +USER_FUNCTION (make_alias); +USER_FUNCTION (unalias); diff --git a/tap/tap.texinfo b/tap/tap.texinfo new file mode 100755 index 0000000..820e803 --- /dev/null +++ b/tap/tap.texinfo @@ -0,0 +1,826 @@ +\input texinfo @c -*-texinfo-*- +@c %**start of header +@setfilename tap.info +@setcontentsaftertitlepage +@settitle Tap +@c %**end of header + + +@c information for a proper install +@dircategory Communication Programs +@direntry +* Tap: (tap). A program to communicate with a serial line tap. +@end direntry + + +@c include commands and keystrokes with the regular index +@syncodeindex fn cp +@syncodeindex ky cp + + +@ifinfo +This file documents Tap, a program to read data from (and write data to) +a serial port. Amongst other things, it may be used to facilitate the +reverse engineering of serial communication protocols. + +Copyright @copyright{} 1999 Jonathan E duSaint @email{jon@@mbayweb.com}. + +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries a copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). + +@end ignore +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +sections entitled ``Copying'' and ``GNU General Public License'' are +included exactly as in the original, and provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation +approved by the Free Software Foundation. +@end ifinfo + + +@titlepage +@title Tap 1.0 +@subtitle A program to communicate with a serial line tap. +@author Jonathan E duSaint <jon@@mbayweb.com> + +@page +@vskip 0pt plus 1filll +Copyright @copyright{} 1999 Jonathan E duSaint @email{jon@@mbayweb.com} + +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +sections entitled ``Copying'' and ``GNU General Public License'' are +included exactly as in the original, and provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions, +except that this permission notice may be stated in a translation +approved by the Free Software Foundation. +@end titlepage + + +@ifnottex +@node Top, Introduction, , (dir) +@comment node-name, next, previous, up +@top Tap + +Tap is a program designed to read data from and write data to the serial +port. As such it has a wide variety of applications. For example, it +can be used to prototype serial hardware, or to read data from a serial +line tap, which can facilitate the reverse engineering of serial line +communication protocols. + +This documentation corresponds with version 1.0 of Tap. +@end ifnottex + +@menu +* Introduction:: An introduction to @code{Tap} and the manual. +* The Command Line:: The ins and outs of the interface. +* Command Overview:: A look at the commands. +* Input and Output:: Communicating with the serial line. +* The Set Command:: Setting the environment. + +* Index:: The command, concept, and keystroke index. + +@detailmenu +--- The Detailed Node Listing --- +* Conventions:: Document conventions. +* Invocation:: How to start @code{Tap} and the command line options. +* Overview:: A brief tour through @code{Tap}. + +* History:: How to use the history functionality. +* Editing:: Command line editing. + +* C-B: Moving Left. Moving the cursor to the left. +* Left Arrow: Moving Left. + Moving the cursor to the left. +* C-F: Moving Right. Moving the cursor to the right. +* Right Arrow: Moving Right. + Moving the cursor to the right. +* C-A:: Moving the cursor to the start of the line. +* C-E:: Moving the cursor to the end of the line. +* C-D:: Deleting the character under the point. +* C-H:: Deleting the character to the left of the point. +* C-U:: Entering a numerical argument. +* C-space: Killing and Yanking Text. + Setting the mark position. +* C-K: Killing and Yanking Text. + Killing from the point to the end of the line. +* C-W: Killing and Yanking Text. + Killing in between the mark and the point. +* C-Y: Killing and Yanking Text. + Yanking text from the kill ring. +* C-T:: Transposing characters. +* C-L:: Clearing the screen. +* tab:: Command line completion. + +* alias:: Assign a different name to a command. +* capture:: Capture incoming data to a file or a register. +* clear:: Clear the input window. +* close:: Close an open serial port. +* help:: Get help on a command. +* open:: Open a serial port. +* out:: Write format controlled data to the serial port. +* print:: Print the contents of a file or a register. +* quit:: Quit the program. +* set:: Change the behavior of the program. +* unalias:: Remove an alias. + +* Help:: The --help command line option. +* version:: The --version command line option. +* port:: The --port command line option. +* save-history:: The --save-history command line option. +@end detailmenu +@end menu + + +@node Introduction, The Command Line, Top, Top +@comment node-name, next, previous, up +@chapter Introduction + +This chapter serves as an introduction to the manual as well as to +@code{Tap}, and documents the command line options. + +@menu +* Conventions:: Document conventions. +* Invocation:: How to start @code{Tap} and the command line options. +* Overview:: A brief tour through @code{Tap}. +@end menu + + +@node Conventions, Invocation, Introduction, Introduction +@comment node-name, next, previous, up +@section Conventions +@cindex Document, How to Read +@cindex Explanation of the C-? Syntax +@cindex C-? Syntax, Explanation of + +The syntax @dfn{C-whatever} means to press the @kbd{Control} key and to +simultaneously press the @kbd{whatever} key. For example, @kbd{C-P} +means to hold down the @kbd{Control} key, and then hit the @kbd{P} key. + +When the command definitions are presented, the command usage is put +first. Then follows a description of the function. If there is more +than one way to use a function, then each invocation is shown with an +explanation after it. + + +@node Invocation, Overview, Conventions, Top +@comment node-name, next, previous, up +@section Invocation +@cindex Options +@cindex Command Line Options +@cindex Startup Options +@cindex Invocation + +To start @code{Tap}, merely type @kbd{tap} at the shell prompt. If it +is properly installed, you should be presented with the interface. If +it isn't, then read the file @file{INSTALL} for information on how to do +so. @code{Tap} can be invoked with several different command line +options. + +@menu +* Help:: The --help command line option. +* version:: The --version command line option. +* port:: The --port command line option. +* save-history:: The --save-history command line option. +@end menu + + +@node Help, version, Invocation, Invocation +@comment node-name, next, previous, up +@subsection @code{--help} +@findex --help +@findex -h +@cindex @code{help}, Command Line Option + +@code{--help}, @code{-h} + +Invoking @code{Tap} with this option will cause @code{Tap} to print a +short help message to @var{stdout} and then exit. + + +@node version, port, Help, Invocation +@comment node-name, next, previous, up +@subsection @code{--version} +@findex --version +@findex -v +@cindex @code{version}, Command Line Option + +@code{--version}, @code{-v} + +Invoking @code{Tap} with this option will cause @code{Tap} to print +version and copyright information to @var{stdout} and then exit. + + +@node port, save-history, version, Invocation +@comment node-name, next, previous, up +@subsection @code{--port} +@cindex Default Port +@cindex Port, Opening on Invocation +@findex --port +@findex -p +@cindex @code{port}, Command Line Option + +@code{--port} @file{serial port}, @code{-p} @file{serial port} + +With this option, @code{Tap} will open @file{serial port} at startup. +If the serial port can't be opened, @code{Tap} will print an error +message at startup. + + +@node save-history, , port, Invocation +@comment node-name, next, previous, up +@subsection @code{--save-history} +@findex --save-history +@findex -s +@cindex @code{save-history}, Command Line Option +@cindex Saving History +@cindex History, Saving +@cindex History File +@cindex Default History File + +@code{--save-history} @file{history filename}, @code{-s} @file{history filename} + +This option will cause @code{Tap} to read history information at startup +from the file specified and to write history information at shutdown to +the file. If the file specified doesn't exist, it will be created, and +if no file is specified, the default will be used, which is usually +@file{~/.tap_history}. + + +@node Overview, , Invocation, Introduction +@comment node-name, next, previous, up +@section Overview +@findex help, Command + +This overview assumes that @code{tap} is already properly installed on +your system. If it isn't, read the file @file{INSTALL} for instructions. + +To start, invoke @code{tap} from the command line by typing @kbd{tap}. +Once it is started, you will notice that the screen is divided into two +parts, both with headers. The top part is called the @dfn{input window} +and the bottom part is called the @dfn{output window}. At the bottom of +the screen is the status bar. It should state that no serial port is +open on one side, and suggest typing @code{help} for help. + +As a first experiment, try typing @code{help}. You will be presented +with a brief summary of all of the commands available. To get more +specific help information, type @code{help command-name} + + +@node The Command Line, Command Overview, Introduction, Top +@comment node-name, next, previous, up +@chapter The Command Line +@cindex Command Line Interface + +If you are familiar with @code{Bash} or @code{Emacs}, then you should +already have a good grasp of most of the functionality of the command +line interface. While @code{Tap} doesn't actually use the @code{GNU +Readline} or the @code{GNU History} libraries, the interface was +designed to be very similar. All commands are saved in a history list, +although if a command is repeated one or more times, only one copy is +saved. Also, while entering text on the command line, a variety of +keystrokes are available for command line editing. + +@menu +* History:: How to use the history functionality. +* Editing:: Command line editing. +@end menu + +@node History, Editing, The Command Line, The Command Line +@comment node-name, next, previous, up +@section History +@cindex History Functionality +@cindex History List +@cindex History File +@kindex C-P +@kindex C-N +@kindex Up Arrow +@kindex Arrow, Up +@kindex Down Arrow +@kindex Arrow, Down + +Every time a command is entered, it is automatically inserted into the +internal history list. However, if the command is identical to the +previous command, a list entry is not made. + +To access previous entries, press either @kbd{Up Arrow}, or +@kbd{C-P}. To access more recent entries, press either @kbd{Down +Arrow}, or @kbd{C-N}. + +If a previous entry is edited and @kbd{Enter} is not pressed, the edited +copy will be saved in the place of the original. However, once +@kbd{Enter} is pressed, the original copy will once again be available. + +When @code{Tap} is invoked with the @code{--save-history} +(@pxref{save-history}) option, all history entries are read at startup +from the file specified, or, if one isn't specified, from the default +file. At shutdown, the current history list is appended onto the file. + +If a file is specified that doesn't exist, then one will be created at +shutdown. + + +@node Editing, , History, The Command Line +@comment node-name, next, previous, up +@section Editing +@cindex Command Line Editing +@cindex Editing, Command Line + +@code{Tap} contains features for editing the current command line. The +keystrokes are the same as used in @code{Emacs} and in the @code{Bash} +shell's @code{Emacs} mode. + +@menu +--- list of keystrokes --- +* C-B: Moving Left. Moving the cursor to the left. +* Left Arrow: Moving Left. + Moving the cursor to the left. +* C-F: Moving Right. Moving the cursor to the right. +* Right Arrow: Moving Right. + Moving the cursor to the right. +* C-A:: Moving the cursor to the start of the line. +* C-E:: Moving the cursor to the end of the line. +* C-D:: Deleting the character under the point. +* C-H:: Deleting the character to the left of the point. +* C-U:: Entering a numerical argument. +* C-space: Killing and Yanking Text. + Setting the mark position. +* C-K: Killing and Yanking Text. + Killing from the point to the end of the line. +* C-W: Killing and Yanking Text. + Killing in between the mark and the point. +* C-Y: Killing and Yanking Text. + Yanking text from the kill ring. +* C-T:: Transposing characters. +* C-L:: Clearing the screen. +* tab:: Command line completion. +@end menu + +@node Moving Left, Moving Right, Editing, Editing +@comment node-name, next, previous, up +@subsection Moving Left +@kindex C-B +@kindex Left Arrow +@kindex Arrow, Left +@cindex Point Movement +@cindex Cursor Movement + +@kbd{C-B}, @kbd{Left Arrow} + +Both of these keystrokes will move the point one character to the left, +if possible. + + +@node Moving Right, C-A, Moving Left, Editing +@comment node-name, next, previous, up +@subsection Moving Right +@kindex C-F +@kindex Right Arrow +@kindex Arrow, Right +@cindex Point Movement +@cindex Cursor Movement + +@kbd{C-F}, @kbd{Right Arrow} + +Both of these keystrokes will move the point one character to the right, +if possible. + + +@node C-A, C-E, Moving Right, Editing +@comment node-name, next, previous, up +@subsection @kbd{C-A} +@kindex C-A +@cindex Cursor Movement +@cindex Point Movement +@cindex Start of Line, Moving the Point to + +@kbd{C-A} + +This keystroke will move the point to the start of the line. + + +@node C-E, C-D, C-A, Editing +@comment node-name, next, previous, up +@subsection @kbd{C-E} +@kindex C-E +@cindex Cursor Movement +@cindex Point Movement +@cindex End of Line, Moving the Point to + +@kbd{C-E} + +This keystroke will move the point to the end of the line. + + +@node C-D, C-H, C-E, Editing +@comment node-name, next, previous, up +@subsection @kbd{C-D} +@kindex C-D +@kindex Delete +@cindex Deleting Characters + +@kbd{C-D}, @kbd{Delete} + +Both of these keystrokes will delete the character under the point. + + +@node C-H, C-U, C-D, Editing +@comment node-name, next, previous, up +@subsection @kbd{C-H} +@kindex C-H +@kindex Backspace +@cindex Deleting Characters + +@kbd{C-H}, @kbd{Backspace} + +Both of these keystrokes will delete the character to the left of the +point. + + +@node C-U, Killing and Yanking Text, C-H, Editing +@comment node-name, next, previous, up +@subsection @kbd{C-U} +@kindex C-U +@cindex Entering Numerical Arguments +@cindex Numerical Arguments, Entering +@cindex Arguments, Numerical +@cindex Numerical Arguments + +@kbd{C-U} + +This command prompts for a number. After the number is entered, press +@kbd{Enter}, and then the desired key. If the key entered was @kbd{C-Y} +(@pxref{Killing and Yanking Text}), then text from the kill ring will be +entered. If @kbd{Enter} was pressed, then it will be just as if +@kbd{Enter} had been pressed without the argument (i.e. the command line +will be processed). Any other key will be repeated as many times as was +indicated by the number. + +For example, type + +@example +@kbd{C-U} @kbd{2} @kbd{0} @kbd{Enter} @kbd{a} +@end example + +@noindent +to insert 20 copies of the letter `a', or type + +@example +@kbd{C-U} @kbd{5} @kbd{Enter} @kbd{C-D} +@end example + +@noindent +to delete the character under the point and four to the right of it. + + +@node Killing and Yanking Text, C-T, C-U, Editing +@comment node-name, next, previous, up +@subsection Killing and Yanking Text +@kindex C-space +@kindex C-W +@kindex C-K +@kindex C-Y +@cindex Killing Text +@cindex Yanking Text +@cindex Kill Ring +@cindex Copying Text +@cindex Cutting Text +@cindex Pasting Text +@cindex Setting the Mark +@cindex Mark, Setting + +@kbd{C-space} Set the mark. + +@kbd{C-W} Kill the text from the mark to the point. + +@kbd{C-K} Kill the text from the mark to the end of the line. + +@kbd{C-Y} Yank from the kill ring and insert it at the point. + +Tap utilizes a kill ring for storage of text which has been killed using +the above commands. The single character delete commands will not +store anything to the ring. + +The @kbd{C-Y} command will only yank the most recent entry. In order to +yank older entries than that, it must be used in combination with the +@kbd{C-U} command (@pxref{C-U}). For example, type + +@example +@kbd{C-U} @kbd{3} @kbd{Enter} @kbd{C-Y} +@end example + +@noindent +to insert the text from the third to last kill. + + +@node C-T, C-L, Killing and Yanking Text, Editing +@comment node-name, next, previous, up +@subsection @kbd{C-T} +@kindex C-T +@cindex Transposing Characters +@cindex Characters, Transposing + +@kbd{C-T} + +This keystroke transposes the character under the point with the +character to its left. + + +@node C-L, tab, C-T, Editing +@comment node-name, next, previous, up +@subsection @kbd{C-L} +@kindex C-T +@cindex Clearing the Screen + +@kbd{C-L} + +This keystroke clears the screen and redraws the current line. + +@xref{clear}. + + +@node tab, , C-L, Editing +@comment node-name, next, previous, up +@subsection @kbd{tab} +@kindex tab +@cindex Command Line Completion +@cindex Completion, Command +@cindex Command Completion + +@kbd{tab} + +Pressing the tab key will complete the token under the point, if at all +possible. If the point is over the first token, then a command +completion will be attempted. If it is over any other token, a +completion will be attempted, based on the arguments that the command +takes. If only a partial completion is possible, the token is completed +as far as it can be, and all of the matching completions are printed +out. If no completion is possible, the program just beeps. + + +@node Command Overview, Input and Output, The Command Line, Top +@comment node-name, next, previous, up +@chapter Command Overview +@cindex Commands +@cindex Command Overview + +These are descriptions of all of the commands. Some commands are +documented elsewhere, so for them, there will be only a short +description. + +@menu +* alias:: Assign a different name to a command. +* capture:: Capture incoming data to a file or a register. +* clear:: Clear the input window. +* close:: Close an open serial port. +* help:: Get help on a command. +* open:: Open a serial port. +* out:: Write format controlled data to the serial port. +* print:: Print the contents of a file or a register. +* quit:: Quit the program. +* set:: Change the behavior of the program. +* unalias:: Remove an alias. +@end menu + + +@node alias, capture, Command Overview, Command Overview +@comment node-name, next, previous, up +@section @code{alias} +@findex alias, Command +@cindex Aliasing Command Names +@cindex Renaming Commands + +@code{alias} + +Invoking @code{alias} with no arguments will print out all of the +aliases which have been assigned. + +@code{alias} @var{alias} + +The @code{alias} command with one argument will print out the alias and +the command to which it is aliased. + +@code{alias} @var{alias} @var{command} + +With two options, @code{alias} will assign @var{alias} to @var{command} +so that when @var{alias} is entered, the program will act as if +@var{command} itself had been entered. + +This command can be very useful to decrease the amount of typing +necessary. If, for example, the @code{out} (@pxref{out}) command were to +be aliased to @code{o}, then rather than typing + +@example +out foo bar\n +@end example + +@noindent +it would be possible to merely type + +@example +o foo bar\n +@end example + +@noindent +which doesn't save that much typing, but does illustrate the point. + +However, @code{alias} is limited to aliasing commands only, so it is not +possible to type something like + +@example +alias bin 'set if bin' +@end example + +Also see @ref{unalias}. + + +@node capture, clear, alias, Command Overview +@comment node-name, next, previous, up +@section @code{capture} + +@code{capture} @option{file} @file{filename} + +When invoked in this manner, @code{capture} starts capturing data +recieved from the serial port to @file{filename}. Only one file may be +open at a time. + +@code{capture} @option{file} @option{off} + +This causes @code{Tap} to stop capturing data and to close the file. + +@code{capture} @option{register} @option{1--64} + +@code{capture}, when invoked like this, will cause @code{Tap} to start +capturing data to a register. Data is captured until it is turned off, +as described below, or the register is full. The size of the registers +may be changed by using the command @code{set}. + +@code{capture} @option{register} @option{off} + +This stops the capturing to the register. + + +@xref{set}, and @ref{The Set Command}. + +@node clear, close, capture, Command Overview +@comment node-name, next, previous, up +@section @code{clear} + +@code{clear} + +Invoking clear will clear the input window. + +@xref{C-L}. + + +@node close, help, clear, Command Overview +@comment node-name, next, previous, up +@section @code{close} + +@code{close} + +This command closes the currently open serial port. + + +@node help, open, close, Command Overview +@comment node-name, next, previous, up +@section @code{help} + +@code{help} + +When invoked without a command name, @code{help} prints a short +description of every command. + +@code{help} @option{command name} + +This provides help on individual commands. + + +@node open, out, help, Command Overview +@comment node-name, next, previous, up +@section @code{open} + +@code{open} @option{serial port} + +@code{open} opens a serial port and prepares it for communication. The +properties of the serial port may be changed with the command +@code{set}. + +@xref{set}. + + +@node out, print, open, Command Overview +@comment node-name, next, previous, up +@section @code{out} + +@code{out} @option{data} + +The @code{out} command writes data to the serial port. + +@xref{Input and Output}. + + +@node print, quit, out, Command Overview +@comment node-name, next, previous, up +@section @code{print} + +@code{print} @option{file} + +If a file is currently being captured to, this will print the contents +of that file. If no file is open, then an error will be thrown. + +@code{print} @option{file} @file{filename} + +This prints the contents of the file specified in @file{filename}. + +@code{print} @option{file} @file{filename} @option{bytes} + +This does the same as above, but only prints the first @option{bytes} +number of bytes. + +@code{print} @option{register} + +If a register is currently being captured to, this will print its +contents. If one isn't being used, then an error will be thrown. + +@code{print} @option{register} @option{register number} + +This will print the contents of the register specified. + +@code{print} @option{register} @option{register number} @option{bytes} + +This will print the first @option{bytes} number of bytes of the +specified register. + + +@node quit, set, print, Command Overview +@comment node-name, next, previous, up +@section @code{quit} + +@code{quit} + +This command quits the program, closing any open files and the serial +port, if one is open. + + +@node set, unalias, quit, Command Overview +@comment node-name, next, previous, up +@section @code{set} + +@code{set} @option{options} + +The @code{set} command adjusts various run time parameters of +@code{Tap}. @xref{The Set Command}, for a full explanation. + + +@node unalias, , set, Command Overview +@comment node-name, next, previous, up +@section @code{unalias} + +@code{unalias} @option{alias} + +The @code{unalias} command removes the alias @option{alias} from the +alias list. + + +@node Input and Output, The Set Command, Command Overview, Top +@comment node-name, next, previous, up +@chapter Input and Output + + + +@node The Set Command, Index, Input and Output, Top +@comment node-name, next, previous, up +@chapter The Set Command + +Often, while working with @code{Tap}, you will find that you might want +to change some of the operational parameters. + +@node Index, , The Set Command, Top +@comment node-name, next, previous, up +@unnumbered Command, Concept, and Keystroke Index + +@printindex cp + +@contents +@bye diff --git a/tap/ui.c b/tap/ui.c new file mode 100755 index 0000000..02822ec --- /dev/null +++ b/tap/ui.c @@ -0,0 +1,369 @@ +/* ui.c + * This is everything concerned with the visual part of Tap's + * user interface (almost). + */ + +/* Copyright (C) 1999 Jonathan duSaint + * + * This file is part of Tap, an interactive program for reading and + * writing data to the serial port to facilitate the reverse + * engineering of communication protocols. + * + * Tap 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. + * + * Tap 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. + * + * The GNU General Public License is generally kept in a file called + * COPYING or LICENSE. If you do not have a copy of the license, write + * to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "tap.h" + +/* global variables used to determine window dimensions */ +int window_height; +int window_width; + +/* used when in passive mode */ +int saved_window_height; + +/* the windows where all of the action takes place */ +WINDOW *input_window; +WINDOW *output_window; + +/* just header/footer information */ +WINDOW *status_bar; +WINDOW *input_header; +WINDOW *output_header; + +/* this is displayed if there are an even number of lines in the terminal */ +WINDOW *title_bar = NULL; + + +/* Write HOW_MUCH bytes from serial_buffer to output_window using + * the default format. + */ +void +write_to_output_window (int how_much) +{ + char *inpb, *outb = NULL; + + inpb = xmalloc (how_much + 1); + + strncpy (inpb, serial_buffer, how_much); + inpb[how_much] = 0; + + switch (output_format) + { + case BIN: + outb = translate_to_bin (inpb); + break; + case OCT: + outb = translate_to_oct (inpb); + break; + case HEX: + outb = translate_to_hex (inpb); + break; + case ASCII_RAW: + outb = translate_to_ascii_raw (inpb); + break; + case ASCII_COOKED: + outb = translate_to_ascii_cooked (inpb); + } + + waddstr (output_window, outb); + wrefresh (output_window); + + xfree (outb); + xfree (inpb); + + wrefresh (input_window); /* transfer control back to input_window */ +} + +/* The next three functions are for displaying a series of + * lines with pagination. + */ + +/* The number of lines which have been printed. */ +int lines_printed; + +/* Initialize pagination */ +void +page_init (void) +{ + lines_printed = 0; +} + +/* Called after a single line has been printed. Returns 0 if + * it is OK to keep printing lines, otherwise 1. + */ +int +page_line_printed (void) +{ + int answer; + + lines_printed++; + + /* a full page has been output */ + if (!((lines_printed + 1) % window_height)) + { + waddstr (input_window, "press any key to continue, <q> to quit: "); + answer = sgetch (); + waddch (input_window, '\n'); + + if (answer == 'q' || answer == 'Q') return (1); + + clear_scr (NULL); + lines_printed = 0; + } + + return (0); +} + +/* Finalize. This isn't always necessary to call. */ +void +page_finalize (void) +{ + lines_printed = 0; +} + +/* Print a multiline string to WINDOW with pagination. */ +void +wprintstr (WINDOW *window, char *string) +{ + char *work_area; + int start = 0, end = 0; + + work_area = xmalloc (strlen (string) + 2); + + strcpy (work_area, string); + + if (string[strlen (string) - 1] != '\n') + { + work_area[strlen (string)] = '\n'; + work_area[strlen (string) + 1] = 0; + } + + page_init (); + + while (1) + { + while (work_area[end] != '\n') end++; + + work_area[end] = 0; + + wprintw (window, "%s\n", work_area + start); + + start = ++end; + + if (page_line_printed ()) break; + if (work_area[start] == 0) break; + } + + page_finalize (); + + xfree (work_area); +} + +/* beep and print a message */ +void +inp_error (char *msg) +{ + beep (); + + wprintw (input_window, "?? %s\n", msg); + wrefresh (input_window); +} + +/* just beep */ +void +warning_beep (void) +{ + beep (); +} + +/* This is used when going from passive mode into active mode. Changes + * the dimensions of input_window. + */ +void +ui_expand_input_window (void) +{ + int pad; + WINDOW *twoutput, *twinput; + + window_height = saved_window_height; + pad = 1 - (LINES % 2); + + twinput = newwin (window_height, window_width, pad + 1, 0); + + mvwin (output_header, window_height + pad + 1, 0); + + twoutput = newwin (window_height, window_width, window_height + pad + 2, 0); + + scrollok (twinput, TRUE); + scrollok (twoutput, TRUE); + + delwin (input_window); + delwin (output_window); + + input_window = twinput; + output_window = twoutput; + + wrefresh (input_window); + wrefresh (output_header); + wrefresh (output_window); + + return; +} + +/* This is used when going from active mode into passive mode. Changes + * the dimensions of input_window. + */ +void +ui_shrink_input_window (void) +{ + int pad; + WINDOW *twoutput, *twinput; + + if (window_height - 5 < 1) return; + + saved_window_height = window_height; + window_height = 5; + + pad = 1 - (LINES % 2); + + twinput = newwin (window_height, window_width, pad + 1, 0); + + mvwin (output_header, window_height + pad + 1, 0); + + twoutput = newwin (2 * saved_window_height - 5, window_width, + window_height + pad + 2, 0); + + scrollok (twinput, TRUE); + scrollok (twoutput, TRUE); + + delwin (input_window); + delwin (output_window); + + input_window = twinput; + output_window = twoutput; + + wrefresh (input_window); + wrefresh (output_header); + wrefresh (output_window); + + return; +} + +/* set up the user interface */ +void +init_console_ui (void) +{ + int io_win_height, k, pad = 0; + + if ((LINES < 11) || (COLS < 70)) + { + fprintf (stderr, "%s: window too small to be of use\n", pname); + endwin (); + exit (1); + } + + /* the display should look something like + * + * ----------- title bar -------------- + * | input header | + * | | + * | input area | + * | | + * | output header | + * | | + * | output area | + * | | + * ---- status area ------------------- + * + * although if there are an odd number of lines in the terminal, + * then there will be no title bar + */ + + io_win_height = ((LINES / 2) + ((LINES % 2) - 1)) - 1; + + /* create title if space permits */ + if (!(LINES % 2)) + { + pad = 1; + title_bar = newwin (1, COLS, 0, 0); + wattron (title_bar, A_REVERSE); + for (k = 0; k < (COLS - 32) / 2; k++) waddch (title_bar, ' '); + waddstr (title_bar, "*** tap - serial line tapper ***"); + for (k = 0; k < (COLS - 32) / 2; k++) waddch (title_bar, ' '); + wrefresh (title_bar); + } + + input_header = newwin (1, COLS, pad, 0); + wattron (input_header, A_REVERSE); + waddstr (input_header, "+++ to serial port "); + for (k = 19; k < COLS; k++) waddch (input_header, '+'); + wrefresh (input_header); + + input_window = newwin (io_win_height, COLS, 1 + pad, 0); + scrollok (input_window, TRUE); + + output_header = newwin (1, COLS, io_win_height + pad + 1, 0); + wattron (output_header, A_REVERSE); + waddstr (output_header, "+++ from serial port "); + for (k = 21; k < COLS; k++) waddch (output_header, '+'); + wrefresh (output_header); + + output_window = newwin (io_win_height, COLS, io_win_height + pad + 2, 0); + scrollok (output_window, TRUE); + + status_bar = newwin (1, COLS, LINES - 1, 0); + wattron (status_bar, A_REVERSE); + + window_height = io_win_height; + window_width = COLS; +} + +/* clean up the user interface */ +void +destroy_console_ui (void) +{ + delwin (input_window); + delwin (output_window); + delwin (status_bar); + delwin (input_header); + delwin (output_header); + + if (title_bar != NULL) delwin (title_bar); +} + + +/* periodically refresh the status bar */ +void +refresh_console_status_line (void) +{ + int k, len; + + if (port == NULL) + { + waddstr (status_bar, "+++ no serial port open "); + len = 24; + } + else + { + len = 5 + strlen (port); + wprintw (status_bar, "+++ %s ", port); + } + + for (k = 0; k < COLS - (len + 25); k++) waddch (status_bar, '+'); + waddstr (status_bar, " type `help' for help +++"); + wrefresh (status_bar); + + wmove (status_bar, 0, 0); +} diff --git a/tap/version.h b/tap/version.h new file mode 100755 index 0000000..9afb71a --- /dev/null +++ b/tap/version.h @@ -0,0 +1 @@ +char version[] = "1.0pre1"; |
