From: Chet Ramey Date: Sun, 4 Dec 2011 03:50:46 +0000 (-0500) Subject: commit bash-20060720 snapshot X-Git-Tag: bash-3.2-beta~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9697d2dc759bd96fcf257923ea7f0c888118be77;p=thirdparty%2Fbash.git commit bash-20060720 snapshot --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 3f85d2216..c0eeaa115 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -13511,7 +13511,7 @@ builtins/read.def unwind-protect that restores the default completion function lib/readline/display.c - - make sure to set local_prompt_len in rl_message() + - make sure to set local_prompt_len in rl_message() [in bash-3.2-alpha] 7/5 --- @@ -13523,15 +13523,46 @@ builtins/printf.def --- lib/readline/display.c - save and restore local_prompt_len in rl_{save,restore}_prompt + [in bash-3.2-alpha] 7/9 --- lib/readline/display.c - make sure that _rl_move_cursor_relative sets cpos_adjusted when it offsets `dpos' by wrap_offset in a multi-byte locale. Bug reported - by Andreas Schwab + by Andreas Schwab and Egmont Koblinger subst.c - make sure that the call to mbstowcs in string_extract_verbatim is passed a string with enough space for the closing NUL. Reported by Andreas Schwab + + 7/18 + ---- +lib/readline/{display,terminal}.c + - remove #ifdefs for HACK_TERMCAP_MOTION so we can use + _rl_term_forward_char in the redisplay code unconditionally + +lib/readline/rlprivate.h + - new extern declaration for _rl_term_forward_char + +lib/readline/display.c + - in _rl_move_cursor_relative, use `dpos' instead of `new' when + deciding whether or not a CR is faster than moving the cursor from + its current position + - in _rl_move_cursor_relative, we can use _rl_term_forward_char to + move the cursor forward in a multibyte locale, if it's available. + Since that function doesn't have a handle on where the cursor is in + the display buffer, it has to output a cr and print all the data + - change variable denoting the position of the cursor in the line buffer + from c_pos (variable local to rl_redisplay) to cpos_buffer_position + (variable local to file) for future use by other functions + + 7/25 + ---- +lib/malloc/{stats,table}.h + - include for prototypes for memset, strlen + +lib/termcap/{termcap,tparam}.c + - include and provide macro replacement for bcopy if + necessary diff --git a/CWRU/CWRU.chlog~ b/CWRU/CWRU.chlog~ index 7a2ec13f5..56fb9dce9 100644 --- a/CWRU/CWRU.chlog~ +++ b/CWRU/CWRU.chlog~ @@ -13511,7 +13511,7 @@ builtins/read.def unwind-protect that restores the default completion function lib/readline/display.c - - make sure to set local_prompt_len in rl_message() + - make sure to set local_prompt_len in rl_message() [in bash-3.2-alpha] 7/5 --- @@ -13523,10 +13523,42 @@ builtins/printf.def --- lib/readline/display.c - save and restore local_prompt_len in rl_{save,restore}_prompt + [in bash-3.2-alpha] 7/9 --- lib/readline/display.c - make sure that _rl_move_cursor_relative sets cpos_adjusted when it offsets `dpos' by wrap_offset in a multi-byte locale. Bug reported + by Andreas Schwab and Egmont Koblinger + +subst.c + - make sure that the call to mbstowcs in string_extract_verbatim is + passed a string with enough space for the closing NUL. Reported by Andreas Schwab + + 7/18 + ---- +lib/readline/{display,terminal}.c + - remove #ifdefs for HACK_TERMCAP_MOTION so we can use + _rl_term_forward_char in the redisplay code unconditionally + +lib/readline/rlprivate.h + - new extern declaration for _rl_term_forward_char + +lib/readline/display.c + - in _rl_move_cursor_relative, use `dpos' instead of `new' when + deciding whether or not a CR is faster than moving the cursor from + its current position + - in _rl_move_cursor_relative, we can use _rl_term_forward_char to + move the cursor forward in a multibyte locale, if it's available. + Since that function doesn't have a handle on where the cursor is in + the display buffer, it has to output a cr and print all the data + - change variable denoting the position of the cursor in the line buffer + from c_pos (variable local to rl_redisplay) to cpos_buffer_position + (variable local to file) for future use by other functions + + 7/25 + ---- +lib/malloc/{stats,table}.h + - include for prototypes for memset, strlen diff --git a/lib/malloc/stats.c b/lib/malloc/stats.c index 18c3cef59..0d119fab5 100644 --- a/lib/malloc/stats.c +++ b/lib/malloc/stats.c @@ -28,6 +28,7 @@ #ifdef HAVE_UNISTD_H # include #endif +#include #include "mstats.h" diff --git a/lib/malloc/stats.c~ b/lib/malloc/stats.c~ new file mode 100644 index 000000000..18c3cef59 --- /dev/null +++ b/lib/malloc/stats.c~ @@ -0,0 +1,202 @@ +/* stats.c - malloc statistics */ + +/* Copyright (C) 2001-2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "imalloc.h" + +#ifdef MALLOC_STATS + +#include +#ifdef HAVE_UNISTD_H +# include +#endif + +#include "mstats.h" + +extern int malloc_free_blocks __P((int)); + +extern struct _malstats _mstats; + +extern FILE *_imalloc_fopen __P((char *, char *, char *, char *, size_t)); + +struct bucket_stats +malloc_bucket_stats (size) + int size; +{ + struct bucket_stats v; + + v.nfree = 0; + + if (size < 0 || size >= NBUCKETS) + { + v.blocksize = 0; + v.nused = v.nmal = v.nmorecore = v.nlesscore = v.nsplit = 0; + return v; + } + + v.blocksize = 1 << (size + 3); + v.nused = _mstats.nmalloc[size]; + v.nmal = _mstats.tmalloc[size]; + v.nmorecore = _mstats.nmorecore[size]; + v.nlesscore = _mstats.nlesscore[size]; + v.nsplit = _mstats.nsplit[size]; + v.ncoalesce = _mstats.ncoalesce[size]; + + v.nfree = malloc_free_blocks (size); /* call back to malloc.c */ + + return v; +} + +/* Return a copy of _MSTATS, with two additional fields filled in: + BYTESFREE is the total number of bytes on free lists. BYTESUSED + is the total number of bytes in use. These two fields are fairly + expensive to compute, so we do it only when asked to. */ +struct _malstats +malloc_stats () +{ + struct _malstats result; + struct bucket_stats v; + register int i; + + result = _mstats; + result.bytesused = result.bytesfree = 0; + for (i = 0; i < NBUCKETS; i++) + { + v = malloc_bucket_stats (i); + result.bytesfree += v.nfree * v.blocksize; + result.bytesused += v.nused * v.blocksize; + } + return (result); +} + +static void +_print_malloc_stats (s, fp) + char *s; + FILE *fp; +{ + register int i; + unsigned long totused, totfree; + struct bucket_stats v; + + fprintf (fp, "Memory allocation statistics: %s\n size\tfree\tin use\ttotal\tmorecore lesscore split\tcoalesce\n", s ? s : ""); + for (i = totused = totfree = 0; i < NBUCKETS; i++) + { + v = malloc_bucket_stats (i); + if (v.nmal > 0) + fprintf (fp, "%8lu\t%4d\t%6d\t%5d\t%8d\t%d %5d %8d\n", (unsigned long)v.blocksize, v.nfree, v.nused, v.nmal, v.nmorecore, v.nlesscore, v.nsplit, v.ncoalesce); + totfree += v.nfree * v.blocksize; + totused += v.nused * v.blocksize; + } + fprintf (fp, "\nTotal bytes in use: %lu, total bytes free: %lu\n", + totused, totfree); + fprintf (fp, "\nTotal bytes requested by application: %lu\n", _mstats.bytesreq); + fprintf (fp, "Total mallocs: %d, total frees: %d, total reallocs: %d (%d copies)\n", + _mstats.nmal, _mstats.nfre, _mstats.nrealloc, _mstats.nrcopy); + fprintf (fp, "Total sbrks: %d, total bytes via sbrk: %d\n", + _mstats.nsbrk, _mstats.tsbrk); + fprintf (fp, "Total blocks split: %d, total block coalesces: %d\n", + _mstats.tbsplit, _mstats.tbcoalesce); +} + +void +print_malloc_stats (s) + char *s; +{ + _print_malloc_stats (s, stderr); +} + +void +fprint_malloc_stats (s, fp) + char *s; + FILE *fp; +{ + _print_malloc_stats (s, fp); +} + +#define TRACEROOT "/var/tmp/maltrace/stats." + +void +trace_malloc_stats (s, fn) + char *s, *fn; +{ + FILE *fp; + char defname[sizeof (TRACEROOT) + 64]; + static char mallbuf[1024]; + + fp = _imalloc_fopen (s, fn, TRACEROOT, defname, sizeof (defname)); + if (fp) + { + setvbuf (fp, mallbuf, _IOFBF, sizeof (mallbuf)); + _print_malloc_stats (s, fp); + fflush(fp); + fclose(fp); + } +} + +#endif /* MALLOC_STATS */ + +#if defined (MALLOC_STATS) || defined (MALLOC_TRACE) +FILE * +_imalloc_fopen (s, fn, def, defbuf, defsiz) + char *s; + char *fn; + char *def; + char *defbuf; + size_t defsiz; +{ + char fname[1024]; + long l; + FILE *fp; + + l = (long)getpid (); + if (fn == 0) + { + sprintf (defbuf, "%s%ld", def, l); + fp = fopen(defbuf, "w"); + } + else + { + char *p, *q, *r; + char pidbuf[32]; + int sp; + + sprintf (pidbuf, "%ld", l); + if ((strlen (pidbuf) + strlen (fn) + 2) >= sizeof (fname)) + return; + for (sp = 0, p = fname, q = fn; *q; ) + { + if (sp == 0 && *q == '%' && q[1] == 'p') + { + sp = 1; + for (r = pidbuf; *r; ) + *p++ = *r++; + q += 2; + } + else + *p++ = *q++; + } + *p = '\0'; + fp = fopen (fname, "w"); + } + + return fp; +} +#endif /* MALLOC_STATS || MALLOC_TRACE */ diff --git a/lib/malloc/table.c b/lib/malloc/table.c index ee37b3a71..b9310a6a0 100644 --- a/lib/malloc/table.c +++ b/lib/malloc/table.c @@ -22,6 +22,7 @@ #endif #include +#include #include "imalloc.h" #include "table.h" diff --git a/lib/malloc/table.c~ b/lib/malloc/table.c~ new file mode 100644 index 000000000..ee37b3a71 --- /dev/null +++ b/lib/malloc/table.c~ @@ -0,0 +1,290 @@ +/* table.c - bookkeeping functions for allocated memory */ + +/* Copyright (C) 2001-2003 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash 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. + + Bash is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with Bash; see the file COPYING. If not, write to the Free Software + Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "imalloc.h" +#include "table.h" + +extern int malloc_register; + +#ifdef MALLOC_REGISTER + +#define FIND_ALLOC 0x01 /* allocate new entry or find existing */ +#define FIND_EXIST 0x02 /* find existing entry */ + +static int table_count = 0; +static int table_allocated = 0; +static mr_table_t mem_table[REG_TABLE_SIZE]; +static mr_table_t mem_overflow; + +/* + * NOTE: taken from dmalloc (http://dmalloc.com) and modified. + */ +static unsigned int +mt_hash (key) + const PTR_T key; +{ + unsigned int a, b, c; + unsigned long x; + + /* set up the internal state */ + a = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + x = (unsigned long)key; /* truncation is OK */ + b = x >> 8; + c = x >> 3; /* XXX - was >> 4 */ + + HASH_MIX(a, b, c); + return c; +} + +#if 0 +static unsigned int +which_bucket (mem) + PTR_T mem; +{ + return (mt_hash ((unsigned char *)mem) & (REG_TABLE_SIZE-1)); +} +#else +#define which_bucket(mem) (mt_hash ((unsigned char *)(mem)) & (REG_TABLE_SIZE-1)); +#endif + +static mr_table_t * +find_entry (mem, flags) + PTR_T mem; + int flags; +{ + unsigned int bucket; + register mr_table_t *tp; + mr_table_t *endp, *lastp; + + if (mem_overflow.mem == mem) + return (&mem_overflow); + + bucket = which_bucket (mem); /* get initial hash */ + tp = endp = mem_table + bucket; + lastp = mem_table + REG_TABLE_SIZE; + + while (1) + { + if (tp->mem == mem) + return (tp); + if (tp->mem == 0 && (flags & FIND_ALLOC)) + { + table_count++; + return (tp); + } + + tp++; + + if (tp == lastp) /* wrap around */ + tp = mem_table; + + if (tp == endp && (flags & FIND_EXIST)) + return ((mr_table_t *)NULL); + + if (tp == endp && (flags & FIND_ALLOC)) + break; + } + + /* oops. table is full. replace an existing free entry. */ + do + { + /* If there are no free entries, punt right away without searching. */ + if (table_allocated == REG_TABLE_SIZE) + break; + + if (tp->flags & MT_FREE) + { + memset(tp, 0, sizeof (mr_table_t)); + return (tp); + } + tp++; + + if (tp == lastp) + tp = mem_table; + } + while (tp != endp); + + /* wow. entirely full. return mem_overflow dummy entry. */ + tp = &mem_overflow; + memset (tp, 0, sizeof (mr_table_t)); + return tp; +} + +mr_table_t * +mr_table_entry (mem) + PTR_T mem; +{ + return (find_entry (mem, FIND_EXIST)); +} + +void +mregister_describe_mem (mem, fp) + PTR_T mem; + FILE *fp; +{ + mr_table_t *entry; + + entry = find_entry (mem, FIND_EXIST); + if (entry == 0) + return; + fprintf (fp, "malloc: %p: %s: last %s from %s:%d\n", + mem, + (entry->flags & MT_ALLOC) ? "allocated" : "free", + (entry->flags & MT_ALLOC) ? "allocated" : "freed", + entry->file ? entry->file : "unknown", + entry->line); +} + +void +mregister_alloc (tag, mem, size, file, line) + const char *tag; + PTR_T mem; + size_t size; + const char *file; + int line; +{ + mr_table_t *tentry; + + tentry = find_entry (mem, FIND_ALLOC); + + if (tentry == 0) + { + /* oops. table is full. punt. */ + fprintf (stderr, _("register_alloc: alloc table is full with FIND_ALLOC?\n")); + return; + } + + if (tentry->flags & MT_ALLOC) + { + /* oops. bad bookkeeping. ignore for now */ + fprintf (stderr, _("register_alloc: %p already in table as allocated?\n"), mem); + } + + tentry->mem = mem; + tentry->size = size; + tentry->func = tag; + tentry->flags = MT_ALLOC; + tentry->file = file; + tentry->line = line; + tentry->nalloc++; + + if (tentry != &mem_overflow) + table_allocated++; +} + +void +mregister_free (mem, size, file, line) + PTR_T mem; + int size; + const char *file; + int line; +{ + mr_table_t *tentry; + + tentry = find_entry (mem, FIND_EXIST); + if (tentry == 0) + { + /* oops. not found. */ +#if 0 + fprintf (stderr, "register_free: %p not in allocation table?\n", mem); +#endif + return; + } + if (tentry->flags & MT_FREE) + { + /* oops. bad bookkeeping. ignore for now */ + fprintf (stderr, _("register_free: %p already in table as free?\n"), mem); + } + + tentry->flags = MT_FREE; + tentry->func = "free"; + tentry->file = file; + tentry->line = line; + tentry->nfree++; + + if (tentry != &mem_overflow) + table_allocated--; +} + +/* If we ever add more flags, this will require changes. */ +static char * +_entry_flags(x) + int x; +{ + if (x & MT_FREE) + return "free"; + else if (x & MT_ALLOC) + return "allocated"; + else + return "undetermined?"; +} + +static void +_register_dump_table(fp) + FILE *fp; +{ + register int i; + mr_table_t entry; + + for (i = 0; i < REG_TABLE_SIZE; i++) + { + entry = mem_table[i]; + if (entry.mem) + fprintf (fp, "[%d] %p:%d:%s:%s:%s:%d:%d:%d\n", i, + entry.mem, entry.size, + _entry_flags(entry.flags), + entry.func ? entry.func : "unknown", + entry.file ? entry.file : "unknown", + entry.line, + entry.nalloc, entry.nfree); + } +} + +void +mregister_dump_table() +{ + _register_dump_table (stderr); +} + +void +mregister_table_init () +{ + memset (mem_table, 0, sizeof(mr_table_t) * REG_TABLE_SIZE); + memset (&mem_overflow, 0, sizeof (mr_table_t)); + table_count = 0; +} + +#endif /* MALLOC_REGISTER */ + +int +malloc_set_register(n) + int n; +{ + int old; + + old = malloc_register; + malloc_register = n; + return old; +} diff --git a/lib/readline/display.c b/lib/readline/display.c index a1b476d26..253cf65f8 100644 --- a/lib/readline/display.c +++ b/lib/readline/display.c @@ -1,6 +1,6 @@ /* display.c -- readline redisplay facility. */ -/* Copyright (C) 1987-2005 Free Software Foundation, Inc. +/* Copyright (C) 1987-2006 Free Software Foundation, Inc. This file is part of the GNU Readline Library, a library for reading lines of text with interactive input and history editing. @@ -59,10 +59,6 @@ extern char *strchr (), *strrchr (); #endif /* !strchr && !__STDC__ */ -#if defined (HACK_TERMCAP_MOTION) -extern char *_rl_term_forward_char; -#endif - static void update_line PARAMS((char *, char *, int, int, int, int)); static void space_to_eol PARAMS((int)); static void delete_chars PARAMS((int)); @@ -80,7 +76,8 @@ static int *inv_lbreaks, *vis_lbreaks; static int inv_lbsize, vis_lbsize; /* Heuristic used to decide whether it is faster to move from CUR to NEW - by backing up or outputting a carriage return and moving forward. */ + by backing up or outputting a carriage return and moving forward. CUR + and NEW are either both buffer positions or absolute screen positions. */ #define CR_FASTER(new, cur) (((new) + 1) < ((cur) - (new))) /* _rl_last_c_pos is an absolute cursor position in multibyte locales and a @@ -143,6 +140,7 @@ int _rl_last_c_pos = 0; int _rl_last_v_pos = 0; static int cpos_adjusted; +static int cpos_buffer_position; /* Number of lines currently on screen minus 1. */ int _rl_vis_botlin = 0; @@ -460,7 +458,7 @@ rl_redisplay () { register int in, out, c, linenum, cursor_linenum; register char *line; - int c_pos, inv_botlin, lb_botlin, lb_linenum, o_cpos; + int inv_botlin, lb_botlin, lb_linenum, o_cpos; int newlines, lpos, temp, modmark, n0, num; char *prompt_this_line; #if defined (HANDLE_MULTIBYTE) @@ -484,7 +482,7 @@ rl_redisplay () } /* Draw the line into the buffer. */ - c_pos = -1; + cpos_buffer_position = -1; line = invisible_line; out = inv_botlin = 0; @@ -666,7 +664,7 @@ rl_redisplay () prompt_last_screen_line = newlines; /* Draw the rest of the line (after the prompt) into invisible_line, keeping - track of where the cursor is (c_pos), the number of the line containing + track of where the cursor is (cpos_buffer_position), the number of the line containing the cursor (lb_linenum), the last line number (lb_botlin and inv_botlin). It maintains an array of line breaks for display (inv_lbreaks). This handles expanding tabs for display and displaying meta characters. */ @@ -719,7 +717,7 @@ rl_redisplay () if (in == rl_point) { - c_pos = out; + cpos_buffer_position = out; lb_linenum = newlines; } @@ -813,7 +811,7 @@ rl_redisplay () } if (in == rl_point) { - c_pos = out; + cpos_buffer_position = out; lb_linenum = newlines; } for (i = in; i < in+wc_bytes; i++) @@ -844,9 +842,9 @@ rl_redisplay () } line[out] = '\0'; - if (c_pos < 0) + if (cpos_buffer_position < 0) { - c_pos = out; + cpos_buffer_position = out; lb_linenum = newlines; } @@ -855,7 +853,7 @@ rl_redisplay () inv_lbreaks[newlines+1] = out; cursor_linenum = lb_linenum; - /* C_POS == position in buffer where cursor should be placed. + /* CPOS_BUFFER_POSITION == position in buffer where cursor should be placed. CURSOR_LINENUM == line number where the cursor should be placed. */ /* PWP: now is when things get a bit hairy. The visible and invisible @@ -1011,7 +1009,7 @@ rl_redisplay () pos = inv_lbreaks[cursor_linenum]; /* nleft == number of characters in the line buffer between the start of the line and the desired cursor position. */ - nleft = c_pos - pos; + nleft = cpos_buffer_position - pos; /* NLEFT is now a number of characters in a buffer. When in a multibyte locale, however, _rl_last_c_pos is an absolute cursor @@ -1057,11 +1055,11 @@ rl_redisplay () will be LMARGIN. */ /* The number of characters that will be displayed before the cursor. */ - ndisp = c_pos - wrap_offset; + ndisp = cpos_buffer_position - wrap_offset; nleft = prompt_visible_length + wrap_offset; /* Where the new cursor position will be on the screen. This can be longer than SCREENWIDTH; if it is, lmargin will be adjusted. */ - phys_c_pos = c_pos - (last_lmargin ? last_lmargin : wrap_offset); + phys_c_pos = cpos_buffer_position - (last_lmargin ? last_lmargin : wrap_offset); t = _rl_screenwidth / 3; /* If the number of characters had already exceeded the screenwidth, @@ -1072,7 +1070,7 @@ rl_redisplay () two-thirds of the way across the screen. */ if (phys_c_pos > _rl_screenwidth - 2) { - lmargin = c_pos - (2 * t); + lmargin = cpos_buffer_position - (2 * t); if (lmargin < 0) lmargin = 0; /* If the left margin would be in the middle of a prompt with @@ -1086,7 +1084,7 @@ rl_redisplay () { /* If we are moving back towards the beginning of the line and the last margin is no longer correct, compute a new one. */ - lmargin = ((c_pos - 1) / t) * t; /* XXX */ + lmargin = ((cpos_buffer_position - 1) / t) * t; /* XXX */ if (wrap_offset && lmargin > 0 && lmargin < nleft) lmargin = nleft; } @@ -1131,7 +1129,7 @@ rl_redisplay () if (visible_first_line_len > _rl_screenwidth) visible_first_line_len = _rl_screenwidth; - _rl_move_cursor_relative (c_pos - lmargin, &invisible_line[lmargin]); + _rl_move_cursor_relative (cpos_buffer_position - lmargin, &invisible_line[lmargin]); last_lmargin = lmargin; } } @@ -1724,11 +1722,7 @@ _rl_move_cursor_relative (new, data) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) { dpos = _rl_col_width (data, 0, new); -#if 0 - if (dpos > woff) -#else - if (dpos > prompt_last_invisible) -#endif + if (dpos > prompt_last_invisible) /* XXX - don't use woff here */ { dpos -= woff; /* Since this will be assigned to _rl_last_c_pos at the end (more @@ -1754,7 +1748,7 @@ _rl_move_cursor_relative (new, data) else #endif i = _rl_last_c_pos - woff; - if (new == 0 || CR_FASTER (new, _rl_last_c_pos) || + if (dpos == 0 || CR_FASTER (dpos, _rl_last_c_pos) || (_rl_term_autowrap && i == _rl_screenwidth)) { #if defined (__MSDOS__) @@ -1776,19 +1770,27 @@ _rl_move_cursor_relative (new, data) sequence telling the terminal to move forward one character. That kind of control is for people who don't know what the data is underneath the cursor. */ -#if defined (HACK_TERMCAP_MOTION) - if (_rl_term_forward_char) - { - for (i = cpos; i < dpos; i++) - tputs (_rl_term_forward_char, 1, _rl_output_character_function); - } - else -#endif /* HACK_TERMCAP_MOTION */ + + /* However, we need a handle on where the current display position is + in the buffer for the immediately preceding comment to be true. + In multibyte locales, we don't currently have that info available. + Without it, we don't know where the data we have to display begins + in the buffer and we have to go back to the beginning of the screen + line. In this case, we can use the terminal sequence to move forward + if it's available. */ if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) { - tputs (_rl_term_cr, 1, _rl_output_character_function); - for (i = 0; i < new; i++) - putc (data[i], rl_outstream); + if (_rl_term_forward_char) + { + for (i = cpos; i < dpos; i++) + tputs (_rl_term_forward_char, 1, _rl_output_character_function); + } + else + { + tputs (_rl_term_cr, 1, _rl_output_character_function); + for (i = 0; i < new; i++) + putc (data[i], rl_outstream); + } } else for (i = cpos; i < new; i++) @@ -2216,7 +2218,8 @@ _rl_update_final () char *last_line; last_line = &visible_line[vis_lbreaks[_rl_vis_botlin]]; - _rl_move_cursor_relative (_rl_screenwidth - 1, last_line); + cpos_buffer_position = -1; /* don't know where we are in buffer */ + _rl_move_cursor_relative (_rl_screenwidth - 1, last_line); /* XXX */ _rl_clear_to_eol (0); putc (last_line[_rl_screenwidth - 1], rl_outstream); } diff --git a/lib/readline/rlprivate.h b/lib/readline/rlprivate.h index d85fa9f9a..64aa7bdd3 100644 --- a/lib/readline/rlprivate.h +++ b/lib/readline/rlprivate.h @@ -408,6 +408,7 @@ extern char *_rl_term_up; extern char *_rl_term_dc; extern char *_rl_term_cr; extern char *_rl_term_IC; +extern char *_rl_term_forward_char; extern int _rl_screenheight; extern int _rl_screenwidth; extern int _rl_screenchars; diff --git a/lib/readline/terminal.c b/lib/readline/terminal.c index ce3cfe5ad..b70ed16fb 100644 --- a/lib/readline/terminal.c +++ b/lib/readline/terminal.c @@ -125,9 +125,7 @@ char *_rl_term_IC; char *_rl_term_dc; char *_rl_term_DC; -#if defined (HACK_TERMCAP_MOTION) char *_rl_term_forward_char; -#endif /* HACK_TERMCAP_MOTION */ /* How to go up a line. */ char *_rl_term_up; @@ -391,9 +389,7 @@ static struct _tc_string tc_strings[] = { "le", &_rl_term_backspace }, { "mm", &_rl_term_mm }, { "mo", &_rl_term_mo }, -#if defined (HACK_TERMCAP_MOTION) { "nd", &_rl_term_forward_char }, -#endif { "pc", &_rl_term_pc }, { "up", &_rl_term_up }, { "vb", &_rl_visible_bell }, @@ -490,9 +486,7 @@ _rl_init_terminal_io (terminal_name) _rl_term_ks = _rl_term_ke = _rl_term_at7 = (char *)NULL; _rl_term_mm = _rl_term_mo = (char *)NULL; _rl_term_ve = _rl_term_vs = (char *)NULL; -#if defined (HACK_TERMCAP_MOTION) - term_forward_char = (char *)NULL; -#endif + _rl_term_forward_char = (char *)NULL; _rl_terminal_can_insert = term_has_meta = 0; /* Reasonable defaults for tgoto(). Readline currently only uses diff --git a/lib/termcap/termcap.c b/lib/termcap/termcap.c index 780b15ce4..0addcc64e 100644 --- a/lib/termcap/termcap.c +++ b/lib/termcap/termcap.c @@ -27,6 +27,10 @@ Free Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #include +#ifdef HAVE_UNISTD_H +#include +#endif + #ifdef HAVE_STDLIB_H # include #else @@ -35,6 +39,14 @@ extern char *malloc (); extern char *realloc (); #endif +#if defined (HAVE_STRING_H) +#include +#endif + +#if !defined (HAVE_BCOPY) && (defined (HAVE_STRING_H) || defined (STDC_HEADERS)) +# define bcopy(s, d, n) memcpy ((d), (s), (n)) +#endif + #else /* not HAVE_CONFIG_H */ #ifdef STDC_HEADERS diff --git a/lib/termcap/termcap.c~ b/lib/termcap/termcap.c~ new file mode 100644 index 000000000..780b15ce4 --- /dev/null +++ b/lib/termcap/termcap.c~ @@ -0,0 +1,800 @@ +/* Work-alike for termcap, plus extra features. + Copyright (C) 1985, 86, 93, 94, 95 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to the +Free Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +/* Emacs config.h may rename various library functions such as malloc. */ +#ifdef HAVE_CONFIG_H + +#include + +/* Get the O_* definitions for open et al. */ +#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H) +# include +#endif + +#include + +#ifdef HAVE_STDLIB_H +# include +#else +extern char *getenv (); +extern char *malloc (); +extern char *realloc (); +#endif + +#else /* not HAVE_CONFIG_H */ + +#ifdef STDC_HEADERS +#include +#include +#else +char *getenv (); +char *malloc (); +char *realloc (); +#endif + +/* Do this after the include, in case string.h prototypes bcopy. */ +#if (defined(HAVE_STRING_H) || defined(STDC_HEADERS)) && !defined(bcopy) +#define bcopy(s, d, n) memcpy ((d), (s), (n)) +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef _POSIX_VERSION +#include +#endif + +#endif /* not HAVE_CONFIG_H */ + +#ifndef NULL +#define NULL (char *) 0 +#endif + +#ifndef O_RDONLY +#define O_RDONLY 0 +#endif + +/* BUFSIZE is the initial size allocated for the buffer + for reading the termcap file. + It is not a limit. + Make it large normally for speed. + Make it variable when debugging, so can exercise + increasing the space dynamically. */ + +#ifndef BUFSIZE +#ifdef DEBUG +#define BUFSIZE bufsize + +int bufsize = 128; +#else +#define BUFSIZE 2048 +#endif +#endif + +#include "ltcap.h" + +#ifndef TERMCAP_FILE +#define TERMCAP_FILE "/etc/termcap" +#endif + +#ifndef emacs +static void +memory_out () +{ + write (2, "virtual memory exhausted\n", 25); + exit (1); +} + +static char * +xmalloc (size) + unsigned size; +{ + register char *tem = malloc (size); + + if (!tem) + memory_out (); + return tem; +} + +static char * +xrealloc (ptr, size) + char *ptr; + unsigned size; +{ + register char *tem = realloc (ptr, size); + + if (!tem) + memory_out (); + return tem; +} +#endif /* not emacs */ + +/* Looking up capabilities in the entry already found. */ + +/* The pointer to the data made by tgetent is left here + for tgetnum, tgetflag and tgetstr to find. */ +static char *term_entry; + +static char *tgetst1 (); + +/* Search entry BP for capability CAP. + Return a pointer to the capability (in BP) if found, + 0 if not found. */ + +static char * +find_capability (bp, cap) + register char *bp, *cap; +{ + for (; *bp; bp++) + if (bp[0] == ':' + && bp[1] == cap[0] + && bp[2] == cap[1]) + return &bp[4]; + return NULL; +} + +__private_extern__ +int +tgetnum (cap) + char *cap; +{ + register char *ptr = find_capability (term_entry, cap); + if (!ptr || ptr[-1] != '#') + return -1; + return atoi (ptr); +} + +__private_extern__ +int +tgetflag (cap) + char *cap; +{ + register char *ptr = find_capability (term_entry, cap); + return ptr && ptr[-1] == ':'; +} + +/* Look up a string-valued capability CAP. + If AREA is non-null, it points to a pointer to a block in which + to store the string. That pointer is advanced over the space used. + If AREA is null, space is allocated with `malloc'. */ + +__private_extern__ +char * +tgetstr (cap, area) + char *cap; + char **area; +{ + register char *ptr = find_capability (term_entry, cap); + if (!ptr || (ptr[-1] != '=' && ptr[-1] != '~')) + return NULL; + return tgetst1 (ptr, area); +} + +/* Table, indexed by a character in range 0100 to 0140 with 0100 subtracted, + gives meaning of character following \, or a space if no special meaning. + Eight characters per line within the string. */ + +static char esctab[] + = " \007\010 \033\014 \ + \012 \ + \015 \011 \013 \ + "; + +/* PTR points to a string value inside a termcap entry. + Copy that value, processing \ and ^ abbreviations, + into the block that *AREA points to, + or to newly allocated storage if AREA is NULL. + Return the address to which we copied the value, + or NULL if PTR is NULL. */ + +static char * +tgetst1 (ptr, area) + char *ptr; + char **area; +{ + register char *p, *r; + register int c; + register int size; + char *ret; + register int c1; + + if (!ptr) + return NULL; + + /* `ret' gets address of where to store the string. */ + if (!area) + { + /* Compute size of block needed (may overestimate). */ + p = ptr; + while ((c = *p++) && c != ':' && c != '\n') + ; + ret = (char *) xmalloc (p - ptr + 1); + } + else + ret = *area; + + /* Copy the string value, stopping at null or colon. + Also process ^ and \ abbreviations. */ + p = ptr; + r = ret; + while ((c = *p++) && c != ':' && c != '\n') + { + if (c == '^') + { + c = *p++; + if (c == '?') + c = 0177; + else + c &= 037; + } + else if (c == '\\') + { + c = *p++; + if (c >= '0' && c <= '7') + { + c -= '0'; + size = 0; + + while (++size < 3 && (c1 = *p) >= '0' && c1 <= '7') + { + c *= 8; + c += c1 - '0'; + p++; + } + } + else if (c >= 0100 && c < 0200) + { + c1 = esctab[(c & ~040) - 0100]; + if (c1 != ' ') + c = c1; + } + } + *r++ = c; + } + *r = '\0'; + /* Update *AREA. */ + if (area) + *area = r + 1; + return ret; +} + +/* Outputting a string with padding. */ + +short ospeed; +/* If OSPEED is 0, we use this as the actual baud rate. */ +int tputs_baud_rate; +__private_extern__ char PC = '\0'; + +/* Actual baud rate if positive; + - baud rate / 100 if negative. */ + +static int speeds[] = + { +#ifdef VMS + 0, 50, 75, 110, 134, 150, -3, -6, -12, -18, + -20, -24, -36, -48, -72, -96, -192 +#else /* not VMS */ + 0, 50, 75, 110, 135, 150, -2, -3, -6, -12, + -18, -24, -48, -96, -192, -288, -384, -576, -1152 +#endif /* not VMS */ + }; + +__private_extern__ +void +tputs (str, nlines, outfun) + register char *str; + int nlines; + register int (*outfun) (); +{ + register int padcount = 0; + register int speed; + +#ifdef emacs + extern baud_rate; + speed = baud_rate; + /* For quite high speeds, convert to the smaller + units to avoid overflow. */ + if (speed > 10000) + speed = - speed / 100; +#else + if (ospeed == 0) + speed = tputs_baud_rate; + else if (ospeed > 0 && ospeed < (sizeof speeds / sizeof speeds[0])) + speed = speeds[ospeed]; + else + speed = 0; +#endif + + if (!str) + return; + + while (*str >= '0' && *str <= '9') + { + padcount += *str++ - '0'; + padcount *= 10; + } + if (*str == '.') + { + str++; + padcount += *str++ - '0'; + } + if (*str == '*') + { + str++; + padcount *= nlines; + } + while (*str) + (*outfun) (*str++); + + /* PADCOUNT is now in units of tenths of msec. + SPEED is measured in characters per 10 seconds + or in characters per .1 seconds (if negative). + We use the smaller units for larger speeds to avoid overflow. */ + padcount *= speed; + padcount += 500; + padcount /= 1000; + if (speed < 0) + padcount = -padcount; + else + { + padcount += 50; + padcount /= 100; + } + + while (padcount-- > 0) + (*outfun) (PC); +} + +/* Finding the termcap entry in the termcap data base. */ + +struct buffer + { + char *beg; + int size; + char *ptr; + int ateof; + int full; + }; + +/* Forward declarations of static functions. */ + +static int scan_file (); +static char *gobble_line (); +static int compare_contin (); +static int name_match (); + +#ifdef VMS + +#include +#include +#include + +static int +valid_filename_p (fn) + char *fn; +{ + struct FAB fab = cc$rms_fab; + struct NAM nam = cc$rms_nam; + char esa[NAM$C_MAXRSS]; + + fab.fab$l_fna = fn; + fab.fab$b_fns = strlen(fn); + fab.fab$l_nam = &nam; + fab.fab$l_fop = FAB$M_NAM; + + nam.nam$l_esa = esa; + nam.nam$b_ess = sizeof esa; + + return SYS$PARSE(&fab, 0, 0) == RMS$_NORMAL; +} + +#else /* !VMS */ + +#ifdef MSDOS /* MW, May 1993 */ +static int +valid_filename_p (fn) + char *fn; +{ + return *fn == '\\' || *fn == '/' || + (*fn >= 'A' && *fn <= 'z' && fn[1] == ':'); +} +#else +#define valid_filename_p(fn) (*(fn) == '/') +#endif + +#endif /* !VMS */ + +/* Find the termcap entry data for terminal type NAME + and store it in the block that BP points to. + Record its address for future use. + + If BP is null, space is dynamically allocated. + + Return -1 if there is some difficulty accessing the data base + of terminal types, + 0 if the data base is accessible but the type NAME is not defined + in it, and some other value otherwise. */ + +__private_extern__ +int +tgetent (bp, name) + char *bp, *name; +{ + register char *termcap_name; + register int fd; + struct buffer buf; + register char *bp1; + char *bp2; + char *term; + int malloc_size = 0; + register int c; + char *tcenv; /* TERMCAP value, if it contains :tc=. */ + char *indirect = NULL; /* Terminal type in :tc= in TERMCAP value. */ + int filep; + +#ifdef INTERNAL_TERMINAL + /* For the internal terminal we don't want to read any termcap file, + so fake it. */ + if (!strcmp (name, "internal")) + { + term = INTERNAL_TERMINAL; + if (!bp) + { + malloc_size = 1 + strlen (term); + bp = (char *) xmalloc (malloc_size); + } + strcpy (bp, term); + goto ret; + } +#endif /* INTERNAL_TERMINAL */ + + /* For compatibility with programs like `less' that want to + put data in the termcap buffer themselves as a fallback. */ + if (bp) + term_entry = bp; + + termcap_name = getenv ("TERMCAP"); + if (termcap_name && *termcap_name == '\0') + termcap_name = NULL; +#if 0 +#if defined (MSDOS) && !defined (TEST) + if (termcap_name && (*termcap_name == '\\' + || *termcap_name == '/' + || termcap_name[1] == ':')) + dostounix_filename(termcap_name); +#endif +#endif + + filep = termcap_name && valid_filename_p (termcap_name); + + /* If termcap_name is non-null and starts with / (in the un*x case, that is), + it is a file name to use instead of /etc/termcap. + If it is non-null and does not start with /, + it is the entry itself, but only if + the name the caller requested matches the TERM variable. */ + + if (termcap_name && !filep && !strcmp (name, getenv ("TERM"))) + { + indirect = tgetst1 (find_capability (termcap_name, "tc"), (char **) 0); + if (!indirect) + { + if (!bp) + bp = termcap_name; + else + strcpy (bp, termcap_name); + goto ret; + } + else + { /* It has tc=. Need to read /etc/termcap. */ + tcenv = termcap_name; + termcap_name = NULL; + } + } + + if (!termcap_name || !filep) + termcap_name = TERMCAP_FILE; + + /* Here we know we must search a file and termcap_name has its name. */ + +#ifdef MSDOS + fd = open (termcap_name, O_RDONLY|O_TEXT, 0); +#else + fd = open (termcap_name, O_RDONLY, 0); +#endif + if (fd < 0) + return -1; + + buf.size = BUFSIZE; + /* Add 1 to size to ensure room for terminating null. */ + buf.beg = (char *) xmalloc (buf.size + 1); + term = indirect ? indirect : name; + + if (!bp) + { + malloc_size = indirect ? strlen (tcenv) + 1 : buf.size; + bp = (char *) xmalloc (malloc_size); + } + bp1 = bp; + + if (indirect) + /* Copy the data from the environment variable. */ + { + strcpy (bp, tcenv); + bp1 += strlen (tcenv); + } + + while (term) + { + /* Scan the file, reading it via buf, till find start of main entry. */ + if (scan_file (term, fd, &buf) == 0) + { + close (fd); + free (buf.beg); + if (malloc_size) + free (bp); + return 0; + } + + /* Free old `term' if appropriate. */ + if (term != name) + free (term); + + /* If BP is malloc'd by us, make sure it is big enough. */ + if (malloc_size) + { + malloc_size = bp1 - bp + buf.size; + termcap_name = (char *) xrealloc (bp, malloc_size); + bp1 += termcap_name - bp; + bp = termcap_name; + } + + bp2 = bp1; + + /* Copy the line of the entry from buf into bp. */ + termcap_name = buf.ptr; + while ((*bp1++ = c = *termcap_name++) && c != '\n') + /* Drop out any \ newline sequence. */ + if (c == '\\' && *termcap_name == '\n') + { + bp1--; + termcap_name++; + } + *bp1 = '\0'; + + /* Does this entry refer to another terminal type's entry? + If something is found, copy it into heap and null-terminate it. */ + term = tgetst1 (find_capability (bp2, "tc"), (char **) 0); + } + + close (fd); + free (buf.beg); + + if (malloc_size) + bp = (char *) xrealloc (bp, bp1 - bp + 1); + + ret: + term_entry = bp; + return 1; +} + +/* Given file open on FD and buffer BUFP, + scan the file from the beginning until a line is found + that starts the entry for terminal type STR. + Return 1 if successful, with that line in BUFP, + or 0 if no entry is found in the file. */ + +static int +scan_file (str, fd, bufp) + char *str; + int fd; + register struct buffer *bufp; +{ + register char *end; + + bufp->ptr = bufp->beg; + bufp->full = 0; + bufp->ateof = 0; + *bufp->ptr = '\0'; + + lseek (fd, 0L, 0); + + while (!bufp->ateof) + { + /* Read a line into the buffer. */ + end = NULL; + do + { + /* if it is continued, append another line to it, + until a non-continued line ends. */ + end = gobble_line (fd, bufp, end); + } + while (!bufp->ateof && end[-2] == '\\'); + + if (*bufp->ptr != '#' + && name_match (bufp->ptr, str)) + return 1; + + /* Discard the line just processed. */ + bufp->ptr = end; + } + return 0; +} + +/* Return nonzero if NAME is one of the names specified + by termcap entry LINE. */ + +static int +name_match (line, name) + char *line, *name; +{ + register char *tem; + + if (!compare_contin (line, name)) + return 1; + /* This line starts an entry. Is it the right one? */ + for (tem = line; *tem && *tem != '\n' && *tem != ':'; tem++) + if (*tem == '|' && !compare_contin (tem + 1, name)) + return 1; + + return 0; +} + +static int +compare_contin (str1, str2) + register char *str1, *str2; +{ + register int c1, c2; + while (1) + { + c1 = *str1++; + c2 = *str2++; + while (c1 == '\\' && *str1 == '\n') + { + str1++; + while ((c1 = *str1++) == ' ' || c1 == '\t'); + } + if (c2 == '\0') + { + /* End of type being looked up. */ + if (c1 == '|' || c1 == ':') + /* If end of name in data base, we win. */ + return 0; + else + return 1; + } + else if (c1 != c2) + return 1; + } +} + +/* Make sure that the buffer <- BUFP contains a full line + of the file open on FD, starting at the place BUFP->ptr + points to. Can read more of the file, discard stuff before + BUFP->ptr, or make the buffer bigger. + + Return the pointer to after the newline ending the line, + or to the end of the file, if there is no newline to end it. + + Can also merge on continuation lines. If APPEND_END is + non-null, it points past the newline of a line that is + continued; we add another line onto it and regard the whole + thing as one line. The caller decides when a line is continued. */ + +static char * +gobble_line (fd, bufp, append_end) + int fd; + register struct buffer *bufp; + char *append_end; +{ + register char *end; + register int nread; + register char *buf = bufp->beg; + register char *tem; + + if (!append_end) + append_end = bufp->ptr; + + while (1) + { + end = append_end; + while (*end && *end != '\n') end++; + if (*end) + break; + if (bufp->ateof) + return buf + bufp->full; + if (bufp->ptr == buf) + { + if (bufp->full == bufp->size) + { + bufp->size *= 2; + /* Add 1 to size to ensure room for terminating null. */ + tem = (char *) xrealloc (buf, bufp->size + 1); + bufp->ptr = (bufp->ptr - buf) + tem; + append_end = (append_end - buf) + tem; + bufp->beg = buf = tem; + } + } + else + { + append_end -= bufp->ptr - buf; + bcopy (bufp->ptr, buf, bufp->full -= bufp->ptr - buf); + bufp->ptr = buf; + } + if (!(nread = read (fd, buf + bufp->full, bufp->size - bufp->full))) + bufp->ateof = 1; + bufp->full += nread; + buf[bufp->full] = '\0'; + } + return end + 1; +} + +#ifdef TEST + +#ifdef NULL +#undef NULL +#endif + +#include + +main (argc, argv) + int argc; + char **argv; +{ + char *term; + char *buf; + + term = argv[1]; + printf ("TERM: %s\n", term); + + buf = (char *) tgetent (0, term); + if ((int) buf <= 0) + { + printf ("No entry.\n"); + return 0; + } + + printf ("Entry: %s\n", buf); + + tprint ("cm"); + tprint ("AL"); + + printf ("co: %d\n", tgetnum ("co")); + printf ("am: %d\n", tgetflag ("am")); +} + +tprint (cap) + char *cap; +{ + char *x = tgetstr (cap, 0); + register char *y; + + printf ("%s: ", cap); + if (x) + { + for (y = x; *y; y++) + if (*y <= ' ' || *y == 0177) + printf ("\\%0o", *y); + else + putchar (*y); + free (x); + } + else + printf ("none"); + putchar ('\n'); +} + +#endif /* TEST */ diff --git a/lib/termcap/tparam.c b/lib/termcap/tparam.c index 1c83f0450..644042d29 100644 --- a/lib/termcap/tparam.c +++ b/lib/termcap/tparam.c @@ -27,6 +27,14 @@ extern char *malloc (); extern char *realloc (); #endif +#if defined (HAVE_STRING_H) +#include +#endif + +#if !defined (HAVE_BCOPY) && (defined (HAVE_STRING_H) || defined (STDC_HEADERS)) +# define bcopy(s, d, n) memcpy ((d), (s), (n)) +#endif + #else /* not HAVE_CONFIG_H */ #if defined(HAVE_STRING_H) || defined(STDC_HEADERS) diff --git a/lib/termcap/tparam.c~ b/lib/termcap/tparam.c~ new file mode 100644 index 000000000..1c83f0450 --- /dev/null +++ b/lib/termcap/tparam.c~ @@ -0,0 +1,334 @@ +/* Merge parameters into a termcap entry string. + Copyright (C) 1985, 87, 93, 95 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to the +Free Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ + +/* Emacs config.h may rename various library functions such as malloc. */ +#ifdef HAVE_CONFIG_H +#include + +#ifdef HAVE_STDLIB_H +# include +#else +extern char *getenv (); +extern char *malloc (); +extern char *realloc (); +#endif + +#else /* not HAVE_CONFIG_H */ + +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#define bcopy(s, d, n) memcpy ((d), (s), (n)) +#endif + +#ifdef STDC_HEADERS +#include +#include +#else +char *malloc (); +char *realloc (); +#endif + +#endif /* not HAVE_CONFIG_H */ + +#include "ltcap.h" + +#ifndef NULL +#define NULL (char *) 0 +#endif + +#ifndef emacs +static void +memory_out () +{ + write (2, "virtual memory exhausted\n", 25); + exit (1); +} + +static char * +xmalloc (size) + unsigned size; +{ + register char *tem = malloc (size); + + if (!tem) + memory_out (); + return tem; +} + +static char * +xrealloc (ptr, size) + char *ptr; + unsigned size; +{ + register char *tem = realloc (ptr, size); + + if (!tem) + memory_out (); + return tem; +} +#endif /* not emacs */ + +/* Assuming STRING is the value of a termcap string entry + containing `%' constructs to expand parameters, + merge in parameter values and store result in block OUTSTRING points to. + LEN is the length of OUTSTRING. If more space is needed, + a block is allocated with `malloc'. + + The value returned is the address of the resulting string. + This may be OUTSTRING or may be the address of a block got with `malloc'. + In the latter case, the caller must free the block. + + The fourth and following args to tparam serve as the parameter values. */ + +static char *tparam1 (); + +/* VARARGS 2 */ +char * +tparam (string, outstring, len, arg0, arg1, arg2, arg3) + char *string; + char *outstring; + int len; + int arg0, arg1, arg2, arg3; +{ + int arg[4]; + + arg[0] = arg0; + arg[1] = arg1; + arg[2] = arg2; + arg[3] = arg3; + return tparam1 (string, outstring, len, NULL, NULL, arg); +} + +__private_extern__ char *BC; +__private_extern__ char *UP; + +static char tgoto_buf[50]; + +__private_extern__ +char * +tgoto (cm, hpos, vpos) + char *cm; + int hpos, vpos; +{ + int args[2]; + if (!cm) + return NULL; + args[0] = vpos; + args[1] = hpos; + return tparam1 (cm, tgoto_buf, 50, UP, BC, args); +} + +static char * +tparam1 (string, outstring, len, up, left, argp) + char *string; + char *outstring; + int len; + char *up, *left; + register int *argp; +{ + register int c; + register char *p = string; + register char *op = outstring; + char *outend; + int outlen = 0; + + register int tem; + int *old_argp = argp; + int doleft = 0; + int doup = 0; + + outend = outstring + len; + + while (1) + { + /* If the buffer might be too short, make it bigger. */ + if (op + 5 >= outend) + { + register char *new; + if (outlen == 0) + { + outlen = len + 40; + new = (char *) xmalloc (outlen); + outend += 40; + bcopy (outstring, new, op - outstring); + } + else + { + outend += outlen; + outlen *= 2; + new = (char *) xrealloc (outstring, outlen); + } + op += new - outstring; + outend += new - outstring; + outstring = new; + } + c = *p++; + if (!c) + break; + if (c == '%') + { + c = *p++; + tem = *argp; + switch (c) + { + case 'd': /* %d means output in decimal. */ + if (tem < 10) + goto onedigit; + if (tem < 100) + goto twodigit; + case '3': /* %3 means output in decimal, 3 digits. */ + if (tem > 999) + { + *op++ = tem / 1000 + '0'; + tem %= 1000; + } + *op++ = tem / 100 + '0'; + case '2': /* %2 means output in decimal, 2 digits. */ + twodigit: + tem %= 100; + *op++ = tem / 10 + '0'; + onedigit: + *op++ = tem % 10 + '0'; + argp++; + break; + + case 'C': + /* For c-100: print quotient of value by 96, if nonzero, + then do like %+. */ + if (tem >= 96) + { + *op++ = tem / 96; + tem %= 96; + } + case '+': /* %+x means add character code of char x. */ + tem += *p++; + case '.': /* %. means output as character. */ + if (left) + { + /* If want to forbid output of 0 and \n and \t, + and this is one of them, increment it. */ + while (tem == 0 || tem == '\n' || tem == '\t') + { + tem++; + if (argp == old_argp) + doup++, outend -= strlen (up); + else + doleft++, outend -= strlen (left); + } + } + *op++ = tem ? tem : 0200; + case 'f': /* %f means discard next arg. */ + argp++; + break; + + case 'b': /* %b means back up one arg (and re-use it). */ + argp--; + break; + + case 'r': /* %r means interchange following two args. */ + argp[0] = argp[1]; + argp[1] = tem; + old_argp++; + break; + + case '>': /* %>xy means if arg is > char code of x, */ + if (argp[0] > *p++) /* then add char code of y to the arg, */ + argp[0] += *p; /* and in any case don't output. */ + p++; /* Leave the arg to be output later. */ + break; + + case 'a': /* %a means arithmetic. */ + /* Next character says what operation. + Add or subtract either a constant or some other arg. */ + /* First following character is + to add or - to subtract + or = to assign. */ + /* Next following char is 'p' and an arg spec + (0100 plus position of that arg relative to this one) + or 'c' and a constant stored in a character. */ + tem = p[2] & 0177; + if (p[1] == 'p') + tem = argp[tem - 0100]; + if (p[0] == '-') + argp[0] -= tem; + else if (p[0] == '+') + argp[0] += tem; + else if (p[0] == '*') + argp[0] *= tem; + else if (p[0] == '/') + argp[0] /= tem; + else + argp[0] = tem; + + p += 3; + break; + + case 'i': /* %i means add one to arg, */ + argp[0] ++; /* and leave it to be output later. */ + argp[1] ++; /* Increment the following arg, too! */ + break; + + case '%': /* %% means output %; no arg. */ + goto ordinary; + + case 'n': /* %n means xor each of next two args with 140. */ + argp[0] ^= 0140; + argp[1] ^= 0140; + break; + + case 'm': /* %m means xor each of next two args with 177. */ + argp[0] ^= 0177; + argp[1] ^= 0177; + break; + + case 'B': /* %B means express arg as BCD char code. */ + argp[0] += 6 * (tem / 10); + break; + + case 'D': /* %D means weird Delta Data transformation. */ + argp[0] -= 2 * (tem % 16); + break; + } + } + else + /* Ordinary character in the argument string. */ + ordinary: + *op++ = c; + } + *op = 0; + while (doup-- > 0) + strcat (op, up); + while (doleft-- > 0) + strcat (op, left); + return outstring; +} + +#ifdef DEBUG + +main (argc, argv) + int argc; + char **argv; +{ + char buf[50]; + int args[3]; + args[0] = atoi (argv[2]); + args[1] = atoi (argv[3]); + args[2] = atoi (argv[4]); + tparam1 (argv[1], buf, "LEFT", "UP", args); + printf ("%s\n", buf); + return 0; +} + +#endif /* DEBUG */