1*3fe5961aSDevin Teske.\" Copyright (c) 2013-2026 Devin Teske <dteske@FreeBSD.org> 2*3fe5961aSDevin Teske.\" Copyright (c) 2021-2026 Faraz Vahedi <kfv@FreeBSD.org> 3*3fe5961aSDevin Teske.\" 4*3fe5961aSDevin Teske.\" SPDX-License-Identifier: BSD-2-Clause 5*3fe5961aSDevin Teske.\" 6*3fe5961aSDevin Teske.Dd August 2, 2026 7*3fe5961aSDevin Teske.Dt BSDCONF 3 8*3fe5961aSDevin Teske.Os 9*3fe5961aSDevin Teske.Sh NAME 10*3fe5961aSDevin Teske.Nm bsdconf , 11*3fe5961aSDevin Teske.Nm bsdconf_parse , 12*3fe5961aSDevin Teske.Nm bsdconf_fparse , 13*3fe5961aSDevin Teske.Nm bsdconf_get_option , 14*3fe5961aSDevin Teske.Nm bsdconf_spool , 15*3fe5961aSDevin Teske.Nm bsdconf_unquote 16*3fe5961aSDevin Teske.Nd configuration file reading library 17*3fe5961aSDevin Teske.Sh LIBRARY 18*3fe5961aSDevin Teske.Lb libbsdconf 19*3fe5961aSDevin Teske.Sh SYNOPSIS 20*3fe5961aSDevin Teske.In bsdconf.h 21*3fe5961aSDevin Teske.Ft int 22*3fe5961aSDevin Teske.Fo bsdconf_parse 23*3fe5961aSDevin Teske.Fa "struct bsdconf_option options[]" 24*3fe5961aSDevin Teske.Fa "const char *path" 25*3fe5961aSDevin Teske.Fa "int \*[lp]*unknown\*[rp]\*[lp]struct bsdconf_option *option" 26*3fe5961aSDevin Teske.Fa "uint32_t line" 27*3fe5961aSDevin Teske.Fa "char *directive" 28*3fe5961aSDevin Teske.Fa "char *value\*[rp]" 29*3fe5961aSDevin Teske.Fa "uint16_t processing_options" 30*3fe5961aSDevin Teske.Fc 31*3fe5961aSDevin Teske.Ft int 32*3fe5961aSDevin Teske.Fo bsdconf_fparse 33*3fe5961aSDevin Teske.Fa "struct bsdconf_option options[]" 34*3fe5961aSDevin Teske.Fa "int fd" 35*3fe5961aSDevin Teske.Fa "int \*[lp]*unknown\*[rp]\*[lp]struct bsdconf_option *option" 36*3fe5961aSDevin Teske.Fa "uint32_t line" 37*3fe5961aSDevin Teske.Fa "char *directive" 38*3fe5961aSDevin Teske.Fa "char *value\*[rp]" 39*3fe5961aSDevin Teske.Fa "uint16_t processing_options" 40*3fe5961aSDevin Teske.Fc 41*3fe5961aSDevin Teske.Ft "struct bsdconf_option *" 42*3fe5961aSDevin Teske.Fo bsdconf_get_option 43*3fe5961aSDevin Teske.Fa "struct bsdconf_option options[]" 44*3fe5961aSDevin Teske.Fa "const char *directive" 45*3fe5961aSDevin Teske.Fc 46*3fe5961aSDevin Teske.Ft int 47*3fe5961aSDevin Teske.Fo bsdconf_spool 48*3fe5961aSDevin Teske.Fa "int fd" 49*3fe5961aSDevin Teske.Fc 50*3fe5961aSDevin Teske.Ft "char *" 51*3fe5961aSDevin Teske.Fo bsdconf_unquote 52*3fe5961aSDevin Teske.Fa "char *value" 53*3fe5961aSDevin Teske.Fc 54*3fe5961aSDevin Teske.Sh DESCRIPTION 55*3fe5961aSDevin TeskeThe 56*3fe5961aSDevin Teske.Nm 57*3fe5961aSDevin Teskelibrary provides a light-weight, 58*3fe5961aSDevin Teskeportable framework for reading and writing configuration 59*3fe5961aSDevin Teskefiles. 60*3fe5961aSDevin TeskeIt is the successor to the retired 61*3fe5961aSDevin Teske.Nm figpar 62*3fe5961aSDevin Teskelibrary, 63*3fe5961aSDevin Teskeextending the original read-only token parser into a unified, 64*3fe5961aSDevin Teskeresilient reader/writer engine. 65*3fe5961aSDevin Teske.Pp 66*3fe5961aSDevin TeskeDue to the fact that configuration files may have basic syntax differences, 67*3fe5961aSDevin Teskethe library does not attempt to impose any structure on the data but instead 68*3fe5961aSDevin Teskeprovides raw data to a set of callback functions. 69*3fe5961aSDevin TeskeThese callback functions can in-turn initiate abort through their return 70*3fe5961aSDevin Teskevalue, 71*3fe5961aSDevin Teskeallowing custom syntax validation during parsing. 72*3fe5961aSDevin Teske.Pp 73*3fe5961aSDevin TeskeSyntax differences between well-known file formats are described by format 74*3fe5961aSDevin Teskedescriptors: 75*3fe5961aSDevin Teskea format descriptor is a small read-only table of properties 76*3fe5961aSDevin Teske.Pq Vt struct bsdconf_format_def ; see Xr bsdconf_format 3 77*3fe5961aSDevin Teskenaming a format's target keyword, 78*3fe5961aSDevin Teskebacking files, 79*3fe5961aSDevin Teskeand tokenizing/quoting rules, 80*3fe5961aSDevin Teskewhich parameterize a single shared engine. 81*3fe5961aSDevin Teske.Pp 82*3fe5961aSDevin TeskeDespite the name, it bears no relation to a file descriptor; 83*3fe5961aSDevin Teskeit is closer in spirit to a driver's method table: 84*3fe5961aSDevin Teskestatic data describing behavior, 85*3fe5961aSDevin Teskeconsulted rather than executed. 86*3fe5961aSDevin TeskeWriting is documented in 87*3fe5961aSDevin Teske.Xr bsdconf_put 3 ; 88*3fe5961aSDevin Teskebuilt-in formats and discovery in 89*3fe5961aSDevin Teske.Xr bsdconf_format 3 . 90*3fe5961aSDevin Teske.Pp 91*3fe5961aSDevin TeskeConfiguration directives, 92*3fe5961aSDevin Tesketypes, 93*3fe5961aSDevin Teskeand callback functions are provided through data structures defined in 94*3fe5961aSDevin Teske.In bsdconf.h : 95*3fe5961aSDevin Teske.Bd -literal -offset indent 96*3fe5961aSDevin Teskestruct bsdconf_option { 97*3fe5961aSDevin Teske enum bsdconf_type type; /* value type */ 98*3fe5961aSDevin Teske const char *directive; /* keyword */ 99*3fe5961aSDevin Teske union bsdconf_value value; /* value */ 100*3fe5961aSDevin Teske enum bsdconf_op op; /* assignment operator */ 101*3fe5961aSDevin Teske uint8_t action; /* bsdconf_put() action */ 102*3fe5961aSDevin Teske uint16_t result; /* set by bsdconf_put() */ 103*3fe5961aSDevin Teske uint32_t line; /* set by bsdconf_put() */ 104*3fe5961aSDevin Teske uint32_t match_line; 105*3fe5961aSDevin Teske /* put: 0=any, else line */ 106*3fe5961aSDevin Teske 107*3fe5961aSDevin Teske /* Pointer to function used when directive is found */ 108*3fe5961aSDevin Teske int (*parse)(struct bsdconf_option *option, uint32_t line, 109*3fe5961aSDevin Teske char *directive, char *value); 110*3fe5961aSDevin Teske}; 111*3fe5961aSDevin Teske 112*3fe5961aSDevin Teskeenum bsdconf_type { 113*3fe5961aSDevin Teske BSDCONF_TYPE_NONE = 0x0000, /* directives with no value */ 114*3fe5961aSDevin Teske BSDCONF_TYPE_BOOL = 0x0001, /* boolean */ 115*3fe5961aSDevin Teske BSDCONF_TYPE_INT = 0x0002, /* signed 32-bit integer */ 116*3fe5961aSDevin Teske BSDCONF_TYPE_UINT = 0x0004, /* unsigned 32-bit integer */ 117*3fe5961aSDevin Teske BSDCONF_TYPE_STR = 0x0008, /* string pointer */ 118*3fe5961aSDevin Teske BSDCONF_TYPE_STRARRAY = 0x0010, /* string array pointer */ 119*3fe5961aSDevin Teske BSDCONF_TYPE_DATA1 = 0x0020, /* void data type-1 (open) */ 120*3fe5961aSDevin Teske BSDCONF_TYPE_DATA2 = 0x0040, /* void data type-2 (open) */ 121*3fe5961aSDevin Teske BSDCONF_TYPE_DATA3 = 0x0080, /* void data type-3 (open) */ 122*3fe5961aSDevin Teske BSDCONF_TYPE_INT64 = 0x0100, /* signed 64-bit integer */ 123*3fe5961aSDevin Teske BSDCONF_TYPE_UINT64 = 0x0200, /* unsigned 64-bit integer */ 124*3fe5961aSDevin Teske BSDCONF_TYPE_RESERVED = 0x0400, /* reserved */ 125*3fe5961aSDevin Teske}; 126*3fe5961aSDevin Teske 127*3fe5961aSDevin Teskeunion bsdconf_value { 128*3fe5961aSDevin Teske void *data; /* Opaque pointer (DATA1..DATA3) */ 129*3fe5961aSDevin Teske char *str; /* Pointer to NUL-terminated string */ 130*3fe5961aSDevin Teske char **strarray; /* Pointer to an array of strings */ 131*3fe5961aSDevin Teske int32_t num; /* Signed 32-bit integer value */ 132*3fe5961aSDevin Teske uint32_t u_num; /* Unsigned 32-bit integer value */ 133*3fe5961aSDevin Teske int64_t num64; /* Signed 64-bit integer value */ 134*3fe5961aSDevin Teske uint64_t u_num64; /* Unsigned 64-bit integer value */ 135*3fe5961aSDevin Teske bool boolean; /* Boolean value */ 136*3fe5961aSDevin Teske}; 137*3fe5961aSDevin Teske.Ed 138*3fe5961aSDevin Teske.Pp 139*3fe5961aSDevin TeskeThe 140*3fe5961aSDevin Teske.Fa processing_options 141*3fe5961aSDevin Teskeargument to 142*3fe5961aSDevin Teske.Fn bsdconf_parse , 143*3fe5961aSDevin Teske.Fn bsdconf_fparse , 144*3fe5961aSDevin Teskeand 145*3fe5961aSDevin Teske.Xr bsdconf_put 3 146*3fe5961aSDevin Teskeis a mask of bit fields which indicate various processing options. 147*3fe5961aSDevin TeskeThe possible flags are: 148*3fe5961aSDevin Teske.Bl -tag -width BSDCONF_BREAK_ON_SEMICOLON 149*3fe5961aSDevin Teske.It Dv BSDCONF_BREAK_ON_EQUALS 150*3fe5961aSDevin TeskeAn equals sign 151*3fe5961aSDevin Teske.Pq Ql = 152*3fe5961aSDevin Teskeis normally considered part of the directive. 153*3fe5961aSDevin TeskeThis flag enables terminating the directive at the equals sign. 154*3fe5961aSDevin TeskeAlso makes equals sign optional and transient. 155*3fe5961aSDevin Teske.It Dv BSDCONF_BREAK_ON_SEMICOLON 156*3fe5961aSDevin TeskeA semicolon 157*3fe5961aSDevin Teske.Pq Ql \&; 158*3fe5961aSDevin Teskeis normally considered part of the value. 159*3fe5961aSDevin TeskeThis flag enables terminating the value at the semicolon. 160*3fe5961aSDevin TeskeAlso allows multiple statements on a single line separated by semicolon. 161*3fe5961aSDevin Teske.It Dv BSDCONF_CASE_SENSITIVE 162*3fe5961aSDevin TeskeNormally directives are matched case insensitively using 163*3fe5961aSDevin Teske.Xr fnmatch 3 . 164*3fe5961aSDevin TeskeThis flag enables directive matching to be case sensitive. 165*3fe5961aSDevin Teske.It Dv BSDCONF_REQUIRE_EQUALS 166*3fe5961aSDevin TeskeIf a directive is not followed by an equals, 167*3fe5961aSDevin Teskeprocessing is aborted. 168*3fe5961aSDevin Teske.It Dv BSDCONF_STRICT_EQUALS 169*3fe5961aSDevin TeskeEquals must be part of the directive 170*3fe5961aSDevin Teske.Pq no whitespace before or after 171*3fe5961aSDevin Tesketo be considered a delimiter between directive and value. 172*3fe5961aSDevin TeskeRequired by file formats whose readers reject whitespace around the equals 173*3fe5961aSDevin Teskesign, 174*3fe5961aSDevin Teskesuch as the 175*3fe5961aSDevin Teske.Fx 176*3fe5961aSDevin Teskeboot loader's processing of 177*3fe5961aSDevin Teske.Xr loader.conf 5 . 178*3fe5961aSDevin Teske.It Dv BSDCONF_OPERATOR_EQUALS 179*3fe5961aSDevin TeskeRecognize 180*3fe5961aSDevin Teske.Xr make 1 181*3fe5961aSDevin Teskestyle assignment modifiers 182*3fe5961aSDevin Teske.Po 183*3fe5961aSDevin Teske.Ql += , 184*3fe5961aSDevin Teske.Ql ?= , 185*3fe5961aSDevin Teske.Ql := , 186*3fe5961aSDevin Teskeand 187*3fe5961aSDevin Teske.Ql != 188*3fe5961aSDevin Teske.Pc 189*3fe5961aSDevin Teskeand split them off the tail of the directive. 190*3fe5961aSDevin TeskeThe parsed operator is reported through the 191*3fe5961aSDevin Teske.Va op 192*3fe5961aSDevin Teskemember of the matched option 193*3fe5961aSDevin Teske.Pq one of Dv BSDCONF_OP_ASSIGN , BSDCONF_OP_APPEND , BSDCONF_OP_COND , BSDCONF_OP_EXPAND , No or Dv BSDCONF_OP_SHELL . 194*3fe5961aSDevin Teske.El 195*3fe5961aSDevin Teske.Pp 196*3fe5961aSDevin TeskeThe 197*3fe5961aSDevin Teske.Fa options 198*3fe5961aSDevin Teskestruct array pointer can be NULL and every directive will run the 199*3fe5961aSDevin Teske.Fn unknown 200*3fe5961aSDevin Teskefunction argument. 201*3fe5961aSDevin Teske.Pp 202*3fe5961aSDevin TeskeThe directive for each bsdconf_option item in the 203*3fe5961aSDevin Teske.Fn bsdconf_parse 204*3fe5961aSDevin Teskeoptions argument is matched against each parsed directive using 205*3fe5961aSDevin Teske.Xr fnmatch 3 206*3fe5961aSDevin Teskeuntil a match is found. 207*3fe5961aSDevin TeskeIf a match is found, 208*3fe5961aSDevin Teskethe 209*3fe5961aSDevin Teske.Fn parse 210*3fe5961aSDevin Teskefunction for that bsdconf_option directive is run with the line number, 211*3fe5961aSDevin Teskedirective, 212*3fe5961aSDevin Teskeand value. 213*3fe5961aSDevin TeskeOtherwise if no match, 214*3fe5961aSDevin Teskethe 215*3fe5961aSDevin Teske.Fn unknown 216*3fe5961aSDevin Teskefunction is run 217*3fe5961aSDevin Teske.Pq with the same arguments . 218*3fe5961aSDevin TeskeWhen 219*3fe5961aSDevin Teske.Dv BSDCONF_OPERATOR_EQUALS 220*3fe5961aSDevin Teskeis set, 221*3fe5961aSDevin Teske.Fn unknown 222*3fe5961aSDevin Teskereceives a non-NULL 223*3fe5961aSDevin Teske.Fa option 224*3fe5961aSDevin Teskewhose 225*3fe5961aSDevin Teske.Va op 226*3fe5961aSDevin Teskemember holds the statement's assignment operator 227*3fe5961aSDevin Teske.Pq there is no matched options-array slot to hang it on ; 228*3fe5961aSDevin Teskeotherwise 229*3fe5961aSDevin Teske.Fa option 230*3fe5961aSDevin Teskemay be 231*3fe5961aSDevin Teske.Dv NULL . 232*3fe5961aSDevin Teske.Pp 233*3fe5961aSDevin TeskeIf either 234*3fe5961aSDevin Teske.Fn parse 235*3fe5961aSDevin Teskeor 236*3fe5961aSDevin Teske.Fn unknown 237*3fe5961aSDevin Teskereturn non-zero, 238*3fe5961aSDevin Teske.Fn bsdconf_parse 239*3fe5961aSDevin Teskeaborts reading the file and returns the error value to its caller. 240*3fe5961aSDevin Teske.Pp 241*3fe5961aSDevin TeskeA value normally ends at the first unescaped newline, 242*3fe5961aSDevin Teskebut a statement may span multiple lines: 243*3fe5961aSDevin Teskea backslash immediately preceding the newline continues the value on the 244*3fe5961aSDevin Teskenext line, 245*3fe5961aSDevin Teskein the manner of 246*3fe5961aSDevin Teske.Xr make 1 247*3fe5961aSDevin Teske.Pq essential to Pa make.conf and its siblings . 248*3fe5961aSDevin TeskeThe backslash-newline pairs are removed from the value delivered to the 249*3fe5961aSDevin Teskecallbacks 250*3fe5961aSDevin Teske.Pq surrounding whitespace is preserved verbatim , 251*3fe5961aSDevin Teskeand reported line numbers are those of each statement's first line. 252*3fe5961aSDevin Teske.Xr bsdconf_put 3 253*3fe5961aSDevin Teskerecognizes the same continuations when locating a value; 254*3fe5961aSDevin Teskerewriting a continued value replaces all of its lines with the single new 255*3fe5961aSDevin Teskevalue. 256*3fe5961aSDevin Teske.Pp 257*3fe5961aSDevin Teske.Fn bsdconf_fparse 258*3fe5961aSDevin Teskeis identical to 259*3fe5961aSDevin Teske.Fn bsdconf_parse 260*3fe5961aSDevin Teskeexcept that it operates on an already-open file descriptor 261*3fe5961aSDevin Teske.Fa fd , 262*3fe5961aSDevin Teskewhich remains open on return 263*3fe5961aSDevin Teske.Pq the caller retains ownership . 264*3fe5961aSDevin TeskeThis allows the caller to constrain the process 265*3fe5961aSDevin Teske.Pq for example with Xr capsicum 4 266*3fe5961aSDevin Teskebefore parsing begins. 267*3fe5961aSDevin TeskeThe scanner requires a seekable descriptor; 268*3fe5961aSDevin Teskeinput that cannot seek 269*3fe5961aSDevin Teske.Pq a pipe or socket, standard input included 270*3fe5961aSDevin Teskeis detected up front and transparently spooled through 271*3fe5961aSDevin Teske.Fn bsdconf_spool , 272*3fe5961aSDevin Teskeat the cost of one transient copy of the data. 273*3fe5961aSDevin Teske.Pp 274*3fe5961aSDevin Teske.Fn bsdconf_spool 275*3fe5961aSDevin Teskecopies the remaining contents of 276*3fe5961aSDevin Teske.Fa fd 277*3fe5961aSDevin Tesketo an unlinked temporary file 278*3fe5961aSDevin Teske.Pq created with Xr tmpfile 3 279*3fe5961aSDevin Teskeand returns a seekable descriptor referencing it, 280*3fe5961aSDevin Teskewhich the caller must 281*3fe5961aSDevin Teske.Xr close 2 282*3fe5961aSDevin Teske.Pq the backing storage is reclaimed then . 283*3fe5961aSDevin TeskeIt is exported for callers that must adapt non-seekable input themselves 284*3fe5961aSDevin Teskebefore revoking their own ability to create files, 285*3fe5961aSDevin Teskeas 286*3fe5961aSDevin Teske.Xr sysconf 8 287*3fe5961aSDevin Teskedoes before entering its 288*3fe5961aSDevin Teske.Xr capsicum 4 289*3fe5961aSDevin Teskesandbox. 290*3fe5961aSDevin Teske.Pp 291*3fe5961aSDevin Teske.Fn bsdconf_get_option 292*3fe5961aSDevin Tesketraverses the options-array and returns the option that matches via 293*3fe5961aSDevin Teske.Xr strcmp 3 , 294*3fe5961aSDevin Teskeor 295*3fe5961aSDevin Teske.Dv NULL 296*3fe5961aSDevin Teskeif none matches. 297*3fe5961aSDevin Teske.Pp 298*3fe5961aSDevin Teske.Fn bsdconf_unquote 299*3fe5961aSDevin Teskestrips one layer of surrounding double-quotes from 300*3fe5961aSDevin Teske.Fa value 301*3fe5961aSDevin Teskein place and returns it. 302*3fe5961aSDevin TeskeParsed values retain their quotes so that data round-trips losslessly; 303*3fe5961aSDevin Teskethis helper is for display and comparison purposes. 304*3fe5961aSDevin Teske.Sh RETURN VALUES 305*3fe5961aSDevin Teske.Fn bsdconf_parse 306*3fe5961aSDevin Teskeand 307*3fe5961aSDevin Teske.Fn bsdconf_fparse 308*3fe5961aSDevin Teskereturn zero on success; 309*3fe5961aSDevin Teskeotherwise -1 310*3fe5961aSDevin Teske.Pq or the non-zero result of a callback 311*3fe5961aSDevin Teskeis returned and the global variable 312*3fe5961aSDevin Teske.Va errno 313*3fe5961aSDevin Teskeis set to indicate the error. 314*3fe5961aSDevin Teske.Fn bsdconf_spool 315*3fe5961aSDevin Teskereturns a new seekable file descriptor on success; 316*3fe5961aSDevin Teskeotherwise -1 with 317*3fe5961aSDevin Teske.Va errno 318*3fe5961aSDevin Teskeset to indicate the error. 319*3fe5961aSDevin Teske.Fn bsdconf_get_option 320*3fe5961aSDevin Teskereturns a pointer to the matching option, 321*3fe5961aSDevin Teskeor 322*3fe5961aSDevin Teske.Dv NULL 323*3fe5961aSDevin Teskewhen none matches. 324*3fe5961aSDevin Teske.Sh EXAMPLES 325*3fe5961aSDevin TeskeRead two known directives from a 326*3fe5961aSDevin Teske.Ql name=value 327*3fe5961aSDevin Teskefile, 328*3fe5961aSDevin Teskerouting every statement through a callback: 329*3fe5961aSDevin Teske.Bd -literal -offset indent 330*3fe5961aSDevin Teske#include <err.h> 331*3fe5961aSDevin Teske#include <stdio.h> 332*3fe5961aSDevin Teske#include <bsdconf.h> 333*3fe5961aSDevin Teske 334*3fe5961aSDevin Teskestatic int 335*3fe5961aSDevin Teskeshow(struct bsdconf_option *option, uint32_t line, 336*3fe5961aSDevin Teske char *directive, char *value) 337*3fe5961aSDevin Teske{ 338*3fe5961aSDevin Teske printf("%u: %s is %s\en", line, directive, 339*3fe5961aSDevin Teske bsdconf_unquote(value)); 340*3fe5961aSDevin Teske return (0); 341*3fe5961aSDevin Teske} 342*3fe5961aSDevin Teske 343*3fe5961aSDevin Teskestatic struct bsdconf_option options[] = { 344*3fe5961aSDevin Teske { .directive = "hostname", .parse = show }, 345*3fe5961aSDevin Teske { .directive = "timeout", .parse = show }, 346*3fe5961aSDevin Teske { .directive = NULL } 347*3fe5961aSDevin Teske}; 348*3fe5961aSDevin Teske 349*3fe5961aSDevin Teskeint 350*3fe5961aSDevin Teskemain(void) 351*3fe5961aSDevin Teske{ 352*3fe5961aSDevin Teske if (bsdconf_parse(options, "/usr/local/etc/myapp.conf", 353*3fe5961aSDevin Teske NULL, BSDCONF_BREAK_ON_EQUALS) != 0) 354*3fe5961aSDevin Teske err(1, "myapp.conf"); 355*3fe5961aSDevin Teske return (0); 356*3fe5961aSDevin Teske} 357*3fe5961aSDevin Teske.Ed 358*3fe5961aSDevin Teske.Pp 359*3fe5961aSDevin TeskeCallbacks own the semantics, 360*3fe5961aSDevin Teskeso a directive that legitimately repeats is accumulated rather than 361*3fe5961aSDevin Teskeoverwritten; 362*3fe5961aSDevin Teskeno format descriptor is involved. 363*3fe5961aSDevin TeskeThe file here is Apache-style 364*3fe5961aSDevin Teske.Pq space-separated, no equals sign , 365*3fe5961aSDevin Teskeso 366*3fe5961aSDevin Teske.Dv BSDCONF_BREAK_ON_EQUALS 367*3fe5961aSDevin Teskeis simply omitted: 368*3fe5961aSDevin Teske.Bd -literal -offset indent 369*3fe5961aSDevin Teskestatic char *servers[16]; 370*3fe5961aSDevin Teskestatic size_t nservers; 371*3fe5961aSDevin Teske 372*3fe5961aSDevin Teskestatic int 373*3fe5961aSDevin Teskeaddserver(struct bsdconf_option *option, uint32_t line, 374*3fe5961aSDevin Teske char *directive, char *value) 375*3fe5961aSDevin Teske{ 376*3fe5961aSDevin Teske if (nservers >= 16 || 377*3fe5961aSDevin Teske (servers[nservers] = strdup(value)) == NULL) 378*3fe5961aSDevin Teske return (-1); /* abort the parse */ 379*3fe5961aSDevin Teske nservers++; 380*3fe5961aSDevin Teske return (0); 381*3fe5961aSDevin Teske} 382*3fe5961aSDevin Teske 383*3fe5961aSDevin Teskestatic struct bsdconf_option cumulative[] = { 384*3fe5961aSDevin Teske { .directive = "server", .parse = addserver }, 385*3fe5961aSDevin Teske { .directive = NULL } 386*3fe5961aSDevin Teske}; 387*3fe5961aSDevin Teske 388*3fe5961aSDevin Teske ... 389*3fe5961aSDevin Teske if (bsdconf_parse(cumulative, path, NULL, 0) != 0) 390*3fe5961aSDevin Teske err(1, "%s", path); 391*3fe5961aSDevin Teske.Ed 392*3fe5961aSDevin Teske.Sh SEE ALSO 393*3fe5961aSDevin Teske.Xr bsdconf_format 3 , 394*3fe5961aSDevin Teske.Xr bsdconf_put 3 , 395*3fe5961aSDevin Teske.Xr loader.conf 5 , 396*3fe5961aSDevin Teske.Xr sysctl.conf 5 , 397*3fe5961aSDevin Teske.Xr sysconf 8 398*3fe5961aSDevin Teske.Sh HISTORY 399*3fe5961aSDevin TeskeThe 400*3fe5961aSDevin Teske.Nm 401*3fe5961aSDevin Teskelibrary first appeared in 402*3fe5961aSDevin Teske.Fx 16.0 . 403*3fe5961aSDevin TeskeIt supersedes the 404*3fe5961aSDevin Teske.Nm figpar 405*3fe5961aSDevin Teskelibrary which first appeared in 406*3fe5961aSDevin Teske.Fx 10.2 407*3fe5961aSDevin Teskeand was retired to the ports tree. 408*3fe5961aSDevin Teske.Sh AUTHORS 409*3fe5961aSDevin Teske.An Devin Teske Aq Mt dteske@FreeBSD.org 410*3fe5961aSDevin Teske.An Faraz Vahedi Aq Mt kfv@FreeBSD.org 411*3fe5961aSDevin Teske.Sh BUGS 412*3fe5961aSDevin TeskeThis is the first implementation of the library, 413*3fe5961aSDevin Teskeand the interface may be subject to refinement. 414*3fe5961aSDevin Teske.Pp 415*3fe5961aSDevin TeskeWrite-path limitations for cumulative directives are discussed in the 416*3fe5961aSDevin Teske.Sx LIMITATIONS 417*3fe5961aSDevin Teskesection of 418*3fe5961aSDevin Teske.Xr bsdconf_put 3 . 419*3fe5961aSDevin Teske.Sh SECURITY CONSIDERATIONS 420*3fe5961aSDevin TeskeParsing allocates buffers sized by the longest directive and value 421*3fe5961aSDevin Teskeencountered rather than by untrusted length fields, 422*3fe5961aSDevin Teskeand 423*3fe5961aSDevin Teske.Fn bsdconf_fparse 424*3fe5961aSDevin Teskeaccepts an already-open descriptor precisely so that a caller may 425*3fe5961aSDevin Teskesandbox itself 426*3fe5961aSDevin Teske.Pq for example with Xr capsicum 4 427*3fe5961aSDevin Teskebefore touching untrusted input, 428*3fe5961aSDevin Teskeas 429*3fe5961aSDevin Teske.Xr sysconf 8 430*3fe5961aSDevin Teskedoes for its read-only operations. 431*3fe5961aSDevin TeskeWrite-path hardening is documented in 432*3fe5961aSDevin Teske.Xr bsdconf_put 3 . 433