1/* 2 * Copyright (c) 2009-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29/* $KAME: advcap.c,v 1.11 2003/05/19 09:46:50 keiichi Exp $ */ 30 31/* 32 * Copyright (c) 1983 The Regents of the University of California. 33 * All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 1. Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * 2. Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 3. All advertising materials mentioning features or use of this software 44 * must display the following acknowledgement: 45 * This product includes software developed by the University of 46 * California, Berkeley and its contributors. 47 * 4. Neither the name of the University nor the names of its contributors 48 * may be used to endorse or promote products derived from this software 49 * without specific prior written permission. 50 * 51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61 * SUCH DAMAGE. 62 */ 63 64/* 65 * remcap - routines for dealing with the remote host data base 66 * 67 * derived from termcap 68 */ 69#include <sys/types.h> 70#include <sys/uio.h> 71#include <unistd.h> 72#include <fcntl.h> 73#include <ctype.h> 74#include <stdlib.h> 75#include <stdio.h> 76#include <syslog.h> 77#include <errno.h> 78#include <string.h> 79#include "pathnames.h" 80 81#ifndef BUFSIZ 82#define BUFSIZ 1024 83#endif 84#define MAXHOP 32 /* max number of tc= indirections */ 85 86#define tgetent agetent 87#define tnchktc anchktc 88#define tnamatch anamatch 89#define tgetnum agetnum 90#define tgetflag agetflag 91#define tgetstr agetstr 92 93#if 0 94#define V_TERMCAP "REMOTE" 95#define V_TERM "HOST" 96#endif 97 98char *RM; 99 100/* 101 * termcap - routines for dealing with the terminal capability data base 102 * 103 * BUG: Should use a "last" pointer in tbuf, so that searching 104 * for capabilities alphabetically would not be a n**2/2 105 * process when large numbers of capabilities are given. 106 * Note: If we add a last pointer now we will screw up the 107 * tc capability. We really should compile termcap. 108 * 109 * Essentially all the work here is scanning and decoding escapes 110 * in string capabilities. We don't use stdio because the editor 111 * doesn't, and because living w/o it is not hard. 112 */ 113 114static char *tbuf; 115static int hopcount; /* detect infinite loops in termcap, init 0 */ 116 117static char *remotefile; 118 119extern char *conffile; 120 121int tgetent(char *, char *); 122int getent(char *, char *, char *); 123int tnchktc(void); 124int tnamatch(char *); 125static char *tskip(char *); 126int64_t tgetnum(char *); 127int tgetflag(char *); 128char *tgetstr(char *, char **); 129static char *tdecode(char *, char **); 130 131/* 132 * Get an entry for terminal name in buffer bp, 133 * from the termcap file. Parse is very rudimentary; 134 * we just notice escaped newlines. 135 */ 136int 137tgetent(bp, name) 138 char *bp, *name; 139{ 140 char *cp; 141 142 remotefile = cp = conffile ? conffile : _PATH_RTADVDCONF; 143 return (getent(bp, name, cp)); 144} 145 146int 147getent(bp, name, cp) 148 char *bp, *name, *cp; 149{ 150 int c; 151 int i = 0, cnt = 0; 152 char ibuf[BUFSIZ]; 153 int tf; 154 155 tbuf = bp; 156 tf = 0; 157 /* 158 * TERMCAP can have one of two things in it. It can be the 159 * name of a file to use instead of /etc/termcap. In this 160 * case it better start with a "/". Or it can be an entry to 161 * use so we don't have to read the file. In this case it 162 * has to already have the newlines crunched out. 163 */ 164 if (cp && *cp) { 165 tf = open(RM = cp, O_RDONLY); 166 } 167 if (tf < 0) { 168 syslog(LOG_INFO, 169 "<%s> open: %s", __func__, strerror(errno)); 170 return (-2); 171 } 172 for (;;) { 173 cp = bp; 174 for (;;) { 175 if (i == cnt) { 176 cnt = read(tf, ibuf, BUFSIZ); 177 if (cnt <= 0) { 178 close(tf); 179 return (0); 180 } 181 i = 0; 182 } 183 c = ibuf[i++]; 184 if (c == '\n') { 185 if (cp > bp && cp[-1] == '\\') { 186 cp--; 187 continue; 188 } 189 break; 190 } 191 if (cp >= bp + BUFSIZ) { 192 write(STDERR_FILENO, "Remcap entry too long\n", 193 23); 194 break; 195 } else 196 *cp++ = c; 197 } 198 *cp = 0; 199 200 /* 201 * The real work for the match. 202 */ 203 if (tnamatch(name)) { 204 close(tf); 205 return (tnchktc()); 206 } 207 } 208} 209 210/* 211 * tnchktc: check the last entry, see if it's tc=xxx. If so, 212 * recursively find xxx and append that entry (minus the names) 213 * to take the place of the tc=xxx entry. This allows termcap 214 * entries to say "like an HP2621 but doesn't turn on the labels". 215 * Note that this works because of the left to right scan. 216 */ 217int 218tnchktc() 219{ 220 char *p, *q; 221 char tcname[16]; /* name of similar terminal */ 222 char tcbuf[BUFSIZ]; 223 char *holdtbuf = tbuf; 224 int l; 225 226 p = tbuf + strlen(tbuf) - 2; /* before the last colon */ 227 while (*--p != ':') 228 if (p < tbuf) { 229 write(STDERR_FILENO, "Bad remcap entry\n", 18); 230 return (0); 231 } 232 p++; 233 /* p now points to beginning of last field */ 234 if (p[0] != 't' || p[1] != 'c') 235 return (1); 236 strlcpy(tcname, p + 3, sizeof tcname); 237 q = tcname; 238 while (*q && *q != ':') 239 q++; 240 *q = 0; 241 if (++hopcount > MAXHOP) { 242 write(STDERR_FILENO, "Infinite tc= loop\n", 18); 243 return (0); 244 } 245 if (getent(tcbuf, tcname, remotefile) != 1) { 246 return (0); 247 } 248 for (q = tcbuf; *q++ != ':'; ) 249 ; 250 l = p - holdtbuf + strlen(q); 251 252 /* check length before copying string below */ 253 if (l > BUFSIZ) { 254 write(STDERR_FILENO, "Remcap entry too long\n", 23); 255 q[BUFSIZ - (p-holdtbuf)] = 0; 256 } 257 strlcpy(p, q, p-tbuf); 258 tbuf = holdtbuf; 259 return (1); 260} 261 262/* 263 * Tnamatch deals with name matching. The first field of the termcap 264 * entry is a sequence of names separated by |'s, so we compare 265 * against each such name. The normal : terminator after the last 266 * name (before the first field) stops us. 267 */ 268int 269tnamatch(np) 270 char *np; 271{ 272 char *Np, *Bp; 273 274 Bp = tbuf; 275 if (*Bp == '#') 276 return (0); 277 for (;;) { 278 for (Np = np; *Np && *Bp == *Np; Bp++, Np++) 279 continue; 280 if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0)) 281 return (1); 282 while (*Bp && *Bp != ':' && *Bp != '|') 283 Bp++; 284 if (*Bp == 0 || *Bp == ':') 285 return (0); 286 Bp++; 287 } 288} 289 290/* 291 * Skip to the next field. Notice that this is very dumb, not 292 * knowing about \: escapes or any such. If necessary, :'s can be put 293 * into the termcap file in octal. 294 */ 295static char * 296tskip(bp) 297 char *bp; 298{ 299 int dquote; 300 301 dquote = 0; 302 while (*bp) { 303 switch (*bp) { 304 case ':': 305 if (!dquote) 306 goto breakbreak; 307 else 308 bp++; 309 break; 310 case '\\': 311 bp++; 312 if (isdigit(*bp)) { 313 while (isdigit(*bp++)) 314 ; 315 } else 316 bp++; 317 case '"': 318 dquote = (dquote ? 1 : 0); 319 bp++; 320 break; 321 default: 322 bp++; 323 break; 324 } 325 } 326breakbreak: 327 if (*bp == ':') 328 bp++; 329 return (bp); 330} 331 332/* 333 * Return the (numeric) option id. 334 * Numeric options look like 335 * li#80 336 * i.e. the option string is separated from the numeric value by 337 * a # character. If the option is not found we return -1. 338 * Note that we handle octal numbers beginning with 0. 339 */ 340int64_t 341tgetnum(id) 342 char *id; 343{ 344 int64_t i; 345 int base; 346 char *bp = tbuf; 347 348 for (;;) { 349 bp = tskip(bp); 350 if (*bp == 0) 351 return (-1); 352 if (strncmp(bp, id, strlen(id)) != 0) 353 continue; 354 bp += strlen(id); 355 if (*bp == '@') 356 return (-1); 357 if (*bp != '#') 358 continue; 359 bp++; 360 base = 10; 361 if (*bp == '0') 362 base = 8; 363 i = 0; 364 while (isdigit(*bp)) 365 i *= base, i += *bp++ - '0'; 366 return (i); 367 } 368} 369 370/* 371 * Handle a flag option. 372 * Flag options are given "naked", i.e. followed by a : or the end 373 * of the buffer. Return 1 if we find the option, or 0 if it is 374 * not given. 375 */ 376int 377tgetflag(id) 378 char *id; 379{ 380 char *bp = tbuf; 381 382 for (;;) { 383 bp = tskip(bp); 384 if (!*bp) 385 return (0); 386 if (strncmp(bp, id, strlen(id)) == 0) { 387 bp += strlen(id); 388 if (!*bp || *bp == ':') 389 return (1); 390 else if (*bp == '@') 391 return (0); 392 } 393 } 394} 395 396/* 397 * Get a string valued option. 398 * These are given as 399 * cl=^Z 400 * Much decoding is done on the strings, and the strings are 401 * placed in area, which is a ref parameter which is updated. 402 * No checking on area overflow. 403 */ 404char * 405tgetstr(id, area) 406 char *id, **area; 407{ 408 char *bp = tbuf; 409 410 for (;;) { 411 bp = tskip(bp); 412 if (!*bp) 413 return (0); 414 if (strncmp(bp, id, strlen(id)) != 0) 415 continue; 416 bp += strlen(id); 417 if (*bp == '@') 418 return (0); 419 if (*bp != '=') 420 continue; 421 bp++; 422 return (tdecode(bp, area)); 423 } 424} 425 426/* 427 * Tdecode does the grung work to decode the 428 * string capability escapes. 429 */ 430static char * 431tdecode(str, area) 432 char *str; 433 char **area; 434{ 435 char *cp; 436 int c; 437 char *dp; 438 int i; 439 char term; 440 441 term = ':'; 442 cp = *area; 443again: 444 if (*str == '"') { 445 term = '"'; 446 str++; 447 } 448 while ((c = *str++) && c != term) { 449 switch (c) { 450 451 case '^': 452 c = *str++ & 037; 453 break; 454 455 case '\\': 456 dp = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\""; 457 c = *str++; 458nextc: 459 if (*dp++ == c) { 460 c = *dp++; 461 break; 462 } 463 dp++; 464 if (*dp) 465 goto nextc; 466 if (isdigit(c)) { 467 c -= '0', i = 2; 468 do 469 c <<= 3, c |= *str++ - '0'; 470 while (--i && isdigit(*str)); 471 } 472 break; 473 } 474 *cp++ = c; 475 } 476 if (c == term && term != ':') { 477 term = ':'; 478 goto again; 479 } 480 *cp++ = 0; 481 str = *area; 482 *area = cp; 483 return (str); 484} 485