jls.c revision 279123
1218893Sdim/*- 2193323Sed * Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org> 3193323Sed * Copyright (c) 2008 Bjoern A. Zeeb <bz@FreeBSD.org> 4193323Sed * Copyright (c) 2009 James Gritton <jamie@FreeBSD.org> 5193323Sed * All rights reserved. 6193323Sed * 7193323Sed * Redistribution and use in source and binary forms, with or without 8193323Sed * modification, are permitted provided that the following conditions 9193323Sed * are met: 10218893Sdim * 1. Redistributions of source code must retain the above copyright 11218893Sdim * notice, this list of conditions and the following disclaimer. 12218893Sdim * 2. Redistributions in binary form must reproduce the above copyright 13193323Sed * notice, this list of conditions and the following disclaimer in the 14193323Sed * documentation and/or other materials provided with the distribution. 15193323Sed * 16193323Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19193323Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21212904Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22193323Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23193323Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24193323Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25218893Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26198090Srdivacky * SUCH DAMAGE. 27193323Sed */ 28199989Srdivacky 29199989Srdivacky#include <sys/cdefs.h> 30218893Sdim__FBSDID("$FreeBSD: head/usr.sbin/jls/jls.c 279123 2015-02-22 00:00:10Z jamie $"); 31199989Srdivacky 32193323Sed#include <sys/param.h> 33199989Srdivacky#include <sys/jail.h> 34193323Sed#include <sys/socket.h> 35198090Srdivacky#include <sys/sysctl.h> 36212904Sdim 37193323Sed#include <arpa/inet.h> 38193323Sed#include <netinet/in.h> 39193323Sed 40193323Sed#include <err.h> 41193323Sed#include <errno.h> 42193323Sed#include <jail.h> 43193323Sed#include <limits.h> 44193323Sed#include <stdio.h> 45193323Sed#include <stdlib.h> 46193323Sed#include <string.h> 47193323Sed#include <unistd.h> 48193323Sed 49193323Sed#define JP_USER 0x01000000 50193323Sed#define JP_OPT 0x02000000 51193323Sed 52193323Sed#define PRINT_DEFAULT 0x01 53193323Sed#define PRINT_HEADER 0x02 54193323Sed#define PRINT_NAMEVAL 0x04 55193323Sed#define PRINT_QUOTED 0x08 56193323Sed#define PRINT_SKIP 0x10 57193323Sed#define PRINT_VERBOSE 0x20 58193323Sed#define PRINT_JAIL_NAME 0x40 59193323Sed 60193323Sedstatic struct jailparam *params; 61193323Sedstatic int *param_parent; 62193323Sedstatic int nparams; 63193323Sed#ifdef INET6 64198892Srdivackystatic int ip6_ok; 65199989Srdivacky#endif 66199989Srdivacky#ifdef INET 67199989Srdivackystatic int ip4_ok; 68199989Srdivacky#endif 69199989Srdivacky 70199989Srdivackystatic int add_param(const char *name, void *value, size_t valuelen, 71193323Sed struct jailparam *source, unsigned flags); 72193323Sedstatic int sort_param(const void *a, const void *b); 73193323Sedstatic char *noname(const char *name); 74193323Sedstatic char *nononame(const char *name); 75193323Sedstatic int print_jail(int pflags, int jflags); 76193323Sedstatic void quoted_print(char *str); 77193323Sed 78193323Sedint 79193323Sedmain(int argc, char **argv) 80199989Srdivacky{ 81193323Sed char *dot, *ep, *jname, *pname; 82193323Sed int c, i, jflags, jid, lastjid, pflags, spc; 83193323Sed 84193323Sed jname = NULL; 85210299Sed pflags = jflags = jid = 0; 86210299Sed while ((c = getopt(argc, argv, "adj:hNnqsv")) >= 0) 87210299Sed switch (c) { 88210299Sed case 'a': 89210299Sed case 'd': 90193323Sed jflags |= JAIL_DYING; 91210299Sed break; 92210299Sed case 'j': 93210299Sed jid = strtoul(optarg, &ep, 10); 94210299Sed if (!jid || *ep) { 95210299Sed jid = 0; 96210299Sed jname = optarg; 97210299Sed } 98210299Sed break; 99210299Sed case 'h': 100218893Sdim pflags = (pflags & ~(PRINT_SKIP | PRINT_VERBOSE)) | 101218893Sdim PRINT_HEADER; 102218893Sdim break; 103193323Sed case 'N': 104193323Sed pflags |= PRINT_JAIL_NAME; 105218893Sdim break; 106218893Sdim case 'n': 107193323Sed pflags = (pflags & ~PRINT_VERBOSE) | PRINT_NAMEVAL; 108198892Srdivacky break; 109193323Sed case 'q': 110193323Sed pflags |= PRINT_QUOTED; 111193323Sed break; 112218893Sdim case 's': 113198090Srdivacky pflags = (pflags & ~(PRINT_HEADER | PRINT_VERBOSE)) | 114199481Srdivacky PRINT_NAMEVAL | PRINT_QUOTED | PRINT_SKIP; 115198090Srdivacky break; 116210299Sed case 'v': 117218893Sdim pflags = (pflags & 118218893Sdim ~(PRINT_HEADER | PRINT_NAMEVAL | PRINT_SKIP)) | 119193323Sed PRINT_VERBOSE; 120193323Sed break; 121193323Sed default: 122193323Sed errx(1, "usage: jls [-dhNnqv] [-j jail] [param ...]"); 123218893Sdim } 124193323Sed 125218893Sdim#ifdef INET6 126193323Sed ip6_ok = feature_present("inet6"); 127193323Sed#endif 128193323Sed#ifdef INET 129218893Sdim ip4_ok = feature_present("inet"); 130218893Sdim#endif 131193323Sed 132193323Sed /* Add the parameters to print. */ 133218893Sdim if (optind == argc) { 134218893Sdim if (pflags & (PRINT_HEADER | PRINT_NAMEVAL)) 135218893Sdim add_param("all", NULL, (size_t)0, NULL, JP_USER); 136218893Sdim else if (pflags & PRINT_VERBOSE) { 137218893Sdim add_param("jid", NULL, (size_t)0, NULL, JP_USER); 138218893Sdim add_param("host.hostname", NULL, (size_t)0, NULL, 139218893Sdim JP_USER); 140193323Sed add_param("path", NULL, (size_t)0, NULL, JP_USER); 141218893Sdim add_param("name", NULL, (size_t)0, NULL, JP_USER); 142218893Sdim add_param("dying", NULL, (size_t)0, NULL, JP_USER); 143218893Sdim add_param("cpuset.id", NULL, (size_t)0, NULL, JP_USER); 144218893Sdim#ifdef INET 145218893Sdim if (ip4_ok) 146218893Sdim add_param("ip4.addr", NULL, (size_t)0, NULL, 147218893Sdim JP_USER); 148193323Sed#endif 149193323Sed#ifdef INET6 150212904Sdim if (ip6_ok) 151212904Sdim add_param("ip6.addr", NULL, (size_t)0, NULL, 152212904Sdim JP_USER | JP_OPT); 153212904Sdim#endif 154212904Sdim } else { 155212904Sdim pflags |= PRINT_DEFAULT; 156212904Sdim if (pflags & PRINT_JAIL_NAME) 157212904Sdim add_param("name", NULL, (size_t)0, NULL, JP_USER); 158212904Sdim else 159212904Sdim add_param("jid", NULL, (size_t)0, NULL, JP_USER); 160212904Sdim#ifdef INET 161212904Sdim if (ip4_ok) 162212904Sdim add_param("ip4.addr", NULL, (size_t)0, NULL, 163212904Sdim JP_USER); 164212904Sdim#endif 165212904Sdim add_param("host.hostname", NULL, (size_t)0, NULL, 166212904Sdim JP_USER); 167212904Sdim add_param("path", NULL, (size_t)0, NULL, JP_USER); 168212904Sdim } 169212904Sdim } else { 170212904Sdim pflags &= ~PRINT_VERBOSE; 171212904Sdim while (optind < argc) 172212904Sdim add_param(argv[optind++], NULL, (size_t)0, NULL, 173212904Sdim JP_USER); 174212904Sdim } 175212904Sdim 176212904Sdim if (pflags & PRINT_SKIP) { 177212904Sdim /* Check for parameters with jailsys parents. */ 178212904Sdim for (i = 0; i < nparams; i++) { 179212904Sdim if ((params[i].jp_flags & JP_USER) && 180212904Sdim (dot = strchr(params[i].jp_name, '.'))) { 181212904Sdim pname = alloca((dot - params[i].jp_name) + 1); 182212904Sdim strlcpy(pname, params[i].jp_name, 183212904Sdim (dot - params[i].jp_name) + 1); 184212904Sdim param_parent[i] = add_param(pname, 185212904Sdim NULL, (size_t)0, NULL, JP_OPT); 186212904Sdim } 187212904Sdim } 188212904Sdim } 189212904Sdim 190212904Sdim /* Add the index key parameters. */ 191212904Sdim if (jid != 0) 192212904Sdim add_param("jid", &jid, sizeof(jid), NULL, 0); 193212904Sdim else if (jname != NULL) 194212904Sdim add_param("name", jname, strlen(jname), NULL, 0); 195212904Sdim else 196212904Sdim add_param("lastjid", &lastjid, sizeof(lastjid), NULL, 0); 197212904Sdim 198212904Sdim /* Print a header line if requested. */ 199212904Sdim if (pflags & PRINT_VERBOSE) 200212904Sdim printf(" JID Hostname Path\n" 201212904Sdim " Name State\n" 202212904Sdim " CPUSetID\n" 203212904Sdim " IP Address(es)\n"); 204212904Sdim else if (pflags & PRINT_DEFAULT) 205212904Sdim if (pflags & PRINT_JAIL_NAME) 206212904Sdim printf(" JID IP Address " 207212904Sdim "Hostname Path\n"); 208212904Sdim else 209212904Sdim printf(" JID IP Address " 210212904Sdim "Hostname Path\n"); 211212904Sdim else if (pflags & PRINT_HEADER) { 212212904Sdim for (i = spc = 0; i < nparams; i++) 213212904Sdim if (params[i].jp_flags & JP_USER) { 214212904Sdim if (spc) 215212904Sdim putchar(' '); 216212904Sdim else 217212904Sdim spc = 1; 218212904Sdim fputs(params[i].jp_name, stdout); 219212904Sdim } 220212904Sdim putchar('\n'); 221212904Sdim } 222212904Sdim 223212904Sdim /* Fetch the jail(s) and print the paramters. */ 224212904Sdim if (jid != 0 || jname != NULL) { 225212904Sdim if (print_jail(pflags, jflags) < 0) 226212904Sdim errx(1, "%s", jail_errmsg); 227218893Sdim } else { 228218893Sdim for (lastjid = 0; 229212904Sdim (lastjid = print_jail(pflags, jflags)) >= 0; ) 230212904Sdim ; 231212904Sdim if (errno != 0 && errno != ENOENT) 232212904Sdim errx(1, "%s", jail_errmsg); 233218893Sdim } 234218893Sdim 235212904Sdim return (0); 236212904Sdim} 237212904Sdim 238212904Sdimstatic int 239212904Sdimadd_param(const char *name, void *value, size_t valuelen, 240212904Sdim struct jailparam *source, unsigned flags) 241212904Sdim{ 242212904Sdim struct jailparam *param, *tparams; 243212904Sdim int i, tnparams; 244212904Sdim 245212904Sdim static int paramlistsize; 246212904Sdim 247212904Sdim /* The pseudo-parameter "all" scans the list of available parameters. */ 248212904Sdim if (!strcmp(name, "all")) { 249212904Sdim tnparams = jailparam_all(&tparams); 250212904Sdim if (tnparams < 0) 251212904Sdim errx(1, "%s", jail_errmsg); 252212904Sdim qsort(tparams, (size_t)tnparams, sizeof(struct jailparam), 253212904Sdim sort_param); 254218893Sdim for (i = 0; i < tnparams; i++) 255212904Sdim add_param(tparams[i].jp_name, NULL, (size_t)0, 256212904Sdim tparams + i, flags); 257212904Sdim free(tparams); 258212904Sdim return -1; 259212904Sdim } 260212904Sdim 261212904Sdim /* Check for repeat parameters. */ 262212904Sdim for (i = 0; i < nparams; i++) 263212904Sdim if (!strcmp(name, params[i].jp_name)) { 264212904Sdim if (value != NULL && jailparam_import_raw(params + i, 265212904Sdim value, valuelen) < 0) 266212904Sdim errx(1, "%s", jail_errmsg); 267212904Sdim params[i].jp_flags |= flags; 268212904Sdim if (source != NULL) 269212904Sdim jailparam_free(source, 1); 270212904Sdim return i; 271212904Sdim } 272212904Sdim 273212904Sdim /* Make sure there is room for the new param record. */ 274212904Sdim if (!nparams) { 275212904Sdim paramlistsize = 32; 276212904Sdim params = malloc(paramlistsize * sizeof(*params)); 277212904Sdim param_parent = malloc(paramlistsize * sizeof(*param_parent)); 278212904Sdim if (params == NULL || param_parent == NULL) 279212904Sdim err(1, "malloc"); 280212904Sdim } else if (nparams >= paramlistsize) { 281212904Sdim paramlistsize *= 2; 282212904Sdim params = realloc(params, paramlistsize * sizeof(*params)); 283218893Sdim param_parent = realloc(param_parent, 284223017Sdim paramlistsize * sizeof(*param_parent)); 285223017Sdim if (params == NULL || param_parent == NULL) 286223017Sdim err(1, "realloc"); 287223017Sdim } 288223017Sdim 289223017Sdim /* Look up the parameter. */ 290223017Sdim param_parent[nparams] = -1; 291223017Sdim param = params + nparams++; 292223017Sdim if (source != NULL) { 293223017Sdim *param = *source; 294223017Sdim param->jp_flags |= flags; 295212904Sdim return param - params; 296212904Sdim } 297223017Sdim if (jailparam_init(param, name) < 0 || 298212904Sdim (value != NULL ? jailparam_import_raw(param, value, valuelen) 299212904Sdim : jailparam_import(param, value)) < 0) { 300212904Sdim if (flags & JP_OPT) { 301212904Sdim nparams--; 302212904Sdim return (-1); 303212904Sdim } 304212904Sdim errx(1, "%s", jail_errmsg); 305212904Sdim } 306212904Sdim param->jp_flags = flags; 307212904Sdim return param - params; 308212904Sdim} 309212904Sdim 310212904Sdimstatic int 311212904Sdimsort_param(const void *a, const void *b) 312212904Sdim{ 313212904Sdim const struct jailparam *parama, *paramb; 314212904Sdim char *ap, *bp; 315212904Sdim 316212904Sdim /* Put top-level parameters first. */ 317212904Sdim parama = a; 318212904Sdim paramb = b; 319212904Sdim ap = strchr(parama->jp_name, '.'); 320212904Sdim bp = strchr(paramb->jp_name, '.'); 321212904Sdim if (ap && !bp) 322212904Sdim return (1); 323212904Sdim if (bp && !ap) 324212904Sdim return (-1); 325212904Sdim return (strcmp(parama->jp_name, paramb->jp_name)); 326212904Sdim} 327212904Sdim 328212904Sdimstatic char * 329212904Sdimnoname(const char *name) 330212904Sdim{ 331212904Sdim char *nname, *p; 332212904Sdim 333212904Sdim nname = malloc(strlen(name) + 3); 334212904Sdim if (nname == NULL) 335212904Sdim err(1, "malloc"); 336212904Sdim p = strrchr(name, '.'); 337212904Sdim if (p != NULL) 338212904Sdim sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1); 339212904Sdim else 340212904Sdim sprintf(nname, "no%s", name); 341212904Sdim return nname; 342212904Sdim} 343212904Sdim 344212904Sdimstatic char * 345212904Sdimnononame(const char *name) 346212904Sdim{ 347212904Sdim char *nname, *p; 348212904Sdim 349212904Sdim p = strrchr(name, '.'); 350212904Sdim if (strncmp(p ? p + 1 : name, "no", 2)) 351212904Sdim return NULL; 352218893Sdim nname = malloc(strlen(name) - 1); 353218893Sdim if (nname == NULL) 354212904Sdim err(1, "malloc"); 355212904Sdim if (p != NULL) 356221345Sdim sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3); 357212904Sdim else 358212904Sdim strcpy(nname, name + 2); 359212904Sdim return nname; 360212904Sdim} 361212904Sdim 362212904Sdimstatic int 363212904Sdimprint_jail(int pflags, int jflags) 364212904Sdim{ 365212904Sdim char *nname; 366212904Sdim char **param_values; 367212904Sdim int i, ai, jid, count, n, spc; 368212904Sdim char ipbuf[INET6_ADDRSTRLEN]; 369212904Sdim 370212904Sdim jid = jailparam_get(params, nparams, jflags); 371212904Sdim if (jid < 0) 372212904Sdim return jid; 373218893Sdim if (pflags & PRINT_VERBOSE) { 374212904Sdim printf("%6d %-29.29s %.74s\n" 375212904Sdim "%6s %-29.29s %.74s\n" 376212904Sdim "%6s %-6d\n", 377212904Sdim *(int *)params[0].jp_value, 378212904Sdim (char *)params[1].jp_value, 379212904Sdim (char *)params[2].jp_value, 380212904Sdim "", 381212904Sdim (char *)params[3].jp_value, 382212904Sdim *(int *)params[4].jp_value ? "DYING" : "ACTIVE", 383212904Sdim "", 384212904Sdim *(int *)params[5].jp_value); 385212904Sdim n = 6; 386212904Sdim#ifdef INET 387212904Sdim if (ip4_ok && !strcmp(params[n].jp_name, "ip4.addr")) { 388212904Sdim count = params[n].jp_valuelen / sizeof(struct in_addr); 389212904Sdim for (ai = 0; ai < count; ai++) 390212904Sdim if (inet_ntop(AF_INET, 391212904Sdim &((struct in_addr *)params[n].jp_value)[ai], 392212904Sdim ipbuf, sizeof(ipbuf)) == NULL) 393212904Sdim err(1, "inet_ntop"); 394212904Sdim else 395212904Sdim printf("%6s %-15.15s\n", "", ipbuf); 396212904Sdim n++; 397212904Sdim } 398212904Sdim#endif 399212904Sdim#ifdef INET6 400212904Sdim if (ip6_ok && !strcmp(params[n].jp_name, "ip6.addr")) { 401212904Sdim count = params[n].jp_valuelen / sizeof(struct in6_addr); 402212904Sdim for (ai = 0; ai < count; ai++) 403212904Sdim if (inet_ntop(AF_INET6, 404212904Sdim &((struct in6_addr *) 405212904Sdim params[n].jp_value)[ai], 406212904Sdim ipbuf, sizeof(ipbuf)) == NULL) 407212904Sdim err(1, "inet_ntop"); 408212904Sdim else 409212904Sdim printf("%6s %s\n", "", ipbuf); 410212904Sdim n++; 411212904Sdim } 412212904Sdim#endif 413212904Sdim } else if (pflags & PRINT_DEFAULT) { 414212904Sdim if (pflags & PRINT_JAIL_NAME) 415212904Sdim printf(" %-15s ", (char *)params[0].jp_value); 416212904Sdim else 417212904Sdim printf("%6d ", *(int *)params[0].jp_value); 418212904Sdim printf("%-15.15s %-29.29s %.74s\n", 419212904Sdim#ifdef INET 420212904Sdim (!ip4_ok || params[1].jp_valuelen == 0) ? "-" 421212904Sdim : inet_ntoa(*(struct in_addr *)params[1].jp_value), 422212904Sdim (char *)params[2-!ip4_ok].jp_value, 423212904Sdim (char *)params[3-!ip4_ok].jp_value); 424212904Sdim#else 425212904Sdim "-", 426212904Sdim (char *)params[1].jp_value, 427210299Sed (char *)params[2].jp_value); 428193323Sed#endif 429193323Sed } else { 430210299Sed param_values = alloca(nparams * sizeof(*param_values)); 431210299Sed for (i = 0; i < nparams; i++) { 432210299Sed if (!(params[i].jp_flags & JP_USER)) 433210299Sed continue; 434210299Sed param_values[i] = jailparam_export(params + i); 435210299Sed if (param_values[i] == NULL) 436210299Sed errx(1, "%s", jail_errmsg); 437210299Sed } 438210299Sed for (i = spc = 0; i < nparams; i++) { 439210299Sed if (!(params[i].jp_flags & JP_USER)) 440210299Sed continue; 441210299Sed if ((pflags & PRINT_SKIP) && 442210299Sed ((!(params[i].jp_ctltype & 443210299Sed (CTLFLAG_WR | CTLFLAG_TUN))) || 444210299Sed (param_parent[i] >= 0 && 445210299Sed *(int *)params[param_parent[i]].jp_value != 446210299Sed JAIL_SYS_NEW))) 447210299Sed continue; 448210299Sed if (spc) 449210299Sed putchar(' '); 450193323Sed else 451218893Sdim spc = 1; 452218893Sdim if (pflags & PRINT_NAMEVAL) { 453193323Sed /* 454223017Sdim * Generally "name=value", but for booleans 455223017Sdim * either "name" or "noname". 456223017Sdim */ 457223017Sdim if (params[i].jp_flags & 458223017Sdim (JP_BOOL | JP_NOBOOL)) { 459223017Sdim if (*(int *)params[i].jp_value) 460223017Sdim printf("%s", params[i].jp_name); 461218893Sdim else { 462218893Sdim nname = (params[i].jp_flags & 463210299Sed JP_NOBOOL) ? 464218893Sdim nononame(params[i].jp_name) 465218893Sdim : noname(params[i].jp_name); 466218893Sdim printf("%s", nname); 467218893Sdim free(nname); 468218893Sdim } 469218893Sdim continue; 470218893Sdim } 471218893Sdim printf("%s=", params[i].jp_name); 472218893Sdim } 473218893Sdim if (params[i].jp_valuelen == 0) { 474223017Sdim if (pflags & PRINT_QUOTED) 475218893Sdim printf("\"\""); 476210299Sed else if (!(pflags & PRINT_NAMEVAL)) 477218893Sdim putchar('-'); 478218893Sdim } else 479223017Sdim quoted_print(param_values[i]); 480198090Srdivacky } 481198090Srdivacky putchar('\n'); 482193323Sed for (i = 0; i < nparams; i++) 483212904Sdim if (params[i].jp_flags & JP_USER) 484218893Sdim free(param_values[i]); 485193323Sed } 486212904Sdim return (jid); 487212904Sdim} 488212904Sdim 489212904Sdimstatic void 490212904Sdimquoted_print(char *str) 491212904Sdim{ 492193323Sed int c, qc; 493193323Sed char *p = str; 494218893Sdim 495193323Sed /* An empty string needs quoting. */ 496212904Sdim if (!*p) { 497212904Sdim fputs("\"\"", stdout); 498212904Sdim return; 499212904Sdim } 500212904Sdim 501212904Sdim /* 502212904Sdim * The value will be surrounded by quotes if it contains spaces 503212904Sdim * or quotes. 504202878Srdivacky */ 505212904Sdim qc = strchr(p, '\'') ? '"' 506212904Sdim : strchr(p, '"') ? '\'' 507212904Sdim : strchr(p, ' ') || strchr(p, '\t') ? '"' 508212904Sdim : 0; 509212904Sdim if (qc) 510202878Srdivacky putchar(qc); 511202878Srdivacky while ((c = *p++)) { 512202878Srdivacky if (c == '\\' || c == qc) 513202878Srdivacky putchar('\\'); 514193323Sed putchar(c); 515223017Sdim } 516223017Sdim if (qc) 517223017Sdim putchar(qc); 518223017Sdim} 519223017Sdim