1113277Smike/*- 2113277Smike * Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org> 3185435Sbz * Copyright (c) 2008 Bjoern A. Zeeb <bz@FreeBSD.org> 4192896Sjamie * Copyright (c) 2009 James Gritton <jamie@FreeBSD.org> 5113277Smike * All rights reserved. 6113277Smike * 7113277Smike * Redistribution and use in source and binary forms, with or without 8113277Smike * modification, are permitted provided that the following conditions 9113277Smike * are met: 10113277Smike * 1. Redistributions of source code must retain the above copyright 11113277Smike * notice, this list of conditions and the following disclaimer. 12113277Smike * 2. Redistributions in binary form must reproduce the above copyright 13113277Smike * notice, this list of conditions and the following disclaimer in the 14113277Smike * documentation and/or other materials provided with the distribution. 15113277Smike * 16113277Smike * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17113277Smike * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18113277Smike * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19113277Smike * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20113277Smike * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21113277Smike * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22113277Smike * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23113277Smike * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24113277Smike * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25113277Smike * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26113277Smike * SUCH DAMAGE. 27113277Smike */ 28113277Smike 29192896Sjamie#include <sys/cdefs.h> 30192896Sjamie__FBSDID("$FreeBSD$"); 31192896Sjamie 32113277Smike#include <sys/param.h> 33113277Smike#include <sys/jail.h> 34192896Sjamie#include <sys/socket.h> 35113277Smike#include <sys/sysctl.h> 36113277Smike 37192896Sjamie#include <arpa/inet.h> 38185435Sbz#include <netinet/in.h> 39192896Sjamie 40113277Smike#include <err.h> 41113277Smike#include <errno.h> 42194869Sjamie#include <jail.h> 43113277Smike#include <limits.h> 44113277Smike#include <stdio.h> 45113277Smike#include <stdlib.h> 46185435Sbz#include <string.h> 47185435Sbz#include <unistd.h> 48113277Smike 49194869Sjamie#define JP_USER 0x01000000 50194869Sjamie#define JP_OPT 0x02000000 51113277Smike 52192896Sjamie#define PRINT_DEFAULT 0x01 53192896Sjamie#define PRINT_HEADER 0x02 54192896Sjamie#define PRINT_NAMEVAL 0x04 55192896Sjamie#define PRINT_QUOTED 0x08 56192896Sjamie#define PRINT_SKIP 0x10 57192896Sjamie#define PRINT_VERBOSE 0x20 58250736Sdes#define PRINT_JAIL_NAME 0x40 59192896Sjamie 60194869Sjamiestatic struct jailparam *params; 61195870Sjamiestatic int *param_parent; 62192896Sjamiestatic int nparams; 63222465Sbz#ifdef INET6 64222465Sbzstatic int ip6_ok; 65222465Sbz#endif 66222465Sbz#ifdef INET 67222465Sbzstatic int ip4_ok; 68222465Sbz#endif 69192896Sjamie 70194869Sjamiestatic int add_param(const char *name, void *value, size_t valuelen, 71194869Sjamie struct jailparam *source, unsigned flags); 72192896Sjamiestatic int sort_param(const void *a, const void *b); 73192896Sjamiestatic char *noname(const char *name); 74192896Sjamiestatic char *nononame(const char *name); 75192896Sjamiestatic int print_jail(int pflags, int jflags); 76194869Sjamiestatic void quoted_print(char *str); 77192896Sjamie 78192896Sjamieint 79192896Sjamiemain(int argc, char **argv) 80185435Sbz{ 81195870Sjamie char *dot, *ep, *jname; 82192896Sjamie int c, i, jflags, jid, lastjid, pflags, spc; 83185435Sbz 84192896Sjamie jname = NULL; 85192896Sjamie pflags = jflags = jid = 0; 86250736Sdes while ((c = getopt(argc, argv, "adj:hNnqsv")) >= 0) 87192896Sjamie switch (c) { 88192896Sjamie case 'a': 89192896Sjamie case 'd': 90192896Sjamie jflags |= JAIL_DYING; 91192896Sjamie break; 92192896Sjamie case 'j': 93192896Sjamie jid = strtoul(optarg, &ep, 10); 94209820Sjamie if (!jid || *ep) { 95209820Sjamie jid = 0; 96192896Sjamie jname = optarg; 97209820Sjamie } 98192896Sjamie break; 99192896Sjamie case 'h': 100195462Sjamie pflags = (pflags & ~(PRINT_SKIP | PRINT_VERBOSE)) | 101195462Sjamie PRINT_HEADER; 102192896Sjamie break; 103250736Sdes case 'N': 104250736Sdes pflags |= PRINT_JAIL_NAME; 105250736Sdes break; 106192896Sjamie case 'n': 107192896Sjamie pflags = (pflags & ~PRINT_VERBOSE) | PRINT_NAMEVAL; 108192896Sjamie break; 109192896Sjamie case 'q': 110192896Sjamie pflags |= PRINT_QUOTED; 111192896Sjamie break; 112192896Sjamie case 's': 113192896Sjamie pflags = (pflags & ~(PRINT_HEADER | PRINT_VERBOSE)) | 114192896Sjamie PRINT_NAMEVAL | PRINT_QUOTED | PRINT_SKIP; 115192896Sjamie break; 116192896Sjamie case 'v': 117195462Sjamie pflags = (pflags & 118195462Sjamie ~(PRINT_HEADER | PRINT_NAMEVAL | PRINT_SKIP)) | 119192896Sjamie PRINT_VERBOSE; 120192896Sjamie break; 121192896Sjamie default: 122250736Sdes errx(1, "usage: jls [-dhNnqv] [-j jail] [param ...]"); 123192896Sjamie } 124185435Sbz 125222465Sbz#ifdef INET6 126222465Sbz ip6_ok = feature_present("inet6"); 127222465Sbz#endif 128222465Sbz#ifdef INET 129222465Sbz ip4_ok = feature_present("inet"); 130222465Sbz#endif 131222465Sbz 132192896Sjamie /* Add the parameters to print. */ 133192896Sjamie if (optind == argc) { 134195462Sjamie if (pflags & (PRINT_HEADER | PRINT_NAMEVAL)) 135195462Sjamie add_param("all", NULL, (size_t)0, NULL, JP_USER); 136195462Sjamie else if (pflags & PRINT_VERBOSE) { 137194869Sjamie add_param("jid", NULL, (size_t)0, NULL, JP_USER); 138194869Sjamie add_param("host.hostname", NULL, (size_t)0, NULL, 139194869Sjamie JP_USER); 140194869Sjamie add_param("path", NULL, (size_t)0, NULL, JP_USER); 141194869Sjamie add_param("name", NULL, (size_t)0, NULL, JP_USER); 142194869Sjamie add_param("dying", NULL, (size_t)0, NULL, JP_USER); 143194869Sjamie add_param("cpuset.id", NULL, (size_t)0, NULL, JP_USER); 144222465Sbz#ifdef INET 145222465Sbz if (ip4_ok) 146222465Sbz add_param("ip4.addr", NULL, (size_t)0, NULL, 147222465Sbz JP_USER); 148222465Sbz#endif 149222465Sbz#ifdef INET6 150222465Sbz if (ip6_ok) 151222465Sbz add_param("ip6.addr", NULL, (size_t)0, NULL, 152222465Sbz JP_USER | JP_OPT); 153222465Sbz#endif 154192896Sjamie } else { 155195462Sjamie pflags |= PRINT_DEFAULT; 156250736Sdes if (pflags & PRINT_JAIL_NAME) 157250736Sdes add_param("name", NULL, (size_t)0, NULL, JP_USER); 158250736Sdes else 159250736Sdes add_param("jid", NULL, (size_t)0, NULL, JP_USER); 160222465Sbz#ifdef INET 161222465Sbz if (ip4_ok) 162222465Sbz add_param("ip4.addr", NULL, (size_t)0, NULL, 163222465Sbz JP_USER); 164222465Sbz#endif 165194869Sjamie add_param("host.hostname", NULL, (size_t)0, NULL, 166194869Sjamie JP_USER); 167194869Sjamie add_param("path", NULL, (size_t)0, NULL, JP_USER); 168192896Sjamie } 169192896Sjamie } else 170192896Sjamie while (optind < argc) 171194869Sjamie add_param(argv[optind++], NULL, (size_t)0, NULL, 172194869Sjamie JP_USER); 173192896Sjamie 174192896Sjamie if (pflags & PRINT_SKIP) { 175195870Sjamie /* Check for parameters with jailsys parents. */ 176192896Sjamie for (i = 0; i < nparams; i++) { 177194869Sjamie if ((params[i].jp_flags & JP_USER) && 178194869Sjamie (dot = strchr(params[i].jp_name, '.'))) { 179192896Sjamie *dot = 0; 180195870Sjamie param_parent[i] = add_param(params[i].jp_name, 181195870Sjamie NULL, (size_t)0, NULL, JP_OPT); 182192896Sjamie *dot = '.'; 183192896Sjamie } 184192896Sjamie } 185192896Sjamie } 186192896Sjamie 187194869Sjamie /* Add the index key parameters. */ 188192896Sjamie if (jid != 0) 189194869Sjamie add_param("jid", &jid, sizeof(jid), NULL, 0); 190192896Sjamie else if (jname != NULL) 191194869Sjamie add_param("name", jname, strlen(jname), NULL, 0); 192192896Sjamie else 193194869Sjamie add_param("lastjid", &lastjid, sizeof(lastjid), NULL, 0); 194192896Sjamie 195192896Sjamie /* Print a header line if requested. */ 196192896Sjamie if (pflags & PRINT_VERBOSE) 197192896Sjamie printf(" JID Hostname Path\n" 198192896Sjamie " Name State\n" 199192896Sjamie " CPUSetID\n" 200192896Sjamie " IP Address(es)\n"); 201192896Sjamie else if (pflags & PRINT_DEFAULT) 202250736Sdes if (pflags & PRINT_JAIL_NAME) 203250736Sdes printf(" JID IP Address " 204250736Sdes "Hostname Path\n"); 205250736Sdes else 206250736Sdes printf(" JID IP Address " 207250736Sdes "Hostname Path\n"); 208192896Sjamie else if (pflags & PRINT_HEADER) { 209192896Sjamie for (i = spc = 0; i < nparams; i++) 210194869Sjamie if (params[i].jp_flags & JP_USER) { 211192896Sjamie if (spc) 212192896Sjamie putchar(' '); 213192896Sjamie else 214192896Sjamie spc = 1; 215194869Sjamie fputs(params[i].jp_name, stdout); 216192896Sjamie } 217192896Sjamie putchar('\n'); 218192896Sjamie } 219192896Sjamie 220192896Sjamie /* Fetch the jail(s) and print the paramters. */ 221192896Sjamie if (jid != 0 || jname != NULL) { 222194869Sjamie if (print_jail(pflags, jflags) < 0) 223194869Sjamie errx(1, "%s", jail_errmsg); 224186085Sbz } else { 225192896Sjamie for (lastjid = 0; 226192896Sjamie (lastjid = print_jail(pflags, jflags)) >= 0; ) 227192896Sjamie ; 228194869Sjamie if (errno != 0 && errno != ENOENT) 229194869Sjamie errx(1, "%s", jail_errmsg); 230186085Sbz } 231185435Sbz 232192896Sjamie return (0); 233185435Sbz} 234185435Sbz 235192896Sjamiestatic int 236194869Sjamieadd_param(const char *name, void *value, size_t valuelen, 237194869Sjamie struct jailparam *source, unsigned flags) 238185435Sbz{ 239194869Sjamie struct jailparam *param, *tparams; 240192896Sjamie int i, tnparams; 241185435Sbz 242192896Sjamie static int paramlistsize; 243185435Sbz 244192896Sjamie /* The pseudo-parameter "all" scans the list of available parameters. */ 245192896Sjamie if (!strcmp(name, "all")) { 246194869Sjamie tnparams = jailparam_all(&tparams); 247194869Sjamie if (tnparams < 0) 248194869Sjamie errx(1, "%s", jail_errmsg); 249194869Sjamie qsort(tparams, (size_t)tnparams, sizeof(struct jailparam), 250194869Sjamie sort_param); 251194869Sjamie for (i = 0; i < tnparams; i++) 252194869Sjamie add_param(tparams[i].jp_name, NULL, (size_t)0, 253194869Sjamie tparams + i, flags); 254194869Sjamie free(tparams); 255192896Sjamie return -1; 256185435Sbz } 257185435Sbz 258192896Sjamie /* Check for repeat parameters. */ 259192896Sjamie for (i = 0; i < nparams; i++) 260194869Sjamie if (!strcmp(name, params[i].jp_name)) { 261194869Sjamie if (value != NULL && jailparam_import_raw(params + i, 262194869Sjamie value, valuelen) < 0) 263194869Sjamie errx(1, "%s", jail_errmsg); 264194869Sjamie params[i].jp_flags |= flags; 265194869Sjamie if (source != NULL) 266194869Sjamie jailparam_free(source, 1); 267192896Sjamie return i; 268192896Sjamie } 269185435Sbz 270192896Sjamie /* Make sure there is room for the new param record. */ 271192896Sjamie if (!nparams) { 272192896Sjamie paramlistsize = 32; 273192896Sjamie params = malloc(paramlistsize * sizeof(*params)); 274195870Sjamie param_parent = malloc(paramlistsize * sizeof(*param_parent)); 275195870Sjamie if (params == NULL || param_parent == NULL) 276192896Sjamie err(1, "malloc"); 277192896Sjamie } else if (nparams >= paramlistsize) { 278192896Sjamie paramlistsize *= 2; 279192896Sjamie params = realloc(params, paramlistsize * sizeof(*params)); 280195870Sjamie param_parent = realloc(param_parent, 281195870Sjamie paramlistsize * sizeof(*param_parent)); 282195870Sjamie if (params == NULL || param_parent == NULL) 283192896Sjamie err(1, "realloc"); 284192896Sjamie } 285185435Sbz 286192896Sjamie /* Look up the parameter. */ 287195870Sjamie param_parent[nparams] = -1; 288192896Sjamie param = params + nparams++; 289194869Sjamie if (source != NULL) { 290194869Sjamie *param = *source; 291194869Sjamie param->jp_flags |= flags; 292194869Sjamie return param - params; 293192896Sjamie } 294194869Sjamie if (jailparam_init(param, name) < 0) 295194869Sjamie errx(1, "%s", jail_errmsg); 296194869Sjamie param->jp_flags = flags; 297194869Sjamie if ((value != NULL ? jailparam_import_raw(param, value, valuelen) 298194869Sjamie : jailparam_import(param, value)) < 0) { 299194869Sjamie if (flags & JP_OPT) { 300192896Sjamie nparams--; 301194869Sjamie return (-1); 302192896Sjamie } 303194869Sjamie errx(1, "%s", jail_errmsg); 304192896Sjamie } 305192896Sjamie return param - params; 306192896Sjamie} 307192896Sjamie 308192896Sjamiestatic int 309192896Sjamiesort_param(const void *a, const void *b) 310192896Sjamie{ 311194869Sjamie const struct jailparam *parama, *paramb; 312192896Sjamie char *ap, *bp; 313186085Sbz 314192896Sjamie /* Put top-level parameters first. */ 315192896Sjamie parama = a; 316192896Sjamie paramb = b; 317194869Sjamie ap = strchr(parama->jp_name, '.'); 318194869Sjamie bp = strchr(paramb->jp_name, '.'); 319192896Sjamie if (ap && !bp) 320192896Sjamie return (1); 321192896Sjamie if (bp && !ap) 322192896Sjamie return (-1); 323194869Sjamie return (strcmp(parama->jp_name, paramb->jp_name)); 324185435Sbz} 325185435Sbz 326192896Sjamiestatic char * 327192896Sjamienoname(const char *name) 328185435Sbz{ 329192896Sjamie char *nname, *p; 330185435Sbz 331192896Sjamie nname = malloc(strlen(name) + 3); 332192896Sjamie if (nname == NULL) 333192896Sjamie err(1, "malloc"); 334192896Sjamie p = strrchr(name, '.'); 335192896Sjamie if (p != NULL) 336192896Sjamie sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1); 337192896Sjamie else 338192896Sjamie sprintf(nname, "no%s", name); 339192896Sjamie return nname; 340185435Sbz} 341185435Sbz 342192896Sjamiestatic char * 343192896Sjamienononame(const char *name) 344192896Sjamie{ 345192896Sjamie char *nname, *p; 346113277Smike 347192896Sjamie p = strrchr(name, '.'); 348192896Sjamie if (strncmp(p ? p + 1 : name, "no", 2)) 349192896Sjamie return NULL; 350192896Sjamie nname = malloc(strlen(name) - 1); 351192896Sjamie if (nname == NULL) 352192896Sjamie err(1, "malloc"); 353192896Sjamie if (p != NULL) 354192896Sjamie sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3); 355192896Sjamie else 356192896Sjamie strcpy(nname, name + 2); 357192896Sjamie return nname; 358192896Sjamie} 359185435Sbz 360192896Sjamiestatic int 361192896Sjamieprint_jail(int pflags, int jflags) 362192896Sjamie{ 363192896Sjamie char *nname; 364194869Sjamie char **param_values; 365222465Sbz int i, ai, jid, count, n, spc; 366192896Sjamie char ipbuf[INET6_ADDRSTRLEN]; 367113277Smike 368194869Sjamie jid = jailparam_get(params, nparams, jflags); 369194869Sjamie if (jid < 0) 370194869Sjamie return jid; 371192896Sjamie if (pflags & PRINT_VERBOSE) { 372192896Sjamie printf("%6d %-29.29s %.74s\n" 373192896Sjamie "%6s %-29.29s %.74s\n" 374192896Sjamie "%6s %-6d\n", 375194869Sjamie *(int *)params[0].jp_value, 376194869Sjamie (char *)params[1].jp_value, 377194869Sjamie (char *)params[2].jp_value, 378192896Sjamie "", 379194869Sjamie (char *)params[3].jp_value, 380194869Sjamie *(int *)params[4].jp_value ? "DYING" : "ACTIVE", 381192896Sjamie "", 382194869Sjamie *(int *)params[5].jp_value); 383222465Sbz n = 6; 384222465Sbz#ifdef INET 385224841Sbz if (ip4_ok && !strcmp(params[n].jp_name, "ip4.addr")) { 386222465Sbz count = params[n].jp_valuelen / sizeof(struct in_addr); 387192896Sjamie for (ai = 0; ai < count; ai++) 388222465Sbz if (inet_ntop(AF_INET, 389222465Sbz &((struct in_addr *)params[n].jp_value)[ai], 390222465Sbz ipbuf, sizeof(ipbuf)) == NULL) 391222465Sbz err(1, "inet_ntop"); 392222465Sbz else 393222465Sbz printf("%6s %-15.15s\n", "", ipbuf); 394222465Sbz n++; 395222465Sbz } 396222465Sbz#endif 397222465Sbz#ifdef INET6 398222465Sbz if (ip6_ok && !strcmp(params[n].jp_name, "ip6.addr")) { 399222465Sbz count = params[n].jp_valuelen / sizeof(struct in6_addr); 400222465Sbz for (ai = 0; ai < count; ai++) 401194869Sjamie if (inet_ntop(AF_INET6, 402222465Sbz &((struct in6_addr *) 403222465Sbz params[n].jp_value)[ai], 404192896Sjamie ipbuf, sizeof(ipbuf)) == NULL) 405192896Sjamie err(1, "inet_ntop"); 406192896Sjamie else 407196137Sbz printf("%6s %s\n", "", ipbuf); 408222465Sbz n++; 409192896Sjamie } 410222465Sbz#endif 411250736Sdes } else if (pflags & PRINT_DEFAULT) { 412250736Sdes if (pflags & PRINT_JAIL_NAME) 413250736Sdes printf(" %-15s ", (char *)params[0].jp_value); 414250736Sdes else 415250736Sdes printf("%6d ", *(int *)params[0].jp_value); 416250736Sdes printf("%-15.15s %-29.29s %.74s\n", 417222465Sbz#ifdef INET 418222465Sbz (!ip4_ok || params[1].jp_valuelen == 0) ? "-" 419194869Sjamie : inet_ntoa(*(struct in_addr *)params[1].jp_value), 420232613Sbz (char *)params[2-!ip4_ok].jp_value, 421232613Sbz (char *)params[3-!ip4_ok].jp_value); 422222465Sbz#else 423223224Sbz "-", 424232613Sbz (char *)params[1].jp_value, 425232613Sbz (char *)params[2].jp_value); 426222465Sbz#endif 427250736Sdes } else { 428194869Sjamie param_values = alloca(nparams * sizeof(*param_values)); 429194869Sjamie for (i = 0; i < nparams; i++) { 430194869Sjamie if (!(params[i].jp_flags & JP_USER)) 431194869Sjamie continue; 432194869Sjamie param_values[i] = jailparam_export(params + i); 433194869Sjamie if (param_values[i] == NULL) 434194869Sjamie errx(1, "%s", jail_errmsg); 435194869Sjamie } 436192896Sjamie for (i = spc = 0; i < nparams; i++) { 437194869Sjamie if (!(params[i].jp_flags & JP_USER)) 438149081Spjd continue; 439192896Sjamie if ((pflags & PRINT_SKIP) && 440194869Sjamie ((!(params[i].jp_ctltype & 441194869Sjamie (CTLFLAG_WR | CTLFLAG_TUN))) || 442195870Sjamie (param_parent[i] >= 0 && 443195870Sjamie *(int *)params[param_parent[i]].jp_value != 444195870Sjamie JAIL_SYS_NEW))) 445192896Sjamie continue; 446192896Sjamie if (spc) 447192896Sjamie putchar(' '); 448192896Sjamie else 449192896Sjamie spc = 1; 450192896Sjamie if (pflags & PRINT_NAMEVAL) { 451192896Sjamie /* 452192896Sjamie * Generally "name=value", but for booleans 453192896Sjamie * either "name" or "noname". 454192896Sjamie */ 455194869Sjamie if (params[i].jp_flags & 456194869Sjamie (JP_BOOL | JP_NOBOOL)) { 457194869Sjamie if (*(int *)params[i].jp_value) 458194869Sjamie printf("%s", params[i].jp_name); 459192896Sjamie else { 460194869Sjamie nname = (params[i].jp_flags & 461194869Sjamie JP_NOBOOL) ? 462194869Sjamie nononame(params[i].jp_name) 463194869Sjamie : noname(params[i].jp_name); 464192896Sjamie printf("%s", nname); 465192896Sjamie free(nname); 466192896Sjamie } 467194869Sjamie continue; 468192896Sjamie } 469194869Sjamie printf("%s=", params[i].jp_name); 470149081Spjd } 471194869Sjamie if (params[i].jp_valuelen == 0) { 472192896Sjamie if (pflags & PRINT_QUOTED) 473192896Sjamie printf("\"\""); 474192896Sjamie else if (!(pflags & PRINT_NAMEVAL)) 475192896Sjamie putchar('-'); 476194869Sjamie } else 477194869Sjamie quoted_print(param_values[i]); 478113277Smike } 479192896Sjamie putchar('\n'); 480194869Sjamie for (i = 0; i < nparams; i++) 481194869Sjamie if (params[i].jp_flags & JP_USER) 482194869Sjamie free(param_values[i]); 483149081Spjd } 484192896Sjamie return (jid); 485192896Sjamie} 486113277Smike 487192896Sjamiestatic void 488194869Sjamiequoted_print(char *str) 489192896Sjamie{ 490192896Sjamie int c, qc; 491192896Sjamie char *p = str; 492192896Sjamie 493192896Sjamie /* An empty string needs quoting. */ 494192896Sjamie if (!*p) { 495192896Sjamie fputs("\"\"", stdout); 496192896Sjamie return; 497113277Smike } 498192896Sjamie 499192896Sjamie /* 500192896Sjamie * The value will be surrounded by quotes if it contains spaces 501192896Sjamie * or quotes. 502192896Sjamie */ 503192896Sjamie qc = strchr(p, '\'') ? '"' 504192896Sjamie : strchr(p, '"') ? '\'' 505192896Sjamie : strchr(p, ' ') || strchr(p, '\t') ? '"' 506192896Sjamie : 0; 507192896Sjamie if (qc) 508192896Sjamie putchar(qc); 509194869Sjamie while ((c = *p++)) { 510192896Sjamie if (c == '\\' || c == qc) 511192896Sjamie putchar('\\'); 512192896Sjamie putchar(c); 513185435Sbz } 514192896Sjamie if (qc) 515192896Sjamie putchar(qc); 516113277Smike} 517