171333Sitojun/* $FreeBSD$ */ 2118786Sume/* $KAME: advcap.c,v 1.11 2003/05/19 09:46:50 keiichi Exp $ */ 362656Skris 455505Sshin/* 555505Sshin * Copyright (c) 1983 The Regents of the University of California. 655505Sshin * All rights reserved. 755505Sshin * 855505Sshin * Redistribution and use in source and binary forms, with or without 955505Sshin * modification, are permitted provided that the following conditions 1055505Sshin * are met: 1155505Sshin * 1. Redistributions of source code must retain the above copyright 1255505Sshin * notice, this list of conditions and the following disclaimer. 1355505Sshin * 2. Redistributions in binary form must reproduce the above copyright 1455505Sshin * notice, this list of conditions and the following disclaimer in the 1555505Sshin * documentation and/or other materials provided with the distribution. 1655505Sshin * 4. Neither the name of the University nor the names of its contributors 1755505Sshin * may be used to endorse or promote products derived from this software 1855505Sshin * without specific prior written permission. 1955505Sshin * 2055505Sshin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2155505Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2255505Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2355505Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2455505Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2555505Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2655505Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2755505Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2855505Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2955505Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3055505Sshin * SUCH DAMAGE. 3155505Sshin */ 3255505Sshin 3355505Sshin/* 3455505Sshin * remcap - routines for dealing with the remote host data base 3555505Sshin * 3655505Sshin * derived from termcap 3755505Sshin */ 3855505Sshin#include <sys/types.h> 3955505Sshin#include <sys/uio.h> 4055505Sshin#include <unistd.h> 4155505Sshin#include <fcntl.h> 4255505Sshin#include <ctype.h> 4355505Sshin#include <stdlib.h> 4455505Sshin#include <stdio.h> 4555505Sshin#include <syslog.h> 4655505Sshin#include <errno.h> 4755505Sshin#include <string.h> 4855505Sshin#include "pathnames.h" 4955505Sshin 5055505Sshin#ifndef BUFSIZ 5155505Sshin#define BUFSIZ 1024 5255505Sshin#endif 5355505Sshin#define MAXHOP 32 /* max number of tc= indirections */ 5455505Sshin 5555505Sshin#define tgetent agetent 5655505Sshin#define tnchktc anchktc 5755505Sshin#define tnamatch anamatch 5855505Sshin#define tgetnum agetnum 5955505Sshin#define tgetflag agetflag 6055505Sshin#define tgetstr agetstr 6155505Sshin 6262656Skris#if 0 6362656Skris#define V_TERMCAP "REMOTE" 6462656Skris#define V_TERM "HOST" 6562656Skris#endif 6662656Skris 6755505Sshin/* 6855505Sshin * termcap - routines for dealing with the terminal capability data base 6955505Sshin * 7055505Sshin * BUG: Should use a "last" pointer in tbuf, so that searching 7155505Sshin * for capabilities alphabetically would not be a n**2/2 7255505Sshin * process when large numbers of capabilities are given. 7355505Sshin * Note: If we add a last pointer now we will screw up the 7455505Sshin * tc capability. We really should compile termcap. 7555505Sshin * 7655505Sshin * Essentially all the work here is scanning and decoding escapes 7755505Sshin * in string capabilities. We don't use stdio because the editor 7855505Sshin * doesn't, and because living w/o it is not hard. 7955505Sshin */ 8055505Sshin 8162656Skrisstatic char *tbuf; 8262656Skrisstatic int hopcount; /* detect infinite loops in termcap, init 0 */ 8355505Sshin 84222732Shrsextern const char *conffile; 8555505Sshin 86173412Skevloint tgetent(char *, char *); 87222732Shrsint getent(char *, char *, const char *); 88173412Skevloint tnchktc(void); 89173412Skevloint tnamatch(char *); 90173412Skevlostatic char *tskip(char *); 91173412Skevloint64_t tgetnum(char *); 92173412Skevloint tgetflag(char *); 93173412Skevlochar *tgetstr(char *, char **); 94173412Skevlostatic char *tdecode(char *, char **); 9555505Sshin 9655505Sshin/* 9755505Sshin * Get an entry for terminal name in buffer bp, 9855505Sshin * from the termcap file. Parse is very rudimentary; 9955505Sshin * we just notice escaped newlines. 10055505Sshin */ 10155505Sshinint 102222732Shrstgetent(char *bp, char *name) 10355505Sshin{ 104222732Shrs return (getent(bp, name, conffile)); 10555505Sshin} 10655505Sshin 10755505Sshinint 108222732Shrsgetent(char *bp, char *name, const char *cfile) 10955505Sshin{ 110118662Sume int c; 111118662Sume int i = 0, cnt = 0; 11255505Sshin char ibuf[BUFSIZ]; 113222732Shrs char *cp; 11455505Sshin int tf; 11555505Sshin 11655505Sshin tbuf = bp; 11755505Sshin tf = 0; 11855505Sshin /* 11955505Sshin * TERMCAP can have one of two things in it. It can be the 12055505Sshin * name of a file to use instead of /etc/termcap. In this 12155505Sshin * case it better start with a "/". Or it can be an entry to 12255505Sshin * use so we don't have to read the file. In this case it 12355505Sshin * has to already have the newlines crunched out. 12455505Sshin */ 125222732Shrs if (cfile && *cfile) 126222732Shrs tf = open(cfile, O_RDONLY); 127222732Shrs 12855505Sshin if (tf < 0) { 12962656Skris syslog(LOG_INFO, 130118660Sume "<%s> open: %s", __func__, strerror(errno)); 13155505Sshin return (-2); 13255505Sshin } 13355505Sshin for (;;) { 13455505Sshin cp = bp; 13555505Sshin for (;;) { 13655505Sshin if (i == cnt) { 13755505Sshin cnt = read(tf, ibuf, BUFSIZ); 13855505Sshin if (cnt <= 0) { 13955505Sshin close(tf); 14055505Sshin return (0); 14155505Sshin } 14255505Sshin i = 0; 14355505Sshin } 14455505Sshin c = ibuf[i++]; 14555505Sshin if (c == '\n') { 14655505Sshin if (cp > bp && cp[-1] == '\\') { 14755505Sshin cp--; 14855505Sshin continue; 14955505Sshin } 15055505Sshin break; 15155505Sshin } 152118664Sume if (cp >= bp + BUFSIZ) { 15380381Ssheldonh write(STDERR_FILENO, "Remcap entry too long\n", 15480381Ssheldonh 23); 15555505Sshin break; 15655505Sshin } else 15755505Sshin *cp++ = c; 15855505Sshin } 15955505Sshin *cp = 0; 16055505Sshin 16155505Sshin /* 16255505Sshin * The real work for the match. 16355505Sshin */ 16455505Sshin if (tnamatch(name)) { 16555505Sshin close(tf); 16655505Sshin return (tnchktc()); 16755505Sshin } 16855505Sshin } 16955505Sshin} 17055505Sshin 17155505Sshin/* 17255505Sshin * tnchktc: check the last entry, see if it's tc=xxx. If so, 17355505Sshin * recursively find xxx and append that entry (minus the names) 17455505Sshin * to take the place of the tc=xxx entry. This allows termcap 17555505Sshin * entries to say "like an HP2621 but doesn't turn on the labels". 17655505Sshin * Note that this works because of the left to right scan. 17755505Sshin */ 17855505Sshinint 179222732Shrstnchktc(void) 18055505Sshin{ 181118662Sume char *p, *q; 18255505Sshin char tcname[16]; /* name of similar terminal */ 18355505Sshin char tcbuf[BUFSIZ]; 18455505Sshin char *holdtbuf = tbuf; 18555505Sshin int l; 18655505Sshin 18755505Sshin p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 18855505Sshin while (*--p != ':') 189118664Sume if (p < tbuf) { 19080381Ssheldonh write(STDERR_FILENO, "Bad remcap entry\n", 18); 19155505Sshin return (0); 19255505Sshin } 19355505Sshin p++; 19455505Sshin /* p now points to beginning of last field */ 19555505Sshin if (p[0] != 't' || p[1] != 'c') 19655505Sshin return (1); 197118786Sume strlcpy(tcname, p + 3, sizeof tcname); 19855505Sshin q = tcname; 19955505Sshin while (*q && *q != ':') 20055505Sshin q++; 20155505Sshin *q = 0; 20255505Sshin if (++hopcount > MAXHOP) { 20380381Ssheldonh write(STDERR_FILENO, "Infinite tc= loop\n", 18); 20455505Sshin return (0); 20555505Sshin } 206222824Shrs if (getent(tcbuf, tcname, conffile) != 1) { 20755505Sshin return (0); 20855505Sshin } 20955505Sshin for (q = tcbuf; *q++ != ':'; ) 21055505Sshin ; 21155505Sshin l = p - holdtbuf + strlen(q); 21255505Sshin if (l > BUFSIZ) { 21380381Ssheldonh write(STDERR_FILENO, "Remcap entry too long\n", 23); 21455505Sshin q[BUFSIZ - (p-holdtbuf)] = 0; 21555505Sshin } 21655505Sshin strcpy(p, q); 21755505Sshin tbuf = holdtbuf; 21855505Sshin return (1); 21955505Sshin} 22055505Sshin 22155505Sshin/* 22255505Sshin * Tnamatch deals with name matching. The first field of the termcap 22355505Sshin * entry is a sequence of names separated by |'s, so we compare 22455505Sshin * against each such name. The normal : terminator after the last 22555505Sshin * name (before the first field) stops us. 22655505Sshin */ 22755505Sshinint 228222732Shrstnamatch(char *np) 22955505Sshin{ 230118662Sume char *Np, *Bp; 23155505Sshin 23255505Sshin Bp = tbuf; 23355505Sshin if (*Bp == '#') 23455505Sshin return (0); 23555505Sshin for (;;) { 23655505Sshin for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 23755505Sshin continue; 23855505Sshin if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 23955505Sshin return (1); 24055505Sshin while (*Bp && *Bp != ':' && *Bp != '|') 24155505Sshin Bp++; 24255505Sshin if (*Bp == 0 || *Bp == ':') 24355505Sshin return (0); 24455505Sshin Bp++; 24555505Sshin } 24655505Sshin} 24755505Sshin 24855505Sshin/* 24955505Sshin * Skip to the next field. Notice that this is very dumb, not 25055505Sshin * knowing about \: escapes or any such. If necessary, :'s can be put 25155505Sshin * into the termcap file in octal. 25255505Sshin */ 25355505Sshinstatic char * 254222732Shrstskip(char *bp) 25555505Sshin{ 25655505Sshin int dquote; 25755505Sshin 25855505Sshin dquote = 0; 25955505Sshin while (*bp) { 26055505Sshin switch (*bp) { 26155505Sshin case ':': 26255505Sshin if (!dquote) 26355505Sshin goto breakbreak; 26455505Sshin else 26555505Sshin bp++; 26655505Sshin break; 26755505Sshin case '\\': 26855505Sshin bp++; 26955505Sshin if (isdigit(*bp)) { 27055505Sshin while (isdigit(*bp++)) 27155505Sshin ; 27255505Sshin } else 27355505Sshin bp++; 27455505Sshin case '"': 27555505Sshin dquote = (dquote ? 1 : 0); 27655505Sshin bp++; 27755505Sshin break; 27855505Sshin default: 27955505Sshin bp++; 28055505Sshin break; 28155505Sshin } 28255505Sshin } 28355505Sshinbreakbreak: 28455505Sshin if (*bp == ':') 28555505Sshin bp++; 28655505Sshin return (bp); 28755505Sshin} 28855505Sshin 28955505Sshin/* 29055505Sshin * Return the (numeric) option id. 29155505Sshin * Numeric options look like 29255505Sshin * li#80 29355505Sshin * i.e. the option string is separated from the numeric value by 29455505Sshin * a # character. If the option is not found we return -1. 29555505Sshin * Note that we handle octal numbers beginning with 0. 29655505Sshin */ 297118784Sumeint64_t 298222732Shrstgetnum(char *id) 29955505Sshin{ 300118784Sume int64_t i; 301118662Sume int base; 302118662Sume char *bp = tbuf; 30355505Sshin 30455505Sshin for (;;) { 30555505Sshin bp = tskip(bp); 30655505Sshin if (*bp == 0) 30755505Sshin return (-1); 30855505Sshin if (strncmp(bp, id, strlen(id)) != 0) 30955505Sshin continue; 31055505Sshin bp += strlen(id); 31155505Sshin if (*bp == '@') 31255505Sshin return (-1); 31355505Sshin if (*bp != '#') 31455505Sshin continue; 31555505Sshin bp++; 31655505Sshin base = 10; 31755505Sshin if (*bp == '0') 31855505Sshin base = 8; 31955505Sshin i = 0; 32055505Sshin while (isdigit(*bp)) 32155505Sshin i *= base, i += *bp++ - '0'; 32255505Sshin return (i); 32355505Sshin } 32455505Sshin} 32555505Sshin 32655505Sshin/* 32755505Sshin * Handle a flag option. 32855505Sshin * Flag options are given "naked", i.e. followed by a : or the end 32955505Sshin * of the buffer. Return 1 if we find the option, or 0 if it is 33055505Sshin * not given. 33155505Sshin */ 33255505Sshinint 333222732Shrstgetflag(char *id) 33455505Sshin{ 335118662Sume char *bp = tbuf; 33655505Sshin 33755505Sshin for (;;) { 33855505Sshin bp = tskip(bp); 33955505Sshin if (!*bp) 34055505Sshin return (0); 34155505Sshin if (strncmp(bp, id, strlen(id)) == 0) { 34255505Sshin bp += strlen(id); 34355505Sshin if (!*bp || *bp == ':') 34455505Sshin return (1); 34555505Sshin else if (*bp == '@') 34655505Sshin return (0); 34755505Sshin } 34855505Sshin } 34955505Sshin} 35055505Sshin 35155505Sshin/* 35255505Sshin * Get a string valued option. 35355505Sshin * These are given as 35455505Sshin * cl=^Z 35555505Sshin * Much decoding is done on the strings, and the strings are 35655505Sshin * placed in area, which is a ref parameter which is updated. 35755505Sshin * No checking on area overflow. 35855505Sshin */ 35955505Sshinchar * 360222732Shrstgetstr(char *id, char **area) 36155505Sshin{ 362118662Sume char *bp = tbuf; 36355505Sshin 36455505Sshin for (;;) { 36555505Sshin bp = tskip(bp); 36655505Sshin if (!*bp) 36755505Sshin return (0); 36855505Sshin if (strncmp(bp, id, strlen(id)) != 0) 36955505Sshin continue; 37055505Sshin bp += strlen(id); 37155505Sshin if (*bp == '@') 37255505Sshin return (0); 37355505Sshin if (*bp != '=') 37455505Sshin continue; 37555505Sshin bp++; 37655505Sshin return (tdecode(bp, area)); 37755505Sshin } 37855505Sshin} 37955505Sshin 38055505Sshin/* 38155505Sshin * Tdecode does the grung work to decode the 38255505Sshin * string capability escapes. 38355505Sshin */ 38455505Sshinstatic char * 385222732Shrstdecode(char *str, char **area) 38655505Sshin{ 387118662Sume char *cp; 388118662Sume int c; 389222732Shrs const char *dp; 39055505Sshin int i; 39155505Sshin char term; 39255505Sshin 39355505Sshin term = ':'; 39455505Sshin cp = *area; 39555505Sshinagain: 39655505Sshin if (*str == '"') { 39755505Sshin term = '"'; 39855505Sshin str++; 39955505Sshin } 40055505Sshin while ((c = *str++) && c != term) { 40155505Sshin switch (c) { 40255505Sshin 40355505Sshin case '^': 40455505Sshin c = *str++ & 037; 40555505Sshin break; 40655505Sshin 40755505Sshin case '\\': 40855505Sshin dp = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\""; 40955505Sshin c = *str++; 41055505Sshinnextc: 41155505Sshin if (*dp++ == c) { 41255505Sshin c = *dp++; 41355505Sshin break; 41455505Sshin } 41555505Sshin dp++; 41655505Sshin if (*dp) 41755505Sshin goto nextc; 41855505Sshin if (isdigit(c)) { 41955505Sshin c -= '0', i = 2; 42055505Sshin do 42155505Sshin c <<= 3, c |= *str++ - '0'; 42255505Sshin while (--i && isdigit(*str)); 42355505Sshin } 42455505Sshin break; 42555505Sshin } 42655505Sshin *cp++ = c; 42755505Sshin } 42855505Sshin if (c == term && term != ':') { 42955505Sshin term = ':'; 43055505Sshin goto again; 43155505Sshin } 43255505Sshin *cp++ = 0; 43355505Sshin str = *area; 43455505Sshin *area = cp; 43555505Sshin return (str); 43655505Sshin} 437