1eda14cbcSMatt Macy /* 2eda14cbcSMatt Macy * This file and its contents are supplied under the terms of the 3eda14cbcSMatt Macy * Common Development and Distribution License ("CDDL"), version 1.0. 4eda14cbcSMatt Macy * You may only use this file in accordance with the terms of version 5eda14cbcSMatt Macy * 1.0 of the CDDL. 6eda14cbcSMatt Macy * 7eda14cbcSMatt Macy * A full copy of the text of the CDDL should have accompanied this 8eda14cbcSMatt Macy * source. A copy of the CDDL is also available via the Internet at 9eda14cbcSMatt Macy * http://www.illumos.org/license/CDDL. 10eda14cbcSMatt Macy */ 11eda14cbcSMatt Macy /* 12eda14cbcSMatt Macy * Copyright (c) 2014, Joyent, Inc. 13eda14cbcSMatt Macy * Copyright (c) 2017 by Delphix. All rights reserved. 14eda14cbcSMatt Macy */ 15eda14cbcSMatt Macy 16eda14cbcSMatt Macy #include <stdio.h> 17eda14cbcSMatt Macy #include <stdlib.h> 18eda14cbcSMatt Macy #include <strings.h> 19eda14cbcSMatt Macy #include <wchar.h> 20eda14cbcSMatt Macy #include <sys/debug.h> 21eda14cbcSMatt Macy 22eda14cbcSMatt Macy #include "libnvpair.h" 23eda14cbcSMatt Macy 24eda14cbcSMatt Macy #define FPRINTF(fp, ...) \ 25eda14cbcSMatt Macy do { \ 26eda14cbcSMatt Macy if (fprintf(fp, __VA_ARGS__) < 0) \ 27eda14cbcSMatt Macy return (-1); \ 28eda14cbcSMatt Macy } while (0) 29eda14cbcSMatt Macy 30eda14cbcSMatt Macy /* 31eda14cbcSMatt Macy * When formatting a string for JSON output we must escape certain characters, 32eda14cbcSMatt Macy * as described in RFC4627. This applies to both member names and 33eda14cbcSMatt Macy * DATA_TYPE_STRING values. 34eda14cbcSMatt Macy * 35eda14cbcSMatt Macy * This function will only operate correctly if the following conditions are 36eda14cbcSMatt Macy * met: 37eda14cbcSMatt Macy * 38eda14cbcSMatt Macy * 1. The input String is encoded in the current locale. 39eda14cbcSMatt Macy * 40eda14cbcSMatt Macy * 2. The current locale includes the Basic Multilingual Plane (plane 0) 41eda14cbcSMatt Macy * as defined in the Unicode standard. 42eda14cbcSMatt Macy * 43eda14cbcSMatt Macy * The output will be entirely 7-bit ASCII (as a subset of UTF-8) with all 44eda14cbcSMatt Macy * representable Unicode characters included in their escaped numeric form. 45eda14cbcSMatt Macy */ 46eda14cbcSMatt Macy static int 47eda14cbcSMatt Macy nvlist_print_json_string(FILE *fp, const char *input) 48eda14cbcSMatt Macy { 49eda14cbcSMatt Macy mbstate_t mbr; 50eda14cbcSMatt Macy wchar_t c; 51eda14cbcSMatt Macy size_t sz; 52eda14cbcSMatt Macy 53eda14cbcSMatt Macy bzero(&mbr, sizeof (mbr)); 54eda14cbcSMatt Macy 55eda14cbcSMatt Macy FPRINTF(fp, "\""); 56eda14cbcSMatt Macy while ((sz = mbrtowc(&c, input, MB_CUR_MAX, &mbr)) > 0) { 57*16038816SMartin Matuska if (sz == (size_t)-1 || sz == (size_t)-2) { 58*16038816SMartin Matuska /* 59*16038816SMartin Matuska * We last read an invalid multibyte character sequence, 60*16038816SMartin Matuska * so return an error. 61*16038816SMartin Matuska */ 62*16038816SMartin Matuska return (-1); 63*16038816SMartin Matuska } 64eda14cbcSMatt Macy switch (c) { 65eda14cbcSMatt Macy case '"': 66eda14cbcSMatt Macy FPRINTF(fp, "\\\""); 67eda14cbcSMatt Macy break; 68eda14cbcSMatt Macy case '\n': 69eda14cbcSMatt Macy FPRINTF(fp, "\\n"); 70eda14cbcSMatt Macy break; 71eda14cbcSMatt Macy case '\r': 72eda14cbcSMatt Macy FPRINTF(fp, "\\r"); 73eda14cbcSMatt Macy break; 74eda14cbcSMatt Macy case '\\': 75eda14cbcSMatt Macy FPRINTF(fp, "\\\\"); 76eda14cbcSMatt Macy break; 77eda14cbcSMatt Macy case '\f': 78eda14cbcSMatt Macy FPRINTF(fp, "\\f"); 79eda14cbcSMatt Macy break; 80eda14cbcSMatt Macy case '\t': 81eda14cbcSMatt Macy FPRINTF(fp, "\\t"); 82eda14cbcSMatt Macy break; 83eda14cbcSMatt Macy case '\b': 84eda14cbcSMatt Macy FPRINTF(fp, "\\b"); 85eda14cbcSMatt Macy break; 86eda14cbcSMatt Macy default: 87eda14cbcSMatt Macy if ((c >= 0x00 && c <= 0x1f) || 88eda14cbcSMatt Macy (c > 0x7f && c <= 0xffff)) { 89eda14cbcSMatt Macy /* 90eda14cbcSMatt Macy * Render both Control Characters and Unicode 91eda14cbcSMatt Macy * characters in the Basic Multilingual Plane 92eda14cbcSMatt Macy * as JSON-escaped multibyte characters. 93eda14cbcSMatt Macy */ 94eda14cbcSMatt Macy FPRINTF(fp, "\\u%04x", (int)(0xffff & c)); 95eda14cbcSMatt Macy } else if (c >= 0x20 && c <= 0x7f) { 96eda14cbcSMatt Macy /* 97eda14cbcSMatt Macy * Render other 7-bit ASCII characters directly 98eda14cbcSMatt Macy * and drop other, unrepresentable characters. 99eda14cbcSMatt Macy */ 100eda14cbcSMatt Macy FPRINTF(fp, "%c", (int)(0xff & c)); 101eda14cbcSMatt Macy } 102eda14cbcSMatt Macy break; 103eda14cbcSMatt Macy } 104eda14cbcSMatt Macy input += sz; 105eda14cbcSMatt Macy } 106eda14cbcSMatt Macy 107eda14cbcSMatt Macy FPRINTF(fp, "\""); 108eda14cbcSMatt Macy return (0); 109eda14cbcSMatt Macy } 110eda14cbcSMatt Macy 111eda14cbcSMatt Macy /* 112eda14cbcSMatt Macy * Dump a JSON-formatted representation of an nvlist to the provided FILE *. 113eda14cbcSMatt Macy * This routine does not output any new-lines or additional whitespace other 114eda14cbcSMatt Macy * than that contained in strings, nor does it call fflush(3C). 115eda14cbcSMatt Macy */ 116eda14cbcSMatt Macy int 117eda14cbcSMatt Macy nvlist_print_json(FILE *fp, nvlist_t *nvl) 118eda14cbcSMatt Macy { 119eda14cbcSMatt Macy nvpair_t *curr; 120eda14cbcSMatt Macy boolean_t first = B_TRUE; 121eda14cbcSMatt Macy 122eda14cbcSMatt Macy FPRINTF(fp, "{"); 123eda14cbcSMatt Macy 124eda14cbcSMatt Macy for (curr = nvlist_next_nvpair(nvl, NULL); curr; 125eda14cbcSMatt Macy curr = nvlist_next_nvpair(nvl, curr)) { 126eda14cbcSMatt Macy data_type_t type = nvpair_type(curr); 127eda14cbcSMatt Macy 128eda14cbcSMatt Macy if (!first) 129eda14cbcSMatt Macy FPRINTF(fp, ","); 130eda14cbcSMatt Macy else 131eda14cbcSMatt Macy first = B_FALSE; 132eda14cbcSMatt Macy 133eda14cbcSMatt Macy if (nvlist_print_json_string(fp, nvpair_name(curr)) == -1) 134eda14cbcSMatt Macy return (-1); 135eda14cbcSMatt Macy FPRINTF(fp, ":"); 136eda14cbcSMatt Macy 137eda14cbcSMatt Macy switch (type) { 138eda14cbcSMatt Macy case DATA_TYPE_STRING: { 139eda14cbcSMatt Macy char *string = fnvpair_value_string(curr); 140eda14cbcSMatt Macy if (nvlist_print_json_string(fp, string) == -1) 141eda14cbcSMatt Macy return (-1); 142eda14cbcSMatt Macy break; 143eda14cbcSMatt Macy } 144eda14cbcSMatt Macy 145eda14cbcSMatt Macy case DATA_TYPE_BOOLEAN: { 146eda14cbcSMatt Macy FPRINTF(fp, "true"); 147eda14cbcSMatt Macy break; 148eda14cbcSMatt Macy } 149eda14cbcSMatt Macy 150eda14cbcSMatt Macy case DATA_TYPE_BOOLEAN_VALUE: { 151eda14cbcSMatt Macy FPRINTF(fp, "%s", fnvpair_value_boolean_value(curr) == 152eda14cbcSMatt Macy B_TRUE ? "true" : "false"); 153eda14cbcSMatt Macy break; 154eda14cbcSMatt Macy } 155eda14cbcSMatt Macy 156eda14cbcSMatt Macy case DATA_TYPE_BYTE: { 157eda14cbcSMatt Macy FPRINTF(fp, "%hhu", fnvpair_value_byte(curr)); 158eda14cbcSMatt Macy break; 159eda14cbcSMatt Macy } 160eda14cbcSMatt Macy 161eda14cbcSMatt Macy case DATA_TYPE_INT8: { 162eda14cbcSMatt Macy FPRINTF(fp, "%hhd", fnvpair_value_int8(curr)); 163eda14cbcSMatt Macy break; 164eda14cbcSMatt Macy } 165eda14cbcSMatt Macy 166eda14cbcSMatt Macy case DATA_TYPE_UINT8: { 167eda14cbcSMatt Macy FPRINTF(fp, "%hhu", fnvpair_value_uint8(curr)); 168eda14cbcSMatt Macy break; 169eda14cbcSMatt Macy } 170eda14cbcSMatt Macy 171eda14cbcSMatt Macy case DATA_TYPE_INT16: { 172eda14cbcSMatt Macy FPRINTF(fp, "%hd", fnvpair_value_int16(curr)); 173eda14cbcSMatt Macy break; 174eda14cbcSMatt Macy } 175eda14cbcSMatt Macy 176eda14cbcSMatt Macy case DATA_TYPE_UINT16: { 177eda14cbcSMatt Macy FPRINTF(fp, "%hu", fnvpair_value_uint16(curr)); 178eda14cbcSMatt Macy break; 179eda14cbcSMatt Macy } 180eda14cbcSMatt Macy 181eda14cbcSMatt Macy case DATA_TYPE_INT32: { 182eda14cbcSMatt Macy FPRINTF(fp, "%d", fnvpair_value_int32(curr)); 183eda14cbcSMatt Macy break; 184eda14cbcSMatt Macy } 185eda14cbcSMatt Macy 186eda14cbcSMatt Macy case DATA_TYPE_UINT32: { 187eda14cbcSMatt Macy FPRINTF(fp, "%u", fnvpair_value_uint32(curr)); 188eda14cbcSMatt Macy break; 189eda14cbcSMatt Macy } 190eda14cbcSMatt Macy 191eda14cbcSMatt Macy case DATA_TYPE_INT64: { 192eda14cbcSMatt Macy FPRINTF(fp, "%lld", 193eda14cbcSMatt Macy (long long)fnvpair_value_int64(curr)); 194eda14cbcSMatt Macy break; 195eda14cbcSMatt Macy } 196eda14cbcSMatt Macy 197eda14cbcSMatt Macy case DATA_TYPE_UINT64: { 198eda14cbcSMatt Macy FPRINTF(fp, "%llu", 199eda14cbcSMatt Macy (unsigned long long)fnvpair_value_uint64(curr)); 200eda14cbcSMatt Macy break; 201eda14cbcSMatt Macy } 202eda14cbcSMatt Macy 203eda14cbcSMatt Macy case DATA_TYPE_HRTIME: { 204eda14cbcSMatt Macy hrtime_t val; 205eda14cbcSMatt Macy VERIFY0(nvpair_value_hrtime(curr, &val)); 206eda14cbcSMatt Macy FPRINTF(fp, "%llu", (unsigned long long)val); 207eda14cbcSMatt Macy break; 208eda14cbcSMatt Macy } 209eda14cbcSMatt Macy 210eda14cbcSMatt Macy case DATA_TYPE_DOUBLE: { 211eda14cbcSMatt Macy double val; 212eda14cbcSMatt Macy VERIFY0(nvpair_value_double(curr, &val)); 213eda14cbcSMatt Macy FPRINTF(fp, "%f", val); 214eda14cbcSMatt Macy break; 215eda14cbcSMatt Macy } 216eda14cbcSMatt Macy 217eda14cbcSMatt Macy case DATA_TYPE_NVLIST: { 218eda14cbcSMatt Macy if (nvlist_print_json(fp, 219eda14cbcSMatt Macy fnvpair_value_nvlist(curr)) == -1) 220eda14cbcSMatt Macy return (-1); 221eda14cbcSMatt Macy break; 222eda14cbcSMatt Macy } 223eda14cbcSMatt Macy 224eda14cbcSMatt Macy case DATA_TYPE_STRING_ARRAY: { 225eda14cbcSMatt Macy char **val; 226eda14cbcSMatt Macy uint_t valsz, i; 227eda14cbcSMatt Macy VERIFY0(nvpair_value_string_array(curr, &val, &valsz)); 228eda14cbcSMatt Macy FPRINTF(fp, "["); 229eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 230eda14cbcSMatt Macy if (i > 0) 231eda14cbcSMatt Macy FPRINTF(fp, ","); 232eda14cbcSMatt Macy if (nvlist_print_json_string(fp, val[i]) == -1) 233eda14cbcSMatt Macy return (-1); 234eda14cbcSMatt Macy } 235eda14cbcSMatt Macy FPRINTF(fp, "]"); 236eda14cbcSMatt Macy break; 237eda14cbcSMatt Macy } 238eda14cbcSMatt Macy 239eda14cbcSMatt Macy case DATA_TYPE_NVLIST_ARRAY: { 240eda14cbcSMatt Macy nvlist_t **val; 241eda14cbcSMatt Macy uint_t valsz, i; 242eda14cbcSMatt Macy VERIFY0(nvpair_value_nvlist_array(curr, &val, &valsz)); 243eda14cbcSMatt Macy FPRINTF(fp, "["); 244eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 245eda14cbcSMatt Macy if (i > 0) 246eda14cbcSMatt Macy FPRINTF(fp, ","); 247eda14cbcSMatt Macy if (nvlist_print_json(fp, val[i]) == -1) 248eda14cbcSMatt Macy return (-1); 249eda14cbcSMatt Macy } 250eda14cbcSMatt Macy FPRINTF(fp, "]"); 251eda14cbcSMatt Macy break; 252eda14cbcSMatt Macy } 253eda14cbcSMatt Macy 254eda14cbcSMatt Macy case DATA_TYPE_BOOLEAN_ARRAY: { 255eda14cbcSMatt Macy boolean_t *val; 256eda14cbcSMatt Macy uint_t valsz, i; 257eda14cbcSMatt Macy VERIFY0(nvpair_value_boolean_array(curr, &val, &valsz)); 258eda14cbcSMatt Macy FPRINTF(fp, "["); 259eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 260eda14cbcSMatt Macy if (i > 0) 261eda14cbcSMatt Macy FPRINTF(fp, ","); 262eda14cbcSMatt Macy FPRINTF(fp, val[i] == B_TRUE ? 263eda14cbcSMatt Macy "true" : "false"); 264eda14cbcSMatt Macy } 265eda14cbcSMatt Macy FPRINTF(fp, "]"); 266eda14cbcSMatt Macy break; 267eda14cbcSMatt Macy } 268eda14cbcSMatt Macy 269eda14cbcSMatt Macy case DATA_TYPE_BYTE_ARRAY: { 270eda14cbcSMatt Macy uchar_t *val; 271eda14cbcSMatt Macy uint_t valsz, i; 272eda14cbcSMatt Macy VERIFY0(nvpair_value_byte_array(curr, &val, &valsz)); 273eda14cbcSMatt Macy FPRINTF(fp, "["); 274eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 275eda14cbcSMatt Macy if (i > 0) 276eda14cbcSMatt Macy FPRINTF(fp, ","); 277eda14cbcSMatt Macy FPRINTF(fp, "%hhu", val[i]); 278eda14cbcSMatt Macy } 279eda14cbcSMatt Macy FPRINTF(fp, "]"); 280eda14cbcSMatt Macy break; 281eda14cbcSMatt Macy } 282eda14cbcSMatt Macy 283eda14cbcSMatt Macy case DATA_TYPE_UINT8_ARRAY: { 284eda14cbcSMatt Macy uint8_t *val; 285eda14cbcSMatt Macy uint_t valsz, i; 286eda14cbcSMatt Macy VERIFY0(nvpair_value_uint8_array(curr, &val, &valsz)); 287eda14cbcSMatt Macy FPRINTF(fp, "["); 288eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 289eda14cbcSMatt Macy if (i > 0) 290eda14cbcSMatt Macy FPRINTF(fp, ","); 291eda14cbcSMatt Macy FPRINTF(fp, "%hhu", val[i]); 292eda14cbcSMatt Macy } 293eda14cbcSMatt Macy FPRINTF(fp, "]"); 294eda14cbcSMatt Macy break; 295eda14cbcSMatt Macy } 296eda14cbcSMatt Macy 297eda14cbcSMatt Macy case DATA_TYPE_INT8_ARRAY: { 298eda14cbcSMatt Macy int8_t *val; 299eda14cbcSMatt Macy uint_t valsz, i; 300eda14cbcSMatt Macy VERIFY0(nvpair_value_int8_array(curr, &val, &valsz)); 301eda14cbcSMatt Macy FPRINTF(fp, "["); 302eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 303eda14cbcSMatt Macy if (i > 0) 304eda14cbcSMatt Macy FPRINTF(fp, ","); 305eda14cbcSMatt Macy FPRINTF(fp, "%hhd", val[i]); 306eda14cbcSMatt Macy } 307eda14cbcSMatt Macy FPRINTF(fp, "]"); 308eda14cbcSMatt Macy break; 309eda14cbcSMatt Macy } 310eda14cbcSMatt Macy 311eda14cbcSMatt Macy case DATA_TYPE_UINT16_ARRAY: { 312eda14cbcSMatt Macy uint16_t *val; 313eda14cbcSMatt Macy uint_t valsz, i; 314eda14cbcSMatt Macy VERIFY0(nvpair_value_uint16_array(curr, &val, &valsz)); 315eda14cbcSMatt Macy FPRINTF(fp, "["); 316eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 317eda14cbcSMatt Macy if (i > 0) 318eda14cbcSMatt Macy FPRINTF(fp, ","); 319eda14cbcSMatt Macy FPRINTF(fp, "%hu", val[i]); 320eda14cbcSMatt Macy } 321eda14cbcSMatt Macy FPRINTF(fp, "]"); 322eda14cbcSMatt Macy break; 323eda14cbcSMatt Macy } 324eda14cbcSMatt Macy 325eda14cbcSMatt Macy case DATA_TYPE_INT16_ARRAY: { 326eda14cbcSMatt Macy int16_t *val; 327eda14cbcSMatt Macy uint_t valsz, i; 328eda14cbcSMatt Macy VERIFY0(nvpair_value_int16_array(curr, &val, &valsz)); 329eda14cbcSMatt Macy FPRINTF(fp, "["); 330eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 331eda14cbcSMatt Macy if (i > 0) 332eda14cbcSMatt Macy FPRINTF(fp, ","); 333eda14cbcSMatt Macy FPRINTF(fp, "%hd", val[i]); 334eda14cbcSMatt Macy } 335eda14cbcSMatt Macy FPRINTF(fp, "]"); 336eda14cbcSMatt Macy break; 337eda14cbcSMatt Macy } 338eda14cbcSMatt Macy 339eda14cbcSMatt Macy case DATA_TYPE_UINT32_ARRAY: { 340eda14cbcSMatt Macy uint32_t *val; 341eda14cbcSMatt Macy uint_t valsz, i; 342eda14cbcSMatt Macy VERIFY0(nvpair_value_uint32_array(curr, &val, &valsz)); 343eda14cbcSMatt Macy FPRINTF(fp, "["); 344eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 345eda14cbcSMatt Macy if (i > 0) 346eda14cbcSMatt Macy FPRINTF(fp, ","); 347eda14cbcSMatt Macy FPRINTF(fp, "%u", val[i]); 348eda14cbcSMatt Macy } 349eda14cbcSMatt Macy FPRINTF(fp, "]"); 350eda14cbcSMatt Macy break; 351eda14cbcSMatt Macy } 352eda14cbcSMatt Macy 353eda14cbcSMatt Macy case DATA_TYPE_INT32_ARRAY: { 354eda14cbcSMatt Macy int32_t *val; 355eda14cbcSMatt Macy uint_t valsz, i; 356eda14cbcSMatt Macy VERIFY0(nvpair_value_int32_array(curr, &val, &valsz)); 357eda14cbcSMatt Macy FPRINTF(fp, "["); 358eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 359eda14cbcSMatt Macy if (i > 0) 360eda14cbcSMatt Macy FPRINTF(fp, ","); 361eda14cbcSMatt Macy FPRINTF(fp, "%d", val[i]); 362eda14cbcSMatt Macy } 363eda14cbcSMatt Macy FPRINTF(fp, "]"); 364eda14cbcSMatt Macy break; 365eda14cbcSMatt Macy } 366eda14cbcSMatt Macy 367eda14cbcSMatt Macy case DATA_TYPE_UINT64_ARRAY: { 368eda14cbcSMatt Macy uint64_t *val; 369eda14cbcSMatt Macy uint_t valsz, i; 370eda14cbcSMatt Macy VERIFY0(nvpair_value_uint64_array(curr, &val, &valsz)); 371eda14cbcSMatt Macy FPRINTF(fp, "["); 372eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 373eda14cbcSMatt Macy if (i > 0) 374eda14cbcSMatt Macy FPRINTF(fp, ","); 375eda14cbcSMatt Macy FPRINTF(fp, "%llu", 376eda14cbcSMatt Macy (unsigned long long)val[i]); 377eda14cbcSMatt Macy } 378eda14cbcSMatt Macy FPRINTF(fp, "]"); 379eda14cbcSMatt Macy break; 380eda14cbcSMatt Macy } 381eda14cbcSMatt Macy 382eda14cbcSMatt Macy case DATA_TYPE_INT64_ARRAY: { 383eda14cbcSMatt Macy int64_t *val; 384eda14cbcSMatt Macy uint_t valsz, i; 385eda14cbcSMatt Macy VERIFY0(nvpair_value_int64_array(curr, &val, &valsz)); 386eda14cbcSMatt Macy FPRINTF(fp, "["); 387eda14cbcSMatt Macy for (i = 0; i < valsz; i++) { 388eda14cbcSMatt Macy if (i > 0) 389eda14cbcSMatt Macy FPRINTF(fp, ","); 390eda14cbcSMatt Macy FPRINTF(fp, "%lld", (long long)val[i]); 391eda14cbcSMatt Macy } 392eda14cbcSMatt Macy FPRINTF(fp, "]"); 393eda14cbcSMatt Macy break; 394eda14cbcSMatt Macy } 395eda14cbcSMatt Macy 396eda14cbcSMatt Macy case DATA_TYPE_UNKNOWN: 397eda14cbcSMatt Macy case DATA_TYPE_DONTCARE: 398eda14cbcSMatt Macy return (-1); 399eda14cbcSMatt Macy } 400eda14cbcSMatt Macy 401eda14cbcSMatt Macy } 402eda14cbcSMatt Macy 403eda14cbcSMatt Macy FPRINTF(fp, "}"); 404eda14cbcSMatt Macy return (0); 405eda14cbcSMatt Macy } 406