/************************************************************************ * * * Copyright (c) 1982, Fred Fish * * All Rights Reserved * * * * This software and/or documentation is released for public * * distribution for personal, non-commercial use only. * * Limited rights to use, modify, and redistribute are hereby * * granted for non-commercial purposes, provided that all * * copyright notices remain intact and all changes are clearly * * documented. The author makes no warranty of any kind with * * respect to this product and explicitly disclaims any implied * * warranties of merchantability or fitness for any particular * * purpose. * * * ************************************************************************ */ /* * Modified: * 1 May 86 ...!ihnp4!ut-sally!ut-ngp!mic * Now forces a '\0' at end of tgoto string. Tgoto wasn't, * and this screwed up VT100-style (i.e. variable) cursor * addressing. * * Sep 88, Apr 89 Tom Hageman * Some optimizations * Jul-Aug 1992, TRH * Add Arcane + GNU extension formats * (See Log at end) */ #ifdef RCS_ID static const char rcsid[] = "$Id: tgoto.c,v 1.6 1993/05/24 22:44:48 tom Exp tom $"; #endif /* * LIBRARY FUNCTION * * tgoto expand cursor addressing string from cm capability * * KEY WORDS * * termcap * * SYNOPSIS * * char *tgoto (cm,destcol,destline) * const char *cm; * int destcol; * int destline; * * DESCRIPTION * * Returns cursor addressing string, decoded from the cm * capability string, to move cursor to column destcol on * line destline. * * The following sequences uses one input argument, either * line or column, and place the appropriate substitution * in the output string: * * %d substitute decimal value (in ASCII) * %2 like %d but forces field width to 2 * %3 like %d but forces field width to 3 * %. like %c * %+x like %c but adds ASCII value of x * * The following sequences cause processing modifications * but do not "use up" one of the arguments. If they * act on an argument they act on the next one to * be converted. * * %>xy if next value to be converted is * greater than value of ASCII char x * then add value of ASCII char y. * %r reverse substitution of line * and column (line is substituted * first by default). * %i causes input values destcol and * destline to be incremented. * %% gives single % character in output. * #if (TCP & EXTENSION) && (TCP & ARCANE) * Gnu Termcap extensions: * * %a[=+-/*][cp]x arithmetic. * %b backup (reuse) previous argument. * %f forward (skip) next argument. * %m Xor next 2 arguments with 0177. * * %Cx argument mod 96, then %+ * %s output argument as string. #endif * * BUGS * #if !(TCP & ARCANE) * Does not implement some of the more arcane sequences for * radically weird terminals (specifically %n, %B, & %D). * If you have one of these you deserve whatever happens. #endif * Does not avoid output of \n or ^D. */ #include #include "_termcap.h" #if _STDARG_ # include #else # include #endif #if (TCP & TPARAM) # define MAXARGS 9 #else # define MAXARGS 2 #endif #if (TCP & BCUP) extern char *BC; extern char *UP; #else # define BC NULL # define UP NULL #endif #if (TCP & TPARAM) static char *_tparam __(( const char *_(cm), char *_(buf), int _(size), # if (TCP & BCUP) const char *_(BC), const char *_(UP), # endif int _(args)[] )); #endif static const char OOPS[] = "OOPS"; /* * PSEUDO CODE * * Begin tgoto * If no string to process then * Return pointer to error string. * Else * Initialize pointer to input string. * Initialize pointer to result string. * First arg is line number by default. * Second arg is col number by default. * No arguments processed yet. * While there is another character to process * If character is a not a % character then * Simply copy to output. * Else * If all args processed * return unprocessed cm string as error message * Process the control sequence: * Switch on next character after % * Case 'd': * setup for %d type conversion (variable width). * Break; * Case '2': * setup for %d type conversion (width 2). * Break; * Case '3': * setup for %d type conversion (width 3). * Break; * Case '.' * Process %c type conversion. * Continue; * Case '+': * Process %c type conversion with offset. * Continue; * Case '>': * Process argument modification. * Continue; * Case 'r': * Process argument reversal. * Continue; * Case 'i': * Increment argument values. * Continue; * Case '%': * Copy to output, incrementing pointers. * Continue; #if (TCP) * etc... #endif * Default: * return pointer to error string. * End switch * Process %d type conversion * End if * End while * Return pointer to static output string. * End if * End tgoto * */ char * tgoto(cm, destcol, destline) const char *cm; int destcol; int destline; { static char output[TGOTOBUFSIZE]; int args[2]; register int *parg = &args[2]; #if (TCP & DYNALLOC) static char *outbuf; if (outbuf != output && outbuf != OOPS) free(outbuf); # if !(TCP & TPARAM) outbuf = output; # define output outbuf # endif #endif /* (TCP & DYNALLOC) */ *--parg = destcol; /* args[1] */ *--parg = destline; /* args[0] */ #if (TCP & TPARAM) return ( # if (TCP & DYNALLOC) outbuf = # endif _tparam(cm, output, TGOTOBUFSIZE, # if (TCP & BCUP) BC, UP, # endif parg)); } char * #if (_ANSI_DEFUN_ && _STDARG_) tparam(const char *cap, char *buf, int size, ...) #else tparam(cap, buf, size, va_alist) const char *cap; char *buf; int size; va_dcl #endif { va_list ap; register int *parg; #if (STACK_DIRECTION > 0 || STACK_DIRECTION == 0) /* Copy parameters to an array if the parameter stack grows in the `wrong' direction (or if parameters are not passed on the stack). */ int args[MAXARGS]; register int *dp = args; register int n; #endif va_begin(ap, size); #if (STACK_DIRECTION < 0 || STACK_DIRECTION == 0) /* If the parameter stack grows in the `right' direction, cheat; parameters can be accessed directly as an array. */ parg = (int *) ap; #endif #if !(STACK_DIRECTION < 0) # if (STACK_DIRECTION > 0) n = MAXARGS; # else /* (STACK_DIRECTION == 0) */ # ifdef STACK_DIRECTION n = MAXARGS; # else /* Stack direction is unknown at compile-time. Make an educated guess by getting a single parameter off the stack, and then comparing old and new values of the `va_list' pointer (assuming it is, indeed, a pointer; if this gives you problems, e.g., if `va_list' is declared as some struct, you should define STACK_DIRECTION explicitly as 0) */ n = MAXARGS - 1; *dp++ = va_arg(ap, int); if ((*(char **) &ap - (char *) parg) != sizeof(int)) # endif { do *dp++ = va_arg(ap, int); while (--n); parg = args; } # endif #endif /* !(STACK_DIRECTION < 0) */ va_end(ap); return _tparam(cap, buf, size, # if (TCP & BCUP) NULL, NULL, # endif parg); } /* {{TODO: size check; BC/UP; dynamic allocation.}} */ static char * _tparam(cm, output, size, # if (TCP & BCUP) BC, UP, # endif args) const char *cm; char *output; int size; # if (TCP & BCUP) const char *BC, *UP; # endif int args[]; { register int *parg = args; #else /* !(TCP & TPARAM) */ # define size TGOTOBUFSIZE #endif /* {...this is still part of tgoto() if not (TCP & TPARAM).} */ { #if (TCP & BCUP) int need_bc = 0; int need_up = 0; #endif register char *out = output; register const char *in; #if (TCP & DYNALLOC) int out_alloced = 0; char *out_limit; if (out_limit = output) out_limit += size - 3; #else # define out_limit (output + size - 3) #endif if ((in = cm) == NULL) oops: return (char *) OOPS; while (1) { if (out > out_limit) { #if (TCP & DYNALLOC) register const char *saved_in = in; out_limit = out; in = output; if (out_alloced == 0) { if (!(output = malloc(out_alloced = size + 32))) goto oops; out = output; while (in < out_limit) { /* copy string. */ *out++ = in++; } /* Now `out' is where it should be. */ } else { if (!(output = realloc(output, out_alloced <<= 1))) goto oops; out = output + (out_limit - in); } out_limit = output + out_alloced - 3; in = saved_in; #else /* !(TCP & DYNALLOC) */ goto oops; #endif } if ((*out++ = *in) == '\0') break; if (*in++ == '%') { register const char *fmt; if (parg >= &args[MAXARGS]) goto oops; --out; switch (*in++) { case 'd': d: fmt = "%d"; break; case '2': fmt = "%02d"; break; case '3': fmt = "%03d"; break; case '.': *out++ = *parg++; #if (TCP & BCUP) c: if (BC && UP) { register char c; while ((c = out[-1]) == '\0' || c == '\n' || c == '\4' || c == '\t') { out[-1]++; if (parg == args) { need_up++; # if (TCP & DYNALLOC) outend -= strlen(UP); # endif } else { need_bc++; # if (TCP & DYNALLOC) outend -= strlen(BC); # endif } } } #endif continue; case '+': *out++ = *parg++ + *in++; #if (TCP & BCUP) goto c; #else continue; #endif case '>': if (*parg > *in++) *parg += *in; in++; continue; case 'r': { int temp = parg[0]; parg[0] = parg[1]; parg[1] = temp; continue; } case 'i': parg[0]++; parg[1]++; continue; case '%': ++out; /* '%' already copied */ continue; #if (TCP & ARCANE) # if (TCP & EXTENSION) /* Gnu termcap extensions */ case 'a': /* arithmetic %a[-+=][pc] */ { register int n = (unsigned) in[2]; if (n == 0) goto oops; switch (in[1]) { case 'c': n &= 0177; break; case 'p': n = parg[n - 0100]; break; default: goto oops; } switch (in[0]) { case '=': *parg = n; break; case '+': *parg += n; break; case '-': *parg -= n; break; case '*': *parg *= n; break; case '/': *parg /= n; break; default: goto oops; } in += 3; continue; } case 'b': /* backup/reuse */ --parg; continue; case 'f': /* discard */ ++parg; continue; case 'm': /* XOR args with 0177 */ parg[0] ^= 0177; parg[1] ^= 0177; continue; case 'C': /* C100: (v % 96), then %+ */ if (*parg >= 96) { *out++ = *parg / 96; *out++ = *parg++ % 96 + *in++; } else { *out++ = *parg++ + *in++; } # if (TCP & BCUP) goto c; # else continue; # endif case 'o': /* like %d if n != 1 */ if (*parg == 1) continue; goto d; case 'z': /* like %d if n != 0 */ if (*parg == 0) continue; goto d; # endif /* (TCP & EXTENSION) */ case 'n': /* XOR args with 0140 */ parg[0] ^= 0140; parg[1] ^= 0140; continue; case 'B': /* BCD 16*(v/10)+v%10 */ *parg += (*parg / 10) * 6; continue; case 'D': /* (v - 2*v%16) */ *parg -= (*parg & 15) << 1; continue; #endif /* (TCP & ARCANE) */ #if (TCP & EXTENSION) case 's': /* output string */ # if (TCP & DYNALLOC) if ((out_limit -= strlen((char *) *parg)) < out) { /* Setup for buffer expansion and re-scan of %s. */ in -= 2; continue; } # endif fmt = in; # ifndef __STRICT_ANSI__ /* Most compilers allow this, even if it is not strict ANSI C. */ in = *((char **) parg)++; # else in = *(char **) parg; parg = (int *)((char *) parg + sizeof(char *)); # endif while (*out++ = *in++) ; --out; in = fmt; continue; #endif default: goto oops; } /* switch */ /* only get here on "non-trivial" conversions */ #ifdef PRINTF_RETURNS_NCHARS out += #endif sprintf(out, fmt, *parg++); #ifndef PRINTF_RETURNS_NCHARS while (*out++) ; /* skip output just appended */ --out; #endif } } #if (TCP & BCUP) while (--need_up >= 0) { in = UP; while (*out++ = *in++) ; --out; } while (--need_bc >= 0) { in = BC; while (*out++ = *in++) ; --out; } #endif } return (output); } /*======================================================================* * $Log: tgoto.c,v $ * Revision 1.6 1993/05/24 22:44:48 tom * implement DYNALLOC, BCUP features; don't output %B, %D as per GNU termcap * 1.2 specs; (tparam): improve STACK_DIRECTION tangle; fix typo in RCS Log. * *======================================================================*/