Index: vendor/Juniper/libxo/dist/libxo/xo_format.5 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_format.5 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_format.5 (revision 296962) @@ -0,0 +1,943 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd November 6, 2015 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_format +.Nd content of format descriptors for xo_emit +.Sh DESCRIPTION +.Pp +.Nm libxo +uses format strings to control the rendering of data into +various output styles, including +.Em text , +.Em XML , +.Em JSON , +and +.Em HTML . +Each format string contains a set of zero or more +.Dq "field descriptions" , +which describe independent data fields. +Each field description contains a set of +.Dq modifiers , +a +.Dq "content string" , +and zero, one, or two +.Dq "format descriptors" . +The modifiers tell +.Nm libxo +what the field is and how to treat it, while the format descriptors are +formatting instructions using +.Xr printf 3 Ns -style +format strings, telling +.Nm libxo +how to format the field. +The field description is placed inside +a set of braces, with a colon +.Ql ( \&: ) +after the modifiers and a slash +.Ql ( \&/ ) +before each format descriptors. +Text may be intermixed with +field descriptions within the format string. +.Pp +The field description is given as follows: +.Bd -literal -offset indent + '{' [ role | modifier ]* [',' long-names ]* ':' [ content ] + [ '/' field-format [ '/' encoding-format ]] '}' +.Ed +.Pp +The role describes the function of the field, while the modifiers +enable optional behaviors. +The contents, field-format, and +encoding-format are used in varying ways, based on the role. +These are described in the following sections. +.Pp +Braces can be escaped by using double braces, similar to "%%" in +.Xr printf 3 . +The format string "{{braces}}" would emit "{braces}". +.Pp +In the following example, three field descriptors appear. +The first +is a padding field containing three spaces of padding, the second is a +label ("In stock"), and the third is a value field ("in-stock"). +The in-stock field has a "%u" format that will parse the next argument +passed to the +.Xr xo_emit 3 , +function as an unsigned integer. +.Bd -literal -offset indent + xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\\n", 65); +.Ed +.Pp +This single line of code can generate text ("In stock: 65\\n"), XML +("65"), JSON ('"in-stock": 65'), or HTML (too +lengthy to be listed here). +.Pp +While roles and modifiers typically use single character for brevity, +there are alternative names for each which allow more verbose +formatting strings. +These names must be preceded by a comma, and may follow any +single-character values: +.Bd -literal -offset indent + xo_emit("{L,white,colon:In stock}{,key:in-stock/%u}\n", 65); +.Ed +.Ss "Field Roles" +Field roles are optional, and indicate the role and formatting of the +content. +The roles are listed below; only one role is permitted: +.Bl -column "M" "Name12341234" +.It Sy "M" "Name " "Description" +.It C "color " "Field is a color or effect" +.It D "decoration " "Field is non-text (e.g. colon, comma)" +.It E "error " "Field is an error message" +.It L "label " "Field is text that prefixes a value" +.It N "note " "Field is text that follows a value" +.It P "padding " "Field is spaces needed for vertical alignment" +.It T "title " "Field is a title value for headings" +.It U "units " "Field is the units for the previous value field" +.It V "value " "Field is the name of field (the default)" +.It W "warning " "Field is a warning message" +.It \&[ "start-anchor" "Begin a section of anchored variable-width text" +.It \&] "stop-anchor " "End a section of anchored variable-width text" +.El +.Bd -literal -offset indent + EXAMPLE: + xo_emit("{L:Free}{D::}{P: }{:free/%u} {U:Blocks}\n", + free_blocks); +.Ed +.Pp +When a role is not provided, the "value" role is used as the default. +.Pp +Roles and modifiers can also use more verbose names, when preceeded by +a comma: +.Bd -literal -offset indent + EXAMPLE: + xo_emit("{,label:Free}{,decoration::}{,padding: }" + "{,value:free/%u} {,units:Blocks}\n", + free_blocks); +.Ed +.Ss "The Color Role ({C:})" +Colors and effects control how text values are displayed; they are +used for display styles (TEXT and HTML). +.Bd -literal -offset indent + xo_emit("{C:bold}{:value}{C:no-bold}\n", value); +.Ed +.Pp +Colors and effects remain in effect until modified by other "C"-role +fields. +.Bd -literal -offset indent + xo_emit("{C:bold}{C:inverse}both{C:no-bold}only inverse\n"); +.Ed +.Pp +If the content is empty, the "reset" action is performed. +.Bd -literal -offset indent + xo_emit("{C:both,underline}{:value}{C:}\n", value); +.Ed +.Pp +The content should be a comma-separated list of zero or more colors or +display effects. +.Bd -literal -offset indent + xo_emit("{C:bold,underline,inverse}All three{C:no-bold,no-inverse}\n"); +.Ed +.Pp +The color content can be either static, when placed directly within +the field descriptor, or a printf-style format descriptor can be used, +if preceded by a slash ("/"): +.Bd -literal -offset indent + xo_emit("{C:/%s%s}{:value}{C:}", need_bold ? "bold" : "", + need_underline ? "underline" : "", value); +.Ed +.Pp +Color names are prefixed with either "fg-" or "bg-" to change the +foreground and background colors, respectively. +.Bd -literal -offset indent + xo_emit("{C:/fg-%s,bg-%s}{Lwc:Cost}{:cost/%u}{C:reset}\n", + fg_color, bg_color, cost); +.Ed +.Pp +The following table lists the supported effects: +.Bl -column "no-underline" +.It Sy "Name " "Description" +.It "bg\-xxxxx " "Change background color" +.It "bold " "Start bold text effect" +.It "fg\-xxxxx " "Change foreground color" +.It "inverse " "Start inverse (aka reverse) text effect" +.It "no\-bold " "Stop bold text effect" +.It "no\-inverse " "Stop inverse (aka reverse) text effect" +.It "no\-underline " "Stop underline text effect" +.It "normal " "Reset effects (only)" +.It "reset " "Reset colors and effects (restore defaults)" +.It "underline " "Start underline text effect" +.El +.Pp +The following color names are supported: +.Bl -column "no-underline" +.It Sy "Name" +.It black +.It blue +.It cyan +.It default +.It green +.It magenta +.It red +.It white +.It yellow +.El +.Ss "The Decoration Role ({D:})" +Decorations are typically punctuation marks such as colons, +semi-colons, and commas used to decorate the text and make it simpler +for human readers. +By marking these distinctly, HTML usage scenarios +can use CSS to direct their display parameters. +.Bd -literal -offset indent + xo_emit("{D:((}{:name}{D:))}\\n", name); +.Ed +.Ss "The Gettext Role ({G:})" +.Nm libxo +supports internationalization (i18n) through its use of +.Xr gettext 3 . +Use the "{G:}" role to request that the remaining part of +the format string, following the "{G:}" field, be handled using +.Fn gettext . +Since +.Fn gettext +uses the string as the key into the message catalog, +.Nm libxo +uses a simplified version of the format string that removes +unimportant field formatting and modifiers, stopping minor formatting +changes from impacting the expensive translation process. +A developer +change such as changing "/%06d" to "/%08d" should not force hand +inspection of all .po files. +.Pp +The simplified version can be generated for a single message using the +"xopo -s " command, or an entire .pot can be translated using +the "xopo -f -o " command. +.Bd -literal -offset indent + xo_emit("{G:}Invalid token\n"); +.Ed +The {G:} role allows a domain name to be set. +.Fn gettext +calls will +continue to use that domain name until the current format string +processing is complete, enabling a library function to emit strings +using it's own catalog. +The domain name can be either static as the +content of the field, or a format can be used to get the domain name +from the arguments. +.Bd -literal -offset indent + xo_emit("{G:libc}Service unavailable in restricted mode\n"); +.Ed +.Ss "The Label Role ({L:})" +Labels are text that appears before a value. +.Bd -literal -offset indent + xo_emit("{Lwc:Cost}{:cost/%u}\\n", cost); +.Ed +.Ss "The Note Role ({N:})" +Notes are text that appears after a value. +.Bd -literal -offset indent + xo_emit("{:cost/%u} {N:per year}\\n", cost); +.Ed +.Ss "The Padding Role ({P:})" +Padding represents whitespace used before and between fields. +The padding content can be either static, when placed directly within +the field descriptor, or a printf-style format descriptor can be used, +if preceded by a slash ("/"): +.Bd -literal -offset indent + xo_emit("{P: }{Lwc:Cost}{:cost/%u}\\n", cost); + xo_emit("{P:/30s}{Lwc:Cost}{:cost/%u}\\n", "", cost); +.Ed +.Ss "The Title Role ({T:})" +Titles are heading or column headers that are meant to be displayed to +the user. +The title can be either static, when placed directly within +the field descriptor, or a printf-style format descriptor can be used, +if preceded by a slash ("/"): +.Bd -literal -offset indent + xo_emit("{T:Interface Statistics}\\n"); + xo_emit("{T:/%20.20s}{T:/%6.6s}\\n", "Item Name", "Cost"); +.Ed +.Ss "The Units Role ({U:})" +Units are the dimension by which values are measured, such as degrees, +miles, bytes, and decibels. +The units field carries this information +for the previous value field. +.Bd -literal -offset indent + xo_emit("{Lwc:Distance}{:distance/%u}{Uw:miles}\\n", miles); +.Ed +.Pp +Note that the sense of the 'w' modifier is reversed for units; +a blank is added before the contents, rather than after it. +.Pp +When the +.Dv XOF_UNITS +flag is set, units are rendered in XML as the +.Dq units +attribute: +.Bd -literal -offset indent + 50 +.Ed +.Pp +Units can also be rendered in HTML as the "data-units" attribute: +.Bd -literal -offset indent +
50
+.Ed +.Ss "The Value Role ({V:} and {:})" +The value role is used to represent the a data value that is +interesting for the non-display output styles (XML and JSON). +Value +is the default role; if no other role designation is given, the field +is a value. +The field name must appear within the field descriptor, +followed by one or two format descriptors. +The first format +descriptor is used for display styles (TEXT and HTML), while the +second one is used for encoding styles (XML and JSON). +If no second +format is given, the encoding format defaults to the first format, +with any minimum width removed. +If no first format is given, both +format descriptors default to "%s". +.Bd -literal -offset indent + xo_emit("{:length/%02u}x{:width/%02u}x{:height/%02u}\\n", + length, width, height); + xo_emit("{:author} wrote \"{:poem}\" in {:year/%4d}\\n, + author, poem, year); +.Ed +.Ss "The Anchor Roles ({[:} and {]:})" +The anchor roles allow a set of strings by be padded as a group, +but still be visible to +.Xr xo_emit 3 +as distinct fields. +Either the start +or stop anchor can give a field width and it can be either directly in +the descriptor or passed as an argument. +Any fields between the start +and stop anchor are padded to meet the minimum width given. +.Pp +To give a width directly, encode it as the content of the anchor tag: +.Bd -literal -offset indent + xo_emit("({[:10}{:min/%d}/{:max/%d}{]:})\\n", min, max); +.Ed +.Pp +To pass a width as an argument, use "%d" as the format, which must +appear after the "/". +Note that only "%d" is supported for widths. +Using any other value could ruin your day. +.Bd -literal -offset indent + xo_emit("({[:/%d}{:min/%d}/{:max/%d}{]:})\\n", width, min, max); +.Ed +.Pp +If the width is negative, padding will be added on the right, suitable +for left justification. +Otherwise the padding will be added to the +left of the fields between the start and stop anchors, suitable for +right justification. +If the width is zero, nothing happens. +If the +number of columns of output between the start and stop anchors is less +than the absolute value of the given width, nothing happens. +.Pp +Widths over 8k are considered probable errors and not supported. +If +.Dv XOF_WARN +is set, a warning will be generated. +.Ss "Field Modifiers" +Field modifiers are flags which modify the way content emitted for +particular output styles: +.Bl -column M "Name123456789" +.It Sy M "Name " "Description" +.It c "colon " "A colon ("":"") is appended after the label" +.It d "display " "Only emit field for display styles (text/HTML)" +.It e "encoding " "Only emit for encoding styles (XML/JSON)" +.It h "humanize (hn) " "Format large numbers in human-readable style" +.It " " "hn-space " "Humanize: Place space between numeric and unit" +.It " " "hn-decimal " "Humanize: Add a decimal digit, if number < 10" +.It " " "hn-1000 " "Humanize: Use 1000 as divisor instead of 1024" +.It k "key " "Field is a key, suitable for XPath predicates" +.It l "leaf-list " "Field is a leaf-list, a list of leaf values" +.It n "no-quotes " "Do not quote the field when using JSON style" +.It q "quotes " "Quote the field when using JSON style" +.It t "trim " "Trim leading and trailing whitespace" +.It w "white space " "A blank ("" "") is appended after the label" +.El +.Pp +For example, the modifier string "Lwc" means the field has a label +role (text that describes the next field) and should be followed by a +colon ('c') and a space ('w'). +The modifier string "Vkq" means the +field has a value role, that it is a key for the current instance, and +that the value should be quoted when encoded for JSON. +.Pp +Roles and modifiers can also use more verbose names, when preceeded by +a comma. +For example, the modifier string "Lwc" (or "L,white,colon") +means the field has a label role (text that describes the next field) +and should be followed by a colon ('c') and a space ('w'). +The modifier string "Vkq" (or ":key,quote") means the field has a value +role (the default role), that it is a key for the current instance, +and that the value should be quoted when encoded for JSON. +.Ss "The Colon Modifier ({c:})" +The colon modifier appends a single colon to the data value: +.Bd -literal -offset indent + EXAMPLE: + xo_emit("{Lc:Name}{:name}\\n", "phil"); + TEXT: + Name:phil +.Ed +.Pp +The colon modifier is only used for the TEXT and HTML output +styles. +It is commonly combined with the space modifier ('{w:}'). +It is purely a convenience feature. +.Ss "The Display Modifier ({d:})" +The display modifier indicated the field should only be generated for +the display output styles, TEXT and HTML. +.Bd -literal -offset indent + EXAMPLE: + xo_emit("{Lcw:Name}{d:name} {:id/%d}\\n", "phil", 1); + TEXT: + Name: phil 1 + XML: + 1 +.Ed +.Pp +The display modifier is the opposite of the encoding modifier, and +they are often used to give to distinct views of the underlying data. +.Ss "The Encoding Modifier ({e:})" +The encoding modifier indicated the field should only be generated for +the encoding output styles, such as JSON and XML. +.Bd -literal -offset indent + EXAMPLE: + xo_emit("{Lcw:Name}{:name} {e:id/%d}\\n", "phil", 1); + TEXT: + Name: phil + XML: + phil1 +.Ed +.Pp +The encoding modifier is the opposite of the display modifier, and +they are often used to give to distinct views of the underlying data. +.Ss "The Humanize Modifier ({h:})" +The humanize modifier is used to render large numbers as in a +human-readable format. +While numbers like "44470272" are completely readable to computers and +savants, humans will generally find "44M" more meaningful. +.Pp +"hn" can be used as an alias for "humanize". +.Pp +The humanize modifier only affects display styles (TEXT and HMTL). +The "no-humanize" option will block the function of the humanize modifier. +.Pp +There are a number of modifiers that affect details of humanization. +These are only available in as full names, not single characters. +The "hn-space" modifier places a space between the number and any +multiplier symbol, such as "M" or "K" (ex: "44 K"). +The "hn-decimal" modifier will add a decimal point and a single tenths digit +when the number is less than 10 (ex: "4.4K"). +The "hn-1000" modifier will use 1000 as divisor instead of 1024, following the +JEDEC-standard instead of the more natural binary powers-of-two +tradition. +.Bd -literal -offset indent + EXAMPLE: + xo_emit("{h:input/%u}, {h,hn-space:output/%u}, " + "{h,hn-decimal:errors/%u}, {h,hn-1000:capacity/%u}, " + "{h,hn-decimal:remaining/%u}\n", + input, output, errors, capacity, remaining); + TEXT: + 21, 57 K, 96M, 44M, 1.2G +.Ed +.Pp +In the HTML style, the original numeric value is rendered in the +"data-number" attribute on the
element: +.Bd -literal -offset indent +
96M
+.Ed +.Ss "The Gettext Modifier ({g:})" +The gettext modifier is used to translate individual fields using the +gettext domain (typically set using the "{G:}" role) and current +language settings. +Once libxo renders the field value, it is passed +to +.Xr gettext 3 , +where it is used as a key to find the native language +translation. +.Pp +In the following example, the strings "State" and "full" are passed +to +.Fn gettext +to find locale-based translated strings. +.Bd -literal -offset indent + xo_emit("{Lgwc:State}{g:state}\n", "full"); +.Ed +.Ss "The Key Modifier ({k:})" +The key modifier is used to indicate that a particular field helps +uniquely identify an instance of list data. +.Bd -literal -offset indent + EXAMPLE: + xo_open_list("user"); + for (i = 0; i < num_users; i++) { + xo_open_instance("user"); + xo_emit("User {k:name} has {:count} tickets\\n", + user[i].u_name, user[i].u_tickets); + xo_close_instance("user"); + } + xo_close_list("user"); +.Ed +.Pp +Currently the key modifier is only used when generating XPath values +for the HTML output style when +.Dv XOF_XPATH +is set, but other uses are likely in the near future. +.Ss "The Leaf-List Modifier ({l:})" +The leaf-list modifier is used to distinguish lists where each +instance consists of only a single value. In XML, these are +rendered as single elements, where JSON renders them as arrays. +.Bd -literal -offset indent + EXAMPLE: + xo_open_list("user"); + for (i = 0; i < num_users; i++) { + xo_emit("Member {l:name}\n", user[i].u_name); + } + xo_close_list("user"); + XML: + phil + pallavi + JSON: + "user": [ "phil", "pallavi" ] +.Ed +.Ss "The No-Quotes Modifier ({n:})" +The no-quotes modifier (and its twin, the 'quotes' modifier) affect +the quoting of values in the JSON output style. +JSON uses quotes for +string values, but no quotes for numeric, boolean, and null data. +.Xr xo_emit 3 +applies a simple heuristic to determine whether quotes are +needed, but often this needs to be controlled by the caller. +.Bd -literal -offset indent + EXAMPLE: + const char *bool = is_true ? "true" : "false"; + xo_emit("{n:fancy/%s}", bool); + JSON: + "fancy": true +.Ed +.Ss "The Plural Modifier ({p:})" +The plural modifier selects the appropriate plural form of an +expression based on the most recent number emitted and the current +language settings. +The contents of the field should be the singular +and plural English values, separated by a comma: +.Bd -literal -offset indent + xo_emit("{:bytes} {Ngp:byte,bytes}\n", bytes); +.Ed +The plural modifier is meant to work with the gettext modifier ({g:}) +but can work independently. +.Pp +When used without the gettext modifier or when the message does not +appear in the message catalog, the first token is chosen when the last +numeric value is equal to 1; otherwise the second value is used, +mimicking the simple pluralization rules of English. +.Pp +When used with the gettext modifier, the +.Xr ngettext 3 +function is +called to handle the heavy lifting, using the message catalog to +convert the singular and plural forms into the native language. +.Ss "The Quotes Modifier ({q:})" +The quotes modifier (and its twin, the 'no-quotes' modifier) affect +the quoting of values in the JSON output style. +JSON uses quotes for +string values, but no quotes for numeric, boolean, and null data. +.Xr xo_emit 3 +applies a simple heuristic to determine whether quotes are +needed, but often this needs to be controlled by the caller. +.Bd -literal -offset indent + EXAMPLE: + xo_emit("{q:time/%d}", 2014); + JSON: + "year": "2014" +.Ed +.Ss "The White Space Modifier ({w:})" +The white space modifier appends a single space to the data value: +.Bd -literal -offset indent + EXAMPLE: + xo_emit("{Lw:Name}{:name}\\n", "phil"); + TEXT: + Name phil +.Ed +.Pp +The white space modifier is only used for the TEXT and HTML output +styles. +It is commonly combined with the colon modifier ('{c:}'). +It is purely a convenience feature. +.Pp +Note that the sense of the 'w' modifier is reversed for the units role +({Uw:}); a blank is added before the contents, rather than after it. +.Ss "Field Formatting" +The field format is similar to the format string for +.Xr printf 3 . +Its use varies based on the role of the field, but generally is used to +format the field's contents. +.Pp +If the format string is not provided for a value field, it defaults +to "%s". +.Pp +Note a field definition can contain zero or more printf-style +.Dq directives , +which are sequences that start with a '%' and end with +one of following characters: "diouxXDOUeEfFgGaAcCsSp". +Each directive +is matched by one of more arguments to the +.Xr xo_emit 3 +function. +.Pp +The format string has the form: +.Bd -literal -offset indent + '%' format-modifier * format-character +.Ed +.Pp +The format- modifier can be: +.Bl -bullet +.It +a '#' character, indicating the output value should be prefixed with +"0x", typically to indicate a base 16 (hex) value. +.It +a minus sign ('-'), indicating the output value should be padded on +the right instead of the left. +.It +a leading zero ('0') indicating the output value should be padded on the +left with zeroes instead of spaces (' '). +.It +one or more digits ('0' - '9') indicating the minimum width of the +argument. +If the width in columns of the output value is less than +the minimum width, the value will be padded to reach the minimum. +.It +a period followed by one or more digits indicating the maximum +number of bytes which will be examined for a string argument, or the maximum +width for a non-string argument. +When handling ASCII strings this +functions as the field width but for multi-byte characters, a single +character may be composed of multiple bytes. +.Xr xo_emit 3 +will never dereference memory beyond the given number of bytes. +.It +a second period followed by one or more digits indicating the maximum +width for a string argument. +This modifier cannot be given for non-string arguments. +.It +one or more 'h' characters, indicating shorter input data. +.It +one or more 'l' characters, indicating longer input data. +.It +a 'z' character, indicating a 'size_t' argument. +.It +a 't' character, indicating a 'ptrdiff_t' argument. +.It +a ' ' character, indicating a space should be emitted before +positive numbers. +.It +a '+' character, indicating sign should emitted before any number. +.El +.Pp +Note that 'q', 'D', 'O', and 'U' are considered deprecated and will be +removed eventually. +.Pp +The format character is described in the following table: +.Bl -column C "Argument Type12" +.It Sy "C" "Argument Type " "Format" +.It d "int " "base 10 (decimal)" +.It i "int " "base 10 (decimal)" +.It o "int " "base 8 (octal)" +.It u "unsigned " "base 10 (decimal)" +.It x "unsigned " "base 16 (hex)" +.It X "unsigned long " "base 16 (hex)" +.It D "long " "base 10 (decimal)" +.It O "unsigned long " "base 8 (octal)" +.It U "unsigned long " "base 10 (decimal)" +.It e "double " "[-]d.ddde+-dd" +.It E "double " "[-]d.dddE+-dd" +.It f "double " "[-]ddd.ddd" +.It F "double " "[-]ddd.ddd" +.It g "double " "as 'e' or 'f'" +.It G "double " "as 'E' or 'F'" +.It a "double " "[-]0xh.hhhp[+-]d" +.It A "double " "[-]0Xh.hhhp[+-]d" +.It c "unsigned char " "a character" +.It C "wint_t " "a character" +.It s "char * " "a UTF-8 string" +.It S "wchar_t * " "a unicode/WCS string" +.It p "void * " "'%#lx'" +.El +.Pp +The 'h' and 'l' modifiers affect the size and treatment of the +argument: +.Bl -column "Mod" "d, i " "o, u, x, X " +.It Sy "Mod" "d, i " "o, u, x, X" +.It "hh " "signed char " "unsigned char" +.It "h " "short " "unsigned short" +.It "l " "long " "unsigned long" +.It "ll " "long long " "unsigned long long" +.It "j " "intmax_t " "uintmax_t" +.It "t " "ptrdiff_t " "ptrdiff_t" +.It "z " "size_t " "size_t" +.It "q " "quad_t " "u_quad_t" +.El +.Ss "UTF-8 and Locale Strings" +All strings for +.Nm libxo +must be UTF-8. +.Nm libxo +will handle turning them +into locale-based strings for display to the user. +.Pp +For strings, the 'h' and 'l' modifiers affect the interpretation of +the bytes pointed to argument. +The default '%s' string is a 'char *' +pointer to a string encoded as UTF-8. +Since UTF-8 is compatible with +.Em ASCII +data, a normal 7-bit +.Em ASCII +string can be used. +"%ls" expects a +"wchar_t *" pointer to a wide-character string, encoded as 32-bit +Unicode values. +"%hs" expects a "char *" pointer to a multi-byte +string encoded with the current locale, as given by the +.Ev LC_CTYPE , +.Ev LANG , +or +.Ev LC_ALL +environment variables. +The first of this list of +variables is used and if none of the variables are set, the locale defaults to +.Em UTF-8 . +.Pp +.Nm libxo +will +convert these arguments as needed to either UTF-8 (for XML, JSON, and +HTML styles) or locale-based strings for display in text style. +.Bd -literal -offset indent + xo_emit("All strings are utf-8 content {:tag/%ls}", + L"except for wide strings"); +.Ed +.Pp +"%S" is equivalent to "%ls". +.Pp +For example, a function is passed a locale-base name, a hat size, +and a time value. +The hat size is formatted in a UTF-8 (ASCII) +string, and the time value is formatted into a wchar_t string. +.Bd -literal -offset indent + void print_order (const char *name, int size, + struct tm *timep) { + char buf[32]; + const char *size_val = "unknown"; + + if (size > 0) + snprintf(buf, sizeof(buf), "%d", size); + size_val = buf; + } + + wchar_t when[32]; + wcsftime(when, sizeof(when), L"%d%b%y", timep); + + xo_emit("The hat for {:name/%hs} is {:size/%s}.\\n", + name, size_val); + xo_emit("It was ordered on {:order-time/%ls}.\\n", + when); + } +.Ed +.Pp +It is important to note that +.Xr xo_emit 3 +will perform the conversion +required to make appropriate output. +Text style output uses the +current locale (as described above), while XML, JSON, and HTML use +UTF-8. +.Pp +UTF-8 and locale-encoded strings can use multiple bytes to encode one +column of data. +The traditional "precision'" (aka "max-width") value +for "%s" printf formatting becomes overloaded since it specifies both +the number of bytes that can be safely referenced and the maximum +number of columns to emit. +.Xr xo_emit 3 +uses the precision as the former, +and adds a third value for specifying the maximum number of columns. +.Pp +In this example, the name field is printed with a minimum of 3 columns +and a maximum of 6. +Up to ten bytes are in used in filling those columns. +.Bd -literal -offset indent + xo_emit("{:name/%3.10.6s}", name); +.Ed +.Ss "Characters Outside of Field Definitions" +Characters in the format string that are not part of a field definition are +copied to the output for the TEXT style, and are ignored for the JSON +and XML styles. +For HTML, these characters are placed in a
with class "text". +.Bd -literal -offset indent + EXAMPLE: + xo_emit("The hat is {:size/%s}.\\n", size_val); + TEXT: + The hat is extra small. + XML: + extra small + JSON: + "size": "extra small" + HTML: +
The hat is
+
extra small
+
.
+.Ed +.Ss "'%n' is Not Supported" +.Nm libxo +does not support the '%n' directive. +It is a bad idea and we +just do not do it. +.Ss "The Encoding Format (eformat)" +The "eformat" string is the format string used when encoding the field +for JSON and XML. +If not provided, it defaults to the primary format +with any minimum width removed. +If the primary is not given, both default to "%s". +.Sh EXAMPLE +In this example, the value for the number of items in stock is emitted: +.Bd -literal -offset indent + xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\\n", + instock); +.Ed +.Pp +This call will generate the following output: +.Bd -literal -offset indent + TEXT: + In stock: 144 + XML: + 144 + JSON: + "in-stock": 144, + HTML: +
+
+
In stock
+
:
+
+
144
+
+.Ed +.Pp +Clearly HTML wins the verbosity award, and this output does +not include +.Dv XOF_XPATH +or +.Dv XOF_INFO +data, which would expand the penultimate line to: +.Bd -literal -offset indent +
144
+.Ed +.Sh WHAT MAKES A GOOD FIELD NAME? +To make useful, consistent field names, follow these guidelines: +.Ss "Use lower case, even for TLAs" +Lower case is more civilized. +Even TLAs should be lower case +to avoid scenarios where the differences between "XPath" and +"Xpath" drive your users crazy. +Using "xpath" is simpler and better. +.Ss "Use hyphens, not underscores" +Use of hyphens is traditional in XML, and the +.Dv XOF_UNDERSCORES +flag can be used to generate underscores in JSON, if desired. +But the raw field name should use hyphens. +.Ss "Use full words" +Do not abbreviate especially when the abbreviation is not obvious or +not widely used. +Use "data-size", not "dsz" or "dsize". +Use +"interface" instead of "ifname", "if-name", "iface", "if", or "intf". +.Ss "Use -" +Using the form - or -- helps in +making consistent, useful names, avoiding the situation where one app +uses "sent-packet" and another "packets-sent" and another +"packets-we-have-sent". +The can be dropped when it is +obvious, as can obvious words in the classification. +Use "receive-after-window-packets" instead of +"received-packets-of-data-after-window". +.Ss "Reuse existing field names" +Nothing is worse than writing expressions like: +.Bd -literal -offset indent + if ($src1/process[pid == $pid]/name == + $src2/proc-table/proc/p[process-id == $pid]/proc-name) { + ... + } +.Ed +.Pp +Find someone else who is expressing similar data and follow their +fields and hierarchy. +Remember the quote is not +.Dq "Consistency is the hobgoblin of little minds" +but +.Dq "A foolish consistency is the hobgoblin of little minds" . +.Ss "Think about your users" +Have empathy for your users, choosing clear and useful fields that +contain clear and useful data. +You may need to augment the display content with +.Xr xo_attr 3 +calls or "{e:}" fields to make the data useful. +.Ss "Do not use an arbitrary number postfix" +What does "errors2" mean? +No one will know. +"errors-after-restart" would be a better choice. +Think of your users, and think of the future. +If you make "errors2", the next guy will happily make +"errors3" and before you know it, someone will be asking what is the +difference between errors37 and errors63. +.Ss "Be consistent, uniform, unsurprising, and predictable" +Think of your field vocabulary as an API. +You want it useful, +expressive, meaningful, direct, and obvious. +You want the client +application's programmer to move between without the need to +understand a variety of opinions on how fields are named. +They should +see the system as a single cohesive whole, not a sack of cats. +.Pp +Field names constitute the means by which client programmers interact +with our system. +By choosing wise names now, you are making their lives better. +.Pp +After using +.Xr xolint 1 +to find errors in your field descriptors, use +.Dq "xolint -V" +to spell check your field names and to detect different +names for the same data. +.Dq dropped-short +and +.Dq dropped-too-short +are both reasonable names, but using them both will lead users to ask the +difference between the two fields. +If there is no difference, +use only one of the field names. +If there is a difference, change the +names to make that difference more obvious. +.Sh SEE ALSO +.Xr libxo 3 , +.Xr xolint 1 , +.Xr xo_emit 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_format.5 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_wcwidth.h =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_wcwidth.h (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_wcwidth.h (revision 296962) @@ -0,0 +1,313 @@ +/* + * This is an implementation of wcwidth() and wcswidth() (defined in + * IEEE Std 1002.1-2001) for Unicode. + * + * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html + * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html + * + * In fixed-width output devices, Latin characters all occupy a single + * "cell" position of equal width, whereas ideographic CJK characters + * occupy two such cells. Interoperability between terminal-line + * applications and (teletype-style) character terminals using the + * UTF-8 encoding requires agreement on which character should advance + * the cursor by how many cell positions. No established formal + * standards exist at present on which Unicode character shall occupy + * how many cell positions on character terminals. These routines are + * a first attempt of defining such behavior based on simple rules + * applied to data provided by the Unicode Consortium. + * + * For some graphical characters, the Unicode standard explicitly + * defines a character-cell width via the definition of the East Asian + * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. + * In all these cases, there is no ambiguity about which width a + * terminal shall use. For characters in the East Asian Ambiguous (A) + * class, the width choice depends purely on a preference of backward + * compatibility with either historic CJK or Western practice. + * Choosing single-width for these characters is easy to justify as + * the appropriate long-term solution, as the CJK practice of + * displaying these characters as double-width comes from historic + * implementation simplicity (8-bit encoded characters were displayed + * single-width and 16-bit ones double-width, even for Greek, + * Cyrillic, etc.) and not any typographic considerations. + * + * Much less clear is the choice of width for the Not East Asian + * (Neutral) class. Existing practice does not dictate a width for any + * of these characters. It would nevertheless make sense + * typographically to allocate two character cells to characters such + * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be + * represented adequately with a single-width glyph. The following + * routines at present merely assign a single-cell width to all + * neutral characters, in the interest of simplicity. This is not + * entirely satisfactory and should be reconsidered before + * establishing a formal standard in this area. At the moment, the + * decision which Not East Asian (Neutral) characters should be + * represented by double-width glyphs cannot yet be answered by + * applying a simple rule from the Unicode database content. Setting + * up a proper standard for the behavior of UTF-8 character terminals + * will require a careful analysis not only of each Unicode character, + * but also of each presentation form, something the author of these + * routines has avoided to do so far. + * + * http://www.unicode.org/unicode/reports/tr11/ + * + * Markus Kuhn -- 2007-05-26 (Unicode 5.0) + * + * Permission to use, copy, modify, and distribute this software + * for any purpose and without fee is hereby granted. The author + * disclaims all warranties with regard to this software. + * + * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c + */ + +#include + +struct interval { + wchar_t first; + wchar_t last; +}; + +/* auxiliary function for binary search in interval table */ +static int +xo_bisearch (wchar_t ucs, const struct interval *table, int max) +{ + int min = 0; + int mid; + + if (ucs < table[0].first || ucs > table[max].last) + return 0; + while (max >= min) { + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + + return 0; +} + + +/* The following two functions define the column width of an ISO 10646 + * character as follows: + * + * - The null character (U+0000) has a column width of 0. + * + * - Other C0/C1 control characters and DEL will lead to a return + * value of -1. + * + * - Non-spacing and enclosing combining characters (general + * category code Mn or Me in the Unicode database) have a + * column width of 0. + * + * - SOFT HYPHEN (U+00AD) has a column width of 1. + * + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. + * + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * + * - Spacing characters in the East Asian Wide (W) or East Asian + * Full-width (F) category as defined in Unicode Technical + * Report #11 have a column width of 2. + * + * - All remaining characters (including all printable + * ISO 8859-1 and WGL4 characters, Unicode control characters, + * etc.) have a column width of 1. + * + * This implementation assumes that wchar_t characters are encoded + * in ISO 10646. + */ + +static int +xo_wcwidth (wchar_t ucs) +{ + /* sorted list of non-overlapping intervals of non-spacing characters */ + /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ + static const struct interval combining[] = { + { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, + { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, + { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, + { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, + { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, + { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, + { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, + { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, + { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, + { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, + { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, + { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, + { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, + { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, + { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, + { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, + { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, + { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, + { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, + { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, + { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, + { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, + { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, + { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, + { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, + { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, + { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, + { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, + { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, + { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, + { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, + { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, + { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, + { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, + { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, + { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, + { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, + { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, + { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, + { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, + { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, + { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, + { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, + { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, + { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, + { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, + { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, + { 0xE0100, 0xE01EF } + }; + + /* test for 8-bit control characters */ + if (ucs == 0) + return 0; + if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) + return -1; + + /* binary search in table of non-spacing characters */ + if (xo_bisearch(ucs, combining, + sizeof(combining) / sizeof(struct interval) - 1)) + return 0; + + /* if we arrive here, ucs is not a combining or C0/C1 control character */ + + return 1 + + (ucs >= 0x1100 && + (ucs <= 0x115f || /* Hangul Jamo init. consonants */ + ucs == 0x2329 || ucs == 0x232a || + (ucs >= 0x2e80 && ucs <= 0xa4cf && + ucs != 0x303f) || /* CJK ... Yi */ + (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ + (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ + (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ + (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ + (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ + (ucs >= 0xffe0 && ucs <= 0xffe6) || + (ucs >= 0x20000 && ucs <= 0x2fffd) || + (ucs >= 0x30000 && ucs <= 0x3fffd))); +} + +#if UNUSED_CODE +static int xo_wcswidth(const wchar_t *pwcs, size_t n) +{ + int w, width = 0; + + for (;*pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth(*pwcs)) < 0) + return -1; + else + width += w; + + return width; +} + + +/* + * The following functions are the same as mk_wcwidth() and + * mk_wcswidth(), except that spacing characters in the East Asian + * Ambiguous (A) category as defined in Unicode Technical Report #11 + * have a column width of 2. This variant might be useful for users of + * CJK legacy encodings who want to migrate to UCS without changing + * the traditional terminal character-width behaviour. It is not + * otherwise recommended for general use. + */ +int mk_wcwidth_cjk(wchar_t ucs) +{ + /* sorted list of non-overlapping intervals of East Asian Ambiguous + * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */ + static const struct interval ambiguous[] = { + { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 }, + { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 }, + { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 }, + { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 }, + { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED }, + { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA }, + { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 }, + { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B }, + { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 }, + { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 }, + { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 }, + { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE }, + { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 }, + { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA }, + { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 }, + { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB }, + { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB }, + { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 }, + { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 }, + { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 }, + { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 }, + { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 }, + { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 }, + { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 }, + { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC }, + { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 }, + { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 }, + { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 }, + { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 }, + { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 }, + { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 }, + { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B }, + { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 }, + { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 }, + { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E }, + { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 }, + { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 }, + { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F }, + { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 }, + { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF }, + { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B }, + { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 }, + { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 }, + { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 }, + { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 }, + { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 }, + { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 }, + { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 }, + { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 }, + { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F }, + { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF }, + { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD } + }; + + /* binary search in table of non-spacing characters */ + if (xo_bisearch(ucs, ambiguous, + sizeof(ambiguous) / sizeof(struct interval) - 1)) + return 2; + + return mk_wcwidth(ucs); +} + + +int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) +{ + int w, width = 0; + + for (;*pwcs && n-- > 0; pwcs++) + if ((w = mk_wcwidth_cjk(*pwcs)) < 0) + return -1; + else + width += w; + + return width; +} +#endif /* UNUSED_CODE */ Property changes on: vendor/Juniper/libxo/dist/libxo/xo_wcwidth.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/Makefile.am =================================================================== --- vendor/Juniper/libxo/dist/libxo/Makefile.am (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/Makefile.am (revision 296962) @@ -0,0 +1,89 @@ +# +# Copyright 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +if LIBXO_WARNINGS_HIGH +LIBXO_WARNINGS = HIGH +endif +if HAVE_GCC +GCC_WARNINGS = yes +endif +include ${top_srcdir}/warnings.mk + +libxoincdir = ${includedir}/libxo + +AM_CFLAGS = \ + -I${top_srcdir} \ + ${WARNINGS} \ + ${GETTEXT_CFLAGS} + +AM_CFLAGS += \ + -DXO_ENCODERDIR=\"${XO_ENCODERDIR}\" + +lib_LTLIBRARIES = libxo.la + +LIBS = \ + ${GETTEXT_LIBS} + +libxoinc_HEADERS = \ + xo.h \ + xo_encoder.h + +noinst_HEADERS = \ + xo_buf.h \ + xo_humanize.h \ + xo_wcwidth.h + +libxo_la_SOURCES = \ + libxo.c \ + xo_encoder.c \ + xo_syslog.c + +man3_files = \ + libxo.3 \ + xo_attr.3 \ + xo_create.3 \ + xo_emit.3 \ + xo_emit_err.3 \ + xo_err.3 \ + xo_error.3 \ + xo_finish.3 \ + xo_flush.3 \ + xo_message.3 \ + xo_no_setlocale.3 \ + xo_open_container.3 \ + xo_open_list.3 \ + xo_open_marker.3 \ + xo_parse_args.3 \ + xo_set_allocator.3 \ + xo_set_flags.3 \ + xo_set_info.3 \ + xo_set_options.3 \ + xo_set_style.3 \ + xo_set_syslog_enterprise_id.3 \ + xo_set_version.3 \ + xo_set_writer.3 \ + xo_syslog.3 + +man5_files = \ + xo_format.5 + +man_MANS = ${man3_files} ${man5_files} + +EXTRA_DIST = \ + ${man_MANS} + +call-graph: + ${RM} libxo.o + ${MAKE} CC="clang -Xclang -analyze -Xclang \ + -analyzer-checker=debug.ViewCallGraph" libxo.o + +install-data-hook: + for file in ${man3_files}; do \ + cat ../libxo/add.man >> ${DESTDIR}${man3dir}/$$file ; done + for file in ${man5_files}; do \ + cat ../libxo/add.man >> ${DESTDIR}${man5dir}/$$file ; done Property changes on: vendor/Juniper/libxo/dist/libxo/Makefile.am ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/add.man =================================================================== --- vendor/Juniper/libxo/dist/libxo/add.man (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/add.man (revision 296962) @@ -0,0 +1,29 @@ +.Sh ADDITIONAL DOCUMENTATION +.Fx +uses +.Nm libxo +version 0.4.3. +Complete documentation can be found on github: +.Bd -literal -offset indent +http://juniper.github.io/libxo/0.4.3/libxo\-manual.html +.Ed +.Pp +.Nm libxo +lives on github as: +.Bd -literal -offset indent +https://github.com/Juniper/libxo +.Ed +.Pp +The latest release of +.Nm libxo +is available at: +.Bd -literal -offset indent +https://github.com/Juniper/libxo/releases +.Ed +.Sh HISTORY +The +.Nm libxo +library was added in +.Fx 11.0 . +.Sh AUTHOR +Phil Shafer Index: vendor/Juniper/libxo/dist/libxo/add.man.in =================================================================== --- vendor/Juniper/libxo/dist/libxo/add.man.in (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/add.man.in (revision 296962) @@ -0,0 +1,29 @@ +.Sh ADDITIONAL DOCUMENTATION +.Fx +uses +.Nm libxo +version @LIBXO_VERSION@. +Complete documentation can be found on github: +.Bd -literal -offset indent +http://juniper.github.io/libxo/@LIBXO_VERSION@/libxo\-manual.html +.Ed +.Pp +.Nm libxo +lives on github as: +.Bd -literal -offset indent +https://github.com/Juniper/libxo +.Ed +.Pp +The latest release of +.Nm libxo +is available at: +.Bd -literal -offset indent +https://github.com/Juniper/libxo/releases +.Ed +.Sh HISTORY +The +.Nm libxo +library was added in +.Fx 11.0 . +.Sh AUTHOR +Phil Shafer Property changes on: vendor/Juniper/libxo/dist/libxo/add.man.in ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/libxo.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/libxo.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/libxo.3 (revision 296962) @@ -0,0 +1,313 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 8, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm libxo +.Nd library for emitting text, XML, JSON, or HTML output +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Sh DESCRIPTION +The functions defined in +.Nm +are used to generate a choice of +.Em TEXT , +.Em XML , +.Em JSON , +or +.Em HTML +output. +A common set of functions are used, with +command line switches passed to the library to control the details of +the output. +.Pp +Most commands emit text output aimed at humans. +It is designed +to be parsed and understood by a user. +Humans are gifted at extracting +details and pattern matching. +Often programmers need to extract +information from this human-oriented output. +Programmers use tools +like +.Xr grep 1 , +.Xr awk 1 , +and regular expressions to ferret out the pieces of +information they need. +Such solutions are fragile and require +updates when output contents change or evolve, requiring testing and +validation. +.Pp +Modern tool developers favor encoding schemes like XML and JSON, +which allow trivial parsing and extraction of data. +Such formats are +simple, well understood, hierarchical, easily parsed, and often +integrate easier with common tools and environments. +.Pp +In addition, modern reality means that more output ends up in web +browsers than in terminals, making HTML output valuable. +.Pp +.Nm +allows a single set of function calls in source code to generate +traditional text output, as well as XML and JSON formatted data. +HTML +can also be generated; "
" elements surround the traditional text +output, with attributes that detail how to render the data. +.Pp +There are four encoding styles supported by +.Nm : +.Bl -bullet +.It +TEXT output can be display on a terminal session, allowing +compatibility with traditional command line usage. +.It +XML output is suitable for tools like XPath and protocols like +NETCONF. +.It +JSON output can be used for RESTful APIs and integration with +languages like Javascript and Python. +.It +HTML can be matched with a small CSS file to permit rendering in any +HTML5 browser. +.El +.Pp +In general, XML and JSON are suitable for encoding data, while TEXT is +suited for terminal output and HTML is suited for display in a web +browser (see +.Xr xohtml 1 ). +.Pp +The +.Nm +library allows an application to generate text, XML, JSON, +and HTML output using a common set of function calls. +The application +decides at run time which output style should be produced. +The +application calls a function +.Xr xo_emit 3 +to product output that is +described in a format string. +A +.Dq field descriptor +tells +.Nm +what the field is and what it means. +Each field descriptor is placed in +braces with a printf-like format string: +.Bd -literal -offset indent + xo_emit(" {:lines/%7ju} {:words/%7ju} " + "{:characters/%7ju}{d:filename/%s}\\n", + linect, wordct, charct, file); +.Ed +.Pp +Each field can have a role, with the 'value' role being the default, +and the role tells +.Nm +how and when to render that field, as well as +a +.Xr printf 3 Ns -like +format string. +.Pp +Output +can then be generated in various style, using the "--libxo" option. +.Sh DEFAULT HANDLE +Handles give an abstraction for +.Nm +that encapsulates the state of a +stream of output. +Handles have the data type "xo_handle_t" and are +opaque to the caller. +.Pp +The library has a default handle that is automatically initialized. +By default, this handle will send text style output to standard output. +The +.Xr xo_set_style 3 +and +.Xr xo_set_flags 3 +functions can be used to change this +behavior. +.Pp +Many +.Nm +functions take a handle as their first parameter; most that +do not use the default handle. +Any function taking a handle can +be passed +.Dv NULL +to access the default handle. +.Pp +For the typical command that is generating output on standard output, +there is no need to create an explicit handle, but they are available +when needed, e.g., for daemons that generate multiple streams of +output. +.Sh FUNCTION OVERVIEW +The +.Nm +library includes the following functions: +.Bl -tag -width "xo_close_container_hd" +.It Sy "Function Description" +.It Fn xo_attr +.It Fn xo_attr_h +.It Fn xo_attr_hv +Allows the caller to emit XML attributes with the next open element. +.It Fn xo_create +.It Fn xo_create_to_file +Allow the caller to create a new handle. +Note that +.Nm +has a default handle that allows the caller to avoid use of an +explicitly created handle. +Only callers writing to files other than +.Dv stdout +would need to call +.Fn xo_create . +.It Fn xo_destroy +Frees any resources associated with the handle, including the handle +itself. +.It Fn xo_emit +.It Fn xo_emit_h +.It Fn xo_emit_hv +Emit formatted output. +The +.Fa fmt +string controls the conversion of the remaining arguments into +formatted output. +See +.Xr xo_format 5 +for details. +.It Fn xo_emit_warn +.It Fn xo_emit_warnx +.It Fn xo_emit_warn_c +.It Fn xo_emit_warn_hc +.It Fn xo_emit_err +.It Fn xo_emit_errc +.It Fn xo_emit_errx +These functions are mildly compatible with their standard libc +namesakes, but use the format string defined in +.Xr xo_format 5 . +While there is an increased cost for converting the strings, the +output provided can be richer and more useful. See also +.Xr xo_err 3 +.It Fn xo_warn +.It Fn xo_warnx +.It Fn xo_warn_c +.It Fn xo_warn_hc +.It Fn xo_err +.It Fn xo_errc +.It Fn xo_errx +.It Fn xo_message +.It Fn xo_message_c +.It Fn xo_message_hc +.It Fn xo_message_hcv +These functions are meant to be compatible with their standard libc namesakes. +.It Fn xo_finish +.It Fn xo_finish_h +Flush output, close open construct, and complete any pending +operations. +.It Fn xo_flush +.It Fn xo_flush_h +Allow the caller to flush any pending output for a handle. +.It Fn xo_no_setlocale +Direct +.Nm +to avoid initializing the locale. +This function should be called before any other +.Nm +function is called. +.It Fn xo_open_container +.It Fn xo_open_container_h +.It Fn xo_open_container_hd +.It Fn xo_open_container_d +.It Fn xo_close_container +.It Fn xo_close_container_h +.It Fn xo_close_container_hd +.It Fn xo_close_container_d +Containers a singleton levels of hierarchy, typically used to organize +related content. +.It Fn xo_open_list_h +.It Fn xo_open_list +.It Fn xo_open_list_hd +.It Fn xo_open_list_d +.It Fn xo_open_instance_h +.It Fn xo_open_instance +.It Fn xo_open_instance_hd +.It Fn xo_open_instance_d +.It Fn xo_close_instance_h +.It Fn xo_close_instance +.It Fn xo_close_instance_hd +.It Fn xo_close_instance_d +.It Fn xo_close_list_h +.It Fn xo_close_list +.It Fn xo_close_list_hd +.It Fn xo_close_list_d +Lists are levels of hierarchy that can appear multiple times within +the same parent. +Two calls are needed to encapsulate them, one for +the list and one for each instance of that list. +Typically +.Fn xo_open_list +and +.Fn xo_close_list +are called outside a +for-loop, where +.Fn xo_open_instance +it called at the top of the loop, and +.Fn xo_close_instance +is called at the bottom of the loop. +.It Fn xo_parse_args +Inspects command line arguments for directions to +.Nm . +This function should be called before +.Va argv +is inspected by the application. +.It Fn xo_set_allocator +Instructs +.Nm +to use an alternative memory allocator and deallocator. +.It Fn xo_set_flags +.It Fn xo_clear_flags +Change the flags set for a handle. +.It Fn xo_set_info +Provides additional information about elements for use with HTML +rendering. +.It Fn xo_set_options +Changes formatting options used by handle. +.It Fn xo_set_style +.It Fn xo_set_style_name +Changes the output style used by a handle. +.It Fn xo_set_writer +Instructs +.Nm +to use an alternative set of low-level output functions. +.El +.Sh SEE ALSO +.Xr xo 1 , +.Xr xolint 1 , +.Xr xo_attr 3 , +.Xr xo_create 3 , +.Xr xo_emit 3 , +.Xr xo_emit_err 3 , +.Xr xo_err 3 , +.Xr xo_finish 3 , +.Xr xo_flush 3 , +.Xr xo_no_setlocale 3 , +.Xr xo_open_container 3 , +.Xr xo_open_list 3 , +.Xr xo_parse_args 3 , +.Xr xo_set_allocator 3 , +.Xr xo_set_flags 3 , +.Xr xo_set_info 3 , +.Xr xo_set_options 3 , +.Xr xo_set_style 3 , +.Xr xo_set_writer 3 , +.Xr xo_format 5 Property changes on: vendor/Juniper/libxo/dist/libxo/libxo.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/libxo.c =================================================================== --- vendor/Juniper/libxo/dist/libxo/libxo.c (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/libxo.c (revision 296962) @@ -0,0 +1,7659 @@ +/* + * Copyright (c) 2014-2015, Juniper Networks, Inc. + * All rights reserved. + * This SOFTWARE is licensed under the LICENSE provided in the + * ../Copyright file. By downloading, installing, copying, or otherwise + * using the SOFTWARE, you agree to be bound by the terms of that + * LICENSE. + * Phil Shafer, July 2014 + * + * This is the implementation of libxo, the formatting library that + * generates multiple styles of output from a single code path. + * Command line utilities can have their normal text output while + * automation tools can see XML or JSON output, and web tools can use + * HTML output that encodes the text output annotated with additional + * information. Specialized encoders can be built that allow custom + * encoding including binary ones like CBOR, thrift, protobufs, etc. + * + * Full documentation is available in ./doc/libxo.txt or online at: + * http://juniper.github.io/libxo/libxo-manual.html + * + * For first time readers, the core bits of code to start looking at are: + * - xo_do_emit() -- the central function of the library + * - xo_do_format_field() -- handles formatting a single field + * - xo_transiton() -- the state machine that keeps things sane + * and of course the "xo_handle_t" data structure, which carries all + * configuration and state. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xo_config.h" +#include "xo.h" +#include "xo_encoder.h" +#include "xo_buf.h" + +/* + * We ask wcwidth() to do an impossible job, really. It's supposed to + * need to tell us the number of columns consumed to display a unicode + * character. It returns that number without any sort of context, but + * we know they are characters whose glyph differs based on placement + * (end of word, middle of word, etc) and many that affect characters + * previously emitted. Without content, it can't hope to tell us. + * But it's the only standard tool we've got, so we use it. We would + * use wcswidth() but it typically just loops thru adding the results + * of wcwidth() calls in an entirely unhelpful way. + * + * Even then, there are many poor implementations (macosx), so we have + * to carry our own. We could have configure.ac test this (with + * something like 'assert(wcwidth(0x200d) == 0)'), but it would have + * to run a binary, which breaks cross-compilation. Hmm... I could + * run this test at init time and make a warning for our dear user. + * + * Anyhow, it remains a best-effort sort of thing. And it's all made + * more hopeless because we assume the display code doing the rendering is + * playing by the same rules we are. If it display 0x200d as a square + * box or a funky question mark, the output will be hosed. + */ +#ifdef LIBXO_WCWIDTH +#include "xo_wcwidth.h" +#else /* LIBXO_WCWIDTH */ +#define xo_wcwidth(_x) wcwidth(_x) +#endif /* LIBXO_WCWIDTH */ + +#ifdef HAVE_STDIO_EXT_H +#include +#endif /* HAVE_STDIO_EXT_H */ + +/* + * humanize_number is a great function, unless you don't have it. So + * we carry one in our pocket. + */ +#ifdef HAVE_HUMANIZE_NUMBER +#include +#define xo_humanize_number humanize_number +#else /* HAVE_HUMANIZE_NUMBER */ +#include "xo_humanize.h" +#endif /* HAVE_HUMANIZE_NUMBER */ + +#ifdef HAVE_GETTEXT +#include +#endif /* HAVE_GETTEXT */ + +/* + * Three styles of specifying thread-local variables are supported. + * configure.ac has the brains to run each possibility thru the + * compiler and see what works; we are left to define the THREAD_LOCAL + * macro to the right value. Most toolchains (clang, gcc) use + * "before", but some (borland) use "after" and I've heard of some + * (ms) that use __declspec. Any others out there? + */ +#define THREAD_LOCAL_before 1 +#define THREAD_LOCAL_after 2 +#define THREAD_LOCAL_declspec 3 + +#ifndef HAVE_THREAD_LOCAL +#define THREAD_LOCAL(_x) _x +#elif HAVE_THREAD_LOCAL == THREAD_LOCAL_before +#define THREAD_LOCAL(_x) __thread _x +#elif HAVE_THREAD_LOCAL == THREAD_LOCAL_after +#define THREAD_LOCAL(_x) _x __thread +#elif HAVE_THREAD_LOCAL == THREAD_LOCAL_declspec +#define THREAD_LOCAL(_x) __declspec(_x) +#else +#error unknown thread-local setting +#endif /* HAVE_THREADS_H */ + +const char xo_version[] = LIBXO_VERSION; +const char xo_version_extra[] = LIBXO_VERSION_EXTRA; + +#ifndef UNUSED +#define UNUSED __attribute__ ((__unused__)) +#endif /* UNUSED */ + +#define XO_INDENT_BY 2 /* Amount to indent when pretty printing */ +#define XO_DEPTH 128 /* Default stack depth */ +#define XO_MAX_ANCHOR_WIDTH (8*1024) /* Anything wider is just sillyb */ + +#define XO_FAILURE_NAME "failure" + +/* Flags for the stack frame */ +typedef unsigned xo_xsf_flags_t; /* XSF_* flags */ +#define XSF_NOT_FIRST (1<<0) /* Not the first element */ +#define XSF_LIST (1<<1) /* Frame is a list */ +#define XSF_INSTANCE (1<<2) /* Frame is an instance */ +#define XSF_DTRT (1<<3) /* Save the name for DTRT mode */ + +#define XSF_CONTENT (1<<4) /* Some content has been emitted */ +#define XSF_EMIT (1<<5) /* Some field has been emitted */ +#define XSF_EMIT_KEY (1<<6) /* A key has been emitted */ +#define XSF_EMIT_LEAF_LIST (1<<7) /* A leaf-list field has been emitted */ + +/* These are the flags we propagate between markers and their parents */ +#define XSF_MARKER_FLAGS \ + (XSF_NOT_FIRST | XSF_CONTENT | XSF_EMIT | XSF_EMIT_KEY | XSF_EMIT_LEAF_LIST ) + +/* + * A word about states: We use a finite state machine (FMS) approach + * to help remove fragility from the caller's code. Instead of + * requiring a specific order of calls, we'll allow the caller more + * flexibility and make the library responsible for recovering from + * missed steps. The goal is that the library should not be capable + * of emitting invalid xml or json, but the developer shouldn't need + * to know or understand all the details about these encodings. + * + * You can think of states as either states or events, since they + * function rather like both. None of the XO_CLOSE_* events will + * persist as states, since the matching stack frame will be popped. + * Same is true of XSS_EMIT, which is an event that asks us to + * prep for emitting output fields. + */ + +/* Stack frame states */ +typedef unsigned xo_state_t; +#define XSS_INIT 0 /* Initial stack state */ +#define XSS_OPEN_CONTAINER 1 +#define XSS_CLOSE_CONTAINER 2 +#define XSS_OPEN_LIST 3 +#define XSS_CLOSE_LIST 4 +#define XSS_OPEN_INSTANCE 5 +#define XSS_CLOSE_INSTANCE 6 +#define XSS_OPEN_LEAF_LIST 7 +#define XSS_CLOSE_LEAF_LIST 8 +#define XSS_DISCARDING 9 /* Discarding data until recovered */ +#define XSS_MARKER 10 /* xo_open_marker's marker */ +#define XSS_EMIT 11 /* xo_emit has a leaf field */ +#define XSS_EMIT_LEAF_LIST 12 /* xo_emit has a leaf-list ({l:}) */ +#define XSS_FINISH 13 /* xo_finish was called */ + +#define XSS_MAX 13 + +#define XSS_TRANSITION(_old, _new) ((_old) << 8 | (_new)) + +/* + * xo_stack_t: As we open and close containers and levels, we + * create a stack of frames to track them. This is needed for + * XOF_WARN and XOF_XPATH. + */ +typedef struct xo_stack_s { + xo_xsf_flags_t xs_flags; /* Flags for this frame */ + xo_state_t xs_state; /* State for this stack frame */ + char *xs_name; /* Name (for XPath value) */ + char *xs_keys; /* XPath predicate for any key fields */ +} xo_stack_t; + +/* + * libxo supports colors and effects, for those who like them. + * XO_COL_* ("colors") refers to fancy ansi codes, while X__EFF_* + * ("effects") are bits since we need to maintain state. + */ +#define XO_COL_DEFAULT 0 +#define XO_COL_BLACK 1 +#define XO_COL_RED 2 +#define XO_COL_GREEN 3 +#define XO_COL_YELLOW 4 +#define XO_COL_BLUE 5 +#define XO_COL_MAGENTA 6 +#define XO_COL_CYAN 7 +#define XO_COL_WHITE 8 + +#define XO_NUM_COLORS 9 + +/* + * Yes, there's no blink. We're civilized. We like users. Blink + * isn't something one does to someone you like. Friends don't let + * friends use blink. On friends. You know what I mean. Blink is + * like, well, it's like bursting into show tunes at a funeral. It's + * just not done. Not something anyone wants. And on those rare + * instances where it might actually be appropriate, it's still wrong, + * since it's likely done by the wrong person for the wrong reason. + * Just like blink. And if I implemented blink, I'd be like a funeral + * director who adds "Would you like us to burst into show tunes?" on + * the list of questions asked while making funeral arrangements. + * It's formalizing wrongness in the wrong way. And we're just too + * civilized to do that. Hhhmph! + */ +#define XO_EFF_RESET (1<<0) +#define XO_EFF_NORMAL (1<<1) +#define XO_EFF_BOLD (1<<2) +#define XO_EFF_UNDERLINE (1<<3) +#define XO_EFF_INVERSE (1<<4) + +#define XO_EFF_CLEAR_BITS XO_EFF_RESET /* Reset gets reset, surprisingly */ + +typedef uint8_t xo_effect_t; +typedef uint8_t xo_color_t; +typedef struct xo_colors_s { + xo_effect_t xoc_effects; /* Current effect set */ + xo_color_t xoc_col_fg; /* Foreground color */ + xo_color_t xoc_col_bg; /* Background color */ +} xo_colors_t; + +/* + * xo_handle_t: this is the principle data structure for libxo. + * It's used as a store for state, options, content, and all manor + * of other information. + */ +struct xo_handle_s { + xo_xof_flags_t xo_flags; /* Flags (XOF_*) from the user*/ + xo_xof_flags_t xo_iflags; /* Internal flags (XOIF_*) */ + xo_style_t xo_style; /* XO_STYLE_* value */ + unsigned short xo_indent; /* Indent level (if pretty) */ + unsigned short xo_indent_by; /* Indent amount (tab stop) */ + xo_write_func_t xo_write; /* Write callback */ + xo_close_func_t xo_close; /* Close callback */ + xo_flush_func_t xo_flush; /* Flush callback */ + xo_formatter_t xo_formatter; /* Custom formating function */ + xo_checkpointer_t xo_checkpointer; /* Custom formating support function */ + void *xo_opaque; /* Opaque data for write function */ + xo_buffer_t xo_data; /* Output data */ + xo_buffer_t xo_fmt; /* Work area for building format strings */ + xo_buffer_t xo_attrs; /* Work area for building XML attributes */ + xo_buffer_t xo_predicate; /* Work area for building XPath predicates */ + xo_stack_t *xo_stack; /* Stack pointer */ + int xo_depth; /* Depth of stack */ + int xo_stack_size; /* Size of the stack */ + xo_info_t *xo_info; /* Info fields for all elements */ + int xo_info_count; /* Number of info entries */ + va_list xo_vap; /* Variable arguments (stdargs) */ + char *xo_leading_xpath; /* A leading XPath expression */ + mbstate_t xo_mbstate; /* Multi-byte character conversion state */ + unsigned xo_anchor_offset; /* Start of anchored text */ + unsigned xo_anchor_columns; /* Number of columns since the start anchor */ + int xo_anchor_min_width; /* Desired width of anchored text */ + unsigned xo_units_offset; /* Start of units insertion point */ + unsigned xo_columns; /* Columns emitted during this xo_emit call */ + uint8_t xo_color_map_fg[XO_NUM_COLORS]; /* Foreground color mappings */ + uint8_t xo_color_map_bg[XO_NUM_COLORS]; /* Background color mappings */ + xo_colors_t xo_colors; /* Current color and effect values */ + xo_buffer_t xo_color_buf; /* HTML: buffer of colors and effects */ + char *xo_version; /* Version string */ + int xo_errno; /* Saved errno for "%m" */ + char *xo_gt_domain; /* Gettext domain, suitable for dgettext(3) */ + xo_encoder_func_t xo_encoder; /* Encoding function */ + void *xo_private; /* Private data for external encoders */ +}; + +/* Flag operations */ +#define XOF_BIT_ISSET(_flag, _bit) (((_flag) & (_bit)) ? 1 : 0) +#define XOF_BIT_SET(_flag, _bit) do { (_flag) |= (_bit); } while (0) +#define XOF_BIT_CLEAR(_flag, _bit) do { (_flag) &= ~(_bit); } while (0) + +#define XOF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_flags, _bit) +#define XOF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_flags, _bit) +#define XOF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_flags, _bit) + +#define XOIF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_iflags, _bit) +#define XOIF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_iflags, _bit) +#define XOIF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_iflags, _bit) + +/* Internal flags */ +#define XOIF_REORDER XOF_BIT(0) /* Reordering fields; record field info */ +#define XOIF_DIV_OPEN XOF_BIT(1) /* A
is open */ +#define XOIF_TOP_EMITTED XOF_BIT(2) /* The top JSON braces have been emitted */ +#define XOIF_ANCHOR XOF_BIT(3) /* An anchor is in place */ + +#define XOIF_UNITS_PENDING XOF_BIT(4) /* We have a units-insertion pending */ +#define XOIF_INIT_IN_PROGRESS XOF_BIT(5) /* Init of handle is in progress */ + +/* Flags for formatting functions */ +typedef unsigned long xo_xff_flags_t; +#define XFF_COLON (1<<0) /* Append a ":" */ +#define XFF_COMMA (1<<1) /* Append a "," iff there's more output */ +#define XFF_WS (1<<2) /* Append a blank */ +#define XFF_ENCODE_ONLY (1<<3) /* Only emit for encoding styles (XML, JSON) */ + +#define XFF_QUOTE (1<<4) /* Force quotes */ +#define XFF_NOQUOTE (1<<5) /* Force no quotes */ +#define XFF_DISPLAY_ONLY (1<<6) /* Only emit for display styles (text, html) */ +#define XFF_KEY (1<<7) /* Field is a key (for XPath) */ + +#define XFF_XML (1<<8) /* Force XML encoding style (for XPath) */ +#define XFF_ATTR (1<<9) /* Escape value using attribute rules (XML) */ +#define XFF_BLANK_LINE (1<<10) /* Emit a blank line */ +#define XFF_NO_OUTPUT (1<<11) /* Do not make any output */ + +#define XFF_TRIM_WS (1<<12) /* Trim whitespace off encoded values */ +#define XFF_LEAF_LIST (1<<13) /* A leaf-list (list of values) */ +#define XFF_UNESCAPE (1<<14) /* Need to printf-style unescape the value */ +#define XFF_HUMANIZE (1<<15) /* Humanize the value (for display styles) */ + +#define XFF_HN_SPACE (1<<16) /* Humanize: put space before suffix */ +#define XFF_HN_DECIMAL (1<<17) /* Humanize: add one decimal place if <10 */ +#define XFF_HN_1000 (1<<18) /* Humanize: use 1000, not 1024 */ +#define XFF_GT_FIELD (1<<19) /* Call gettext() on a field */ + +#define XFF_GT_PLURAL (1<<20) /* Call dngettext to find plural form */ + +/* Flags to turn off when we don't want i18n processing */ +#define XFF_GT_FLAGS (XFF_GT_FIELD | XFF_GT_PLURAL) + +/* + * Normal printf has width and precision, which for strings operate as + * min and max number of columns. But this depends on the idea that + * one byte means one column, which UTF-8 and multi-byte characters + * pitches on its ear. It may take 40 bytes of data to populate 14 + * columns, but we can't go off looking at 40 bytes of data without the + * caller's permission for fear/knowledge that we'll generate core files. + * + * So we make three values, distinguishing between "max column" and + * "number of bytes that we will inspect inspect safely" We call the + * later "size", and make the format "%[[].[[].]]s". + * + * Under the "first do no harm" theory, we default "max" to "size". + * This is a reasonable assumption for folks that don't grok the + * MBS/WCS/UTF-8 world, and while it will be annoying, it will never + * be evil. + * + * For example, xo_emit("{:tag/%-14.14s}", buf) will make 14 + * columns of output, but will never look at more than 14 bytes of the + * input buffer. This is mostly compatible with printf and caller's + * expectations. + * + * In contrast xo_emit("{:tag/%-14..14s}", buf) will look at however + * many bytes (or until a NUL is seen) are needed to fill 14 columns + * of output. xo_emit("{:tag/%-14.*.14s}", xx, buf) will look at up + * to xx bytes (or until a NUL is seen) in order to fill 14 columns + * of output. + * + * It's fairly amazing how a good idea (handle all languages of the + * world) blows such a big hole in the bottom of the fairly weak boat + * that is C string handling. The simplicity and completenesss are + * sunk in ways we haven't even begun to understand. + */ +#define XF_WIDTH_MIN 0 /* Minimal width */ +#define XF_WIDTH_SIZE 1 /* Maximum number of bytes to examine */ +#define XF_WIDTH_MAX 2 /* Maximum width */ +#define XF_WIDTH_NUM 3 /* Numeric fields in printf (min.size.max) */ + +/* Input and output string encodings */ +#define XF_ENC_WIDE 1 /* Wide characters (wchar_t) */ +#define XF_ENC_UTF8 2 /* UTF-8 */ +#define XF_ENC_LOCALE 3 /* Current locale */ + +/* + * A place to parse printf-style format flags for each field + */ +typedef struct xo_format_s { + unsigned char xf_fc; /* Format character */ + unsigned char xf_enc; /* Encoding of the string (XF_ENC_*) */ + unsigned char xf_skip; /* Skip this field */ + unsigned char xf_lflag; /* 'l' (long) */ + unsigned char xf_hflag;; /* 'h' (half) */ + unsigned char xf_jflag; /* 'j' (intmax_t) */ + unsigned char xf_tflag; /* 't' (ptrdiff_t) */ + unsigned char xf_zflag; /* 'z' (size_t) */ + unsigned char xf_qflag; /* 'q' (quad_t) */ + unsigned char xf_seen_minus; /* Seen a minus */ + int xf_leading_zero; /* Seen a leading zero (zero fill) */ + unsigned xf_dots; /* Seen one or more '.'s */ + int xf_width[XF_WIDTH_NUM]; /* Width/precision/size numeric fields */ + unsigned xf_stars; /* Seen one or more '*'s */ + unsigned char xf_star[XF_WIDTH_NUM]; /* Seen one or more '*'s */ +} xo_format_t; + +/* + * This structure represents the parsed field information, suitable for + * processing by xo_do_emit and anything else that needs to parse fields. + * Note that all pointers point to the main format string. + * + * XXX This is a first step toward compilable or cachable format + * strings. We can also cache the results of dgettext when no format + * is used, assuming the 'p' modifier has _not_ been set. + */ +typedef struct xo_field_info_s { + xo_xff_flags_t xfi_flags; /* Flags for this field */ + unsigned xfi_ftype; /* Field type, as character (e.g. 'V') */ + const char *xfi_start; /* Start of field in the format string */ + const char *xfi_content; /* Field's content */ + const char *xfi_format; /* Field's Format */ + const char *xfi_encoding; /* Field's encoding format */ + const char *xfi_next; /* Next character in format string */ + unsigned xfi_len; /* Length of field */ + unsigned xfi_clen; /* Content length */ + unsigned xfi_flen; /* Format length */ + unsigned xfi_elen; /* Encoding length */ + unsigned xfi_fnum; /* Field number (if used; 0 otherwise) */ + unsigned xfi_renum; /* Reordered number (0 == no renumbering) */ +} xo_field_info_t; + +/* + * We keep a 'default' handle to allow callers to avoid having to + * allocate one. Passing NULL to any of our functions will use + * this default handle. Most functions have a variant that doesn't + * require a handle at all, since most output is to stdout, which + * the default handle handles handily. + */ +static THREAD_LOCAL(xo_handle_t) xo_default_handle; +static THREAD_LOCAL(int) xo_default_inited; +static int xo_locale_inited; +static const char *xo_program; + +/* + * To allow libxo to be used in diverse environment, we allow the + * caller to give callbacks for memory allocation. + */ +xo_realloc_func_t xo_realloc = realloc; +xo_free_func_t xo_free = free; + +/* Forward declarations */ +static void +xo_failure (xo_handle_t *xop, const char *fmt, ...); + +static int +xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name, + xo_state_t new_state); + +static void +xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, + const char *name, int nlen, + const char *value, int vlen, + const char *encoding, int elen); + +static void +xo_anchor_clear (xo_handle_t *xop); + +/* + * xo_style is used to retrieve the current style. When we're built + * for "text only" mode, we use this function to drive the removal + * of most of the code in libxo. We return a constant and the compiler + * happily removes the non-text code that is not longer executed. This + * trims our code nicely without needing to trampel perfectly readable + * code with ifdefs. + */ +static inline xo_style_t +xo_style (xo_handle_t *xop UNUSED) +{ +#ifdef LIBXO_TEXT_ONLY + return XO_STYLE_TEXT; +#else /* LIBXO_TEXT_ONLY */ + return xop->xo_style; +#endif /* LIBXO_TEXT_ONLY */ +} + +/* + * Callback to write data to a FILE pointer + */ +static int +xo_write_to_file (void *opaque, const char *data) +{ + FILE *fp = (FILE *) opaque; + + return fprintf(fp, "%s", data); +} + +/* + * Callback to close a file + */ +static void +xo_close_file (void *opaque) +{ + FILE *fp = (FILE *) opaque; + + fclose(fp); +} + +/* + * Callback to flush a FILE pointer + */ +static int +xo_flush_file (void *opaque) +{ + FILE *fp = (FILE *) opaque; + + return fflush(fp); +} + +/* + * Use a rotating stock of buffers to make a printable string + */ +#define XO_NUMBUFS 8 +#define XO_SMBUFSZ 128 + +static const char * +xo_printable (const char *str) +{ + static THREAD_LOCAL(char) bufset[XO_NUMBUFS][XO_SMBUFSZ]; + static THREAD_LOCAL(int) bufnum = 0; + + if (str == NULL) + return ""; + + if (++bufnum == XO_NUMBUFS) + bufnum = 0; + + char *res = bufset[bufnum], *cp, *ep; + + for (cp = res, ep = res + XO_SMBUFSZ - 1; *str && cp < ep; cp++, str++) { + if (*str == '\n') { + *cp++ = '\\'; + *cp = 'n'; + } else if (*str == '\r') { + *cp++ = '\\'; + *cp = 'r'; + } else if (*str == '\"') { + *cp++ = '\\'; + *cp = '"'; + } else + *cp = *str; + } + + *cp = '\0'; + return res; +} + +static int +xo_depth_check (xo_handle_t *xop, int depth) +{ + xo_stack_t *xsp; + + if (depth >= xop->xo_stack_size) { + depth += XO_DEPTH; /* Extra room */ + + xsp = xo_realloc(xop->xo_stack, sizeof(xop->xo_stack[0]) * depth); + if (xsp == NULL) { + xo_failure(xop, "xo_depth_check: out of memory (%d)", depth); + return -1; + } + + int count = depth - xop->xo_stack_size; + + bzero(xsp + xop->xo_stack_size, count * sizeof(*xsp)); + xop->xo_stack_size = depth; + xop->xo_stack = xsp; + } + + return 0; +} + +void +xo_no_setlocale (void) +{ + xo_locale_inited = 1; /* Skip initialization */ +} + +/* + * We need to decide if stdout is line buffered (_IOLBF). Lacking a + * standard way to decide this (e.g. getlinebuf()), we have configure + * look to find __flbf, which glibc supported. If not, we'll rely on + * isatty, with the assumption that terminals are the only thing + * that's line buffered. We _could_ test for "steam._flags & _IOLBF", + * which is all __flbf does, but that's even tackier. Like a + * bedazzled Elvis outfit on an ugly lap dog sort of tacky. Not + * something we're willing to do. + */ +static int +xo_is_line_buffered (FILE *stream) +{ +#if HAVE___FLBF + if (__flbf(stream)) + return 1; +#else /* HAVE___FLBF */ + if (isatty(fileno(stream))) + return 1; +#endif /* HAVE___FLBF */ + return 0; +} + +/* + * Initialize an xo_handle_t, using both static defaults and + * the global settings from the LIBXO_OPTIONS environment + * variable. + */ +static void +xo_init_handle (xo_handle_t *xop) +{ + xop->xo_opaque = stdout; + xop->xo_write = xo_write_to_file; + xop->xo_flush = xo_flush_file; + + if (xo_is_line_buffered(stdout)) + XOF_SET(xop, XOF_FLUSH_LINE); + + /* + * We only want to do color output on terminals, but we only want + * to do this if the user has asked for color. + */ + if (XOF_ISSET(xop, XOF_COLOR_ALLOWED) && isatty(1)) + XOF_SET(xop, XOF_COLOR); + + /* + * We need to initialize the locale, which isn't really pretty. + * Libraries should depend on their caller to set up the + * environment. But we really can't count on the caller to do + * this, because well, they won't. Trust me. + */ + if (!xo_locale_inited) { + xo_locale_inited = 1; /* Only do this once */ + + const char *cp = getenv("LC_CTYPE"); + if (cp == NULL) + cp = getenv("LANG"); + if (cp == NULL) + cp = getenv("LC_ALL"); + if (cp == NULL) + cp = "C"; /* Default for C programs */ + (void) setlocale(LC_CTYPE, cp); + } + + /* + * Initialize only the xo_buffers we know we'll need; the others + * can be allocated as needed. + */ + xo_buf_init(&xop->xo_data); + xo_buf_init(&xop->xo_fmt); + + if (XOIF_ISSET(xop, XOIF_INIT_IN_PROGRESS)) + return; + XOIF_SET(xop, XOIF_INIT_IN_PROGRESS); + + xop->xo_indent_by = XO_INDENT_BY; + xo_depth_check(xop, XO_DEPTH); + +#if !defined(NO_LIBXO_OPTIONS) + if (!XOF_ISSET(xop, XOF_NO_ENV)) { + char *env = getenv("LIBXO_OPTIONS"); + if (env) + xo_set_options(xop, env); + + } +#endif /* NO_GETENV */ + + XOIF_CLEAR(xop, XOIF_INIT_IN_PROGRESS); +} + +/* + * Initialize the default handle. + */ +static void +xo_default_init (void) +{ + xo_handle_t *xop = &xo_default_handle; + + xo_init_handle(xop); + + xo_default_inited = 1; +} + +/* + * Cheap convenience function to return either the argument, or + * the internal handle, after it has been initialized. The usage + * is: + * xop = xo_default(xop); + */ +static xo_handle_t * +xo_default (xo_handle_t *xop) +{ + if (xop == NULL) { + if (xo_default_inited == 0) + xo_default_init(); + xop = &xo_default_handle; + } + + return xop; +} + +/* + * Return the number of spaces we should be indenting. If + * we are pretty-printing, this is indent * indent_by. + */ +static int +xo_indent (xo_handle_t *xop) +{ + int rc = 0; + + xop = xo_default(xop); + + if (XOF_ISSET(xop, XOF_PRETTY)) { + rc = xop->xo_indent * xop->xo_indent_by; + if (XOIF_ISSET(xop, XOIF_TOP_EMITTED)) + rc += xop->xo_indent_by; + } + + return (rc > 0) ? rc : 0; +} + +static void +xo_buf_indent (xo_handle_t *xop, int indent) +{ + xo_buffer_t *xbp = &xop->xo_data; + + if (indent <= 0) + indent = xo_indent(xop); + + if (!xo_buf_has_room(xbp, indent)) + return; + + memset(xbp->xb_curp, ' ', indent); + xbp->xb_curp += indent; +} + +static char xo_xml_amp[] = "&"; +static char xo_xml_lt[] = "<"; +static char xo_xml_gt[] = ">"; +static char xo_xml_quot[] = """; + +static int +xo_escape_xml (xo_buffer_t *xbp, int len, xo_xff_flags_t flags) +{ + int slen; + unsigned delta = 0; + char *cp, *ep, *ip; + const char *sp; + int attr = (flags & XFF_ATTR); + + for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { + /* We're subtracting 2: 1 for the NUL, 1 for the char we replace */ + if (*cp == '<') + delta += sizeof(xo_xml_lt) - 2; + else if (*cp == '>') + delta += sizeof(xo_xml_gt) - 2; + else if (*cp == '&') + delta += sizeof(xo_xml_amp) - 2; + else if (attr && *cp == '"') + delta += sizeof(xo_xml_quot) - 2; + } + + if (delta == 0) /* Nothing to escape; bail */ + return len; + + if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ + return 0; + + ep = xbp->xb_curp; + cp = ep + len; + ip = cp + delta; + do { + cp -= 1; + ip -= 1; + + if (*cp == '<') + sp = xo_xml_lt; + else if (*cp == '>') + sp = xo_xml_gt; + else if (*cp == '&') + sp = xo_xml_amp; + else if (attr && *cp == '"') + sp = xo_xml_quot; + else { + *ip = *cp; + continue; + } + + slen = strlen(sp); + ip -= slen - 1; + memcpy(ip, sp, slen); + + } while (cp > ep && cp != ip); + + return len + delta; +} + +static int +xo_escape_json (xo_buffer_t *xbp, int len, xo_xff_flags_t flags UNUSED) +{ + unsigned delta = 0; + char *cp, *ep, *ip; + + for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { + if (*cp == '\\' || *cp == '"') + delta += 1; + else if (*cp == '\n' || *cp == '\r') + delta += 1; + } + + if (delta == 0) /* Nothing to escape; bail */ + return len; + + if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ + return 0; + + ep = xbp->xb_curp; + cp = ep + len; + ip = cp + delta; + do { + cp -= 1; + ip -= 1; + + if (*cp == '\\' || *cp == '"') { + *ip-- = *cp; + *ip = '\\'; + } else if (*cp == '\n') { + *ip-- = 'n'; + *ip = '\\'; + } else if (*cp == '\r') { + *ip-- = 'r'; + *ip = '\\'; + } else { + *ip = *cp; + } + + } while (cp > ep && cp != ip); + + return len + delta; +} + +/* + * PARAM-VALUE = UTF-8-STRING ; characters '"', '\' and + * ; ']' MUST be escaped. + */ +static int +xo_escape_sdparams (xo_buffer_t *xbp, int len, xo_xff_flags_t flags UNUSED) +{ + unsigned delta = 0; + char *cp, *ep, *ip; + + for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { + if (*cp == '\\' || *cp == '"' || *cp == ']') + delta += 1; + } + + if (delta == 0) /* Nothing to escape; bail */ + return len; + + if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ + return 0; + + ep = xbp->xb_curp; + cp = ep + len; + ip = cp + delta; + do { + cp -= 1; + ip -= 1; + + if (*cp == '\\' || *cp == '"' || *cp == ']') { + *ip-- = *cp; + *ip = '\\'; + } else { + *ip = *cp; + } + + } while (cp > ep && cp != ip); + + return len + delta; +} + +static void +xo_buf_escape (xo_handle_t *xop, xo_buffer_t *xbp, + const char *str, int len, xo_xff_flags_t flags) +{ + if (!xo_buf_has_room(xbp, len)) + return; + + memcpy(xbp->xb_curp, str, len); + + switch (xo_style(xop)) { + case XO_STYLE_XML: + case XO_STYLE_HTML: + len = xo_escape_xml(xbp, len, flags); + break; + + case XO_STYLE_JSON: + len = xo_escape_json(xbp, len, flags); + break; + + case XO_STYLE_SDPARAMS: + len = xo_escape_sdparams(xbp, len, flags); + break; + } + + xbp->xb_curp += len; +} + +/* + * Write the current contents of the data buffer using the handle's + * xo_write function. + */ +static int +xo_write (xo_handle_t *xop) +{ + int rc = 0; + xo_buffer_t *xbp = &xop->xo_data; + + if (xbp->xb_curp != xbp->xb_bufp) { + xo_buf_append(xbp, "", 1); /* Append ending NUL */ + xo_anchor_clear(xop); + if (xop->xo_write) + rc = xop->xo_write(xop->xo_opaque, xbp->xb_bufp); + xbp->xb_curp = xbp->xb_bufp; + } + + /* Turn off the flags that don't survive across writes */ + XOIF_CLEAR(xop, XOIF_UNITS_PENDING); + + return rc; +} + +/* + * Format arguments into our buffer. If a custom formatter has been set, + * we use that to do the work; otherwise we vsnprintf(). + */ +static int +xo_vsnprintf (xo_handle_t *xop, xo_buffer_t *xbp, const char *fmt, va_list vap) +{ + va_list va_local; + int rc; + int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); + + va_copy(va_local, vap); + + if (xop->xo_formatter) + rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local); + else + rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); + + if (rc >= left) { + if (!xo_buf_has_room(xbp, rc)) { + va_end(va_local); + return -1; + } + + /* + * After we call vsnprintf(), the stage of vap is not defined. + * We need to copy it before we pass. Then we have to do our + * own logic below to move it along. This is because the + * implementation can have va_list be a pointer (bsd) or a + * structure (macosx) or anything in between. + */ + + va_end(va_local); /* Reset vap to the start */ + va_copy(va_local, vap); + + left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); + if (xop->xo_formatter) + rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local); + else + rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); + } + va_end(va_local); + + return rc; +} + +/* + * Print some data thru the handle. + */ +static int +xo_printf_v (xo_handle_t *xop, const char *fmt, va_list vap) +{ + xo_buffer_t *xbp = &xop->xo_data; + int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); + int rc; + va_list va_local; + + va_copy(va_local, vap); + + rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); + + if (rc >= left) { + if (!xo_buf_has_room(xbp, rc)) { + va_end(va_local); + return -1; + } + + va_end(va_local); /* Reset vap to the start */ + va_copy(va_local, vap); + + left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); + rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); + } + + va_end(va_local); + + if (rc > 0) + xbp->xb_curp += rc; + + return rc; +} + +static int +xo_printf (xo_handle_t *xop, const char *fmt, ...) +{ + int rc; + va_list vap; + + va_start(vap, fmt); + + rc = xo_printf_v(xop, fmt, vap); + + va_end(vap); + return rc; +} + +/* + * These next few function are make The Essential UTF-8 Ginsu Knife. + * Identify an input and output character, and convert it. + */ +static int xo_utf8_bits[7] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; + +static int +xo_is_utf8 (char ch) +{ + return (ch & 0x80); +} + +static int +xo_utf8_to_wc_len (const char *buf) +{ + unsigned b = (unsigned char) *buf; + int len; + + if ((b & 0x80) == 0x0) + len = 1; + else if ((b & 0xe0) == 0xc0) + len = 2; + else if ((b & 0xf0) == 0xe0) + len = 3; + else if ((b & 0xf8) == 0xf0) + len = 4; + else if ((b & 0xfc) == 0xf8) + len = 5; + else if ((b & 0xfe) == 0xfc) + len = 6; + else + len = -1; + + return len; +} + +static int +xo_buf_utf8_len (xo_handle_t *xop, const char *buf, int bufsiz) +{ + + unsigned b = (unsigned char) *buf; + int len, i; + + len = xo_utf8_to_wc_len(buf); + if (len == -1) { + xo_failure(xop, "invalid UTF-8 data: %02hhx", b); + return -1; + } + + if (len > bufsiz) { + xo_failure(xop, "invalid UTF-8 data (short): %02hhx (%d/%d)", + b, len, bufsiz); + return -1; + } + + for (i = 2; i < len; i++) { + b = (unsigned char ) buf[i]; + if ((b & 0xc0) != 0x80) { + xo_failure(xop, "invalid UTF-8 data (byte %d): %x", i, b); + return -1; + } + } + + return len; +} + +/* + * Build a wide character from the input buffer; the number of + * bits we pull off the first character is dependent on the length, + * but we put 6 bits off all other bytes. + */ +static wchar_t +xo_utf8_char (const char *buf, int len) +{ + int i; + wchar_t wc; + const unsigned char *cp = (const unsigned char *) buf; + + wc = *cp & xo_utf8_bits[len]; + for (i = 1; i < len; i++) { + wc <<= 6; + wc |= cp[i] & 0x3f; + if ((cp[i] & 0xc0) != 0x80) + return (wchar_t) -1; + } + + return wc; +} + +/* + * Determine the number of bytes needed to encode a wide character. + */ +static int +xo_utf8_emit_len (wchar_t wc) +{ + int len; + + if ((wc & ((1<<7) - 1)) == wc) /* Simple case */ + len = 1; + else if ((wc & ((1<<11) - 1)) == wc) + len = 2; + else if ((wc & ((1<<16) - 1)) == wc) + len = 3; + else if ((wc & ((1<<21) - 1)) == wc) + len = 4; + else if ((wc & ((1<<26) - 1)) == wc) + len = 5; + else + len = 6; + + return len; +} + +static void +xo_utf8_emit_char (char *buf, int len, wchar_t wc) +{ + int i; + + if (len == 1) { /* Simple case */ + buf[0] = wc & 0x7f; + return; + } + + for (i = len - 1; i >= 0; i--) { + buf[i] = 0x80 | (wc & 0x3f); + wc >>= 6; + } + + buf[0] &= xo_utf8_bits[len]; + buf[0] |= ~xo_utf8_bits[len] << 1; +} + +static int +xo_buf_append_locale_from_utf8 (xo_handle_t *xop, xo_buffer_t *xbp, + const char *ibuf, int ilen) +{ + wchar_t wc; + int len; + + /* + * Build our wide character from the input buffer; the number of + * bits we pull off the first character is dependent on the length, + * but we put 6 bits off all other bytes. + */ + wc = xo_utf8_char(ibuf, ilen); + if (wc == (wchar_t) -1) { + xo_failure(xop, "invalid utf-8 byte sequence"); + return 0; + } + + if (XOF_ISSET(xop, XOF_NO_LOCALE)) { + if (!xo_buf_has_room(xbp, ilen)) + return 0; + + memcpy(xbp->xb_curp, ibuf, ilen); + xbp->xb_curp += ilen; + + } else { + if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1)) + return 0; + + bzero(&xop->xo_mbstate, sizeof(xop->xo_mbstate)); + len = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate); + + if (len <= 0) { + xo_failure(xop, "could not convert wide char: %lx", + (unsigned long) wc); + return 0; + } + xbp->xb_curp += len; + } + + return xo_wcwidth(wc); +} + +static void +xo_buf_append_locale (xo_handle_t *xop, xo_buffer_t *xbp, + const char *cp, int len) +{ + const char *sp = cp, *ep = cp + len; + unsigned save_off = xbp->xb_bufp - xbp->xb_curp; + int slen; + int cols = 0; + + for ( ; cp < ep; cp++) { + if (!xo_is_utf8(*cp)) { + cols += 1; + continue; + } + + /* + * We're looking at a non-ascii UTF-8 character. + * First we copy the previous data. + * Then we need find the length and validate it. + * Then we turn it into a wide string. + * Then we turn it into a localized string. + * Then we repeat. Isn't i18n fun? + */ + if (sp != cp) + xo_buf_append(xbp, sp, cp - sp); /* Append previous data */ + + slen = xo_buf_utf8_len(xop, cp, ep - cp); + if (slen <= 0) { + /* Bad data; back it all out */ + xbp->xb_curp = xbp->xb_bufp + save_off; + return; + } + + cols += xo_buf_append_locale_from_utf8(xop, xbp, cp, slen); + + /* Next time thru, we'll start at the next character */ + cp += slen - 1; + sp = cp + 1; + } + + /* Update column values */ + if (XOF_ISSET(xop, XOF_COLUMNS)) + xop->xo_columns += cols; + if (XOIF_ISSET(xop, XOIF_ANCHOR)) + xop->xo_anchor_columns += cols; + + /* Before we fall into the basic logic below, we need reset len */ + len = ep - sp; + if (len != 0) /* Append trailing data */ + xo_buf_append(xbp, sp, len); +} + +/* + * Append the given string to the given buffer, without escaping or + * character set conversion. This is the straight copy to the data + * buffer with no fanciness. + */ +static void +xo_data_append (xo_handle_t *xop, const char *str, int len) +{ + xo_buf_append(&xop->xo_data, str, len); +} + +/* + * Append the given string to the given buffer + */ +static void +xo_data_escape (xo_handle_t *xop, const char *str, int len) +{ + xo_buf_escape(xop, &xop->xo_data, str, len, 0); +} + +/* + * Generate a warning. Normally, this is a text message written to + * standard error. If the XOF_WARN_XML flag is set, then we generate + * XMLified content on standard output. + */ +static void +xo_warn_hcv (xo_handle_t *xop, int code, int check_warn, + const char *fmt, va_list vap) +{ + xop = xo_default(xop); + if (check_warn && !XOF_ISSET(xop, XOF_WARN)) + return; + + if (fmt == NULL) + return; + + int len = strlen(fmt); + int plen = xo_program ? strlen(xo_program) : 0; + char *newfmt = alloca(len + 1 + plen + 2); /* NUL, and ": " */ + + if (plen) { + memcpy(newfmt, xo_program, plen); + newfmt[plen++] = ':'; + newfmt[plen++] = ' '; + } + memcpy(newfmt + plen, fmt, len); + newfmt[len + plen] = '\0'; + + if (XOF_ISSET(xop, XOF_WARN_XML)) { + static char err_open[] = ""; + static char err_close[] = ""; + static char msg_open[] = ""; + static char msg_close[] = ""; + + xo_buffer_t *xbp = &xop->xo_data; + + xo_buf_append(xbp, err_open, sizeof(err_open) - 1); + xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1); + + va_list va_local; + va_copy(va_local, vap); + + int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); + int rc = vsnprintf(xbp->xb_curp, left, newfmt, vap); + if (rc >= left) { + if (!xo_buf_has_room(xbp, rc)) { + va_end(va_local); + return; + } + + va_end(vap); /* Reset vap to the start */ + va_copy(vap, va_local); + + left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); + rc = vsnprintf(xbp->xb_curp, left, fmt, vap); + } + va_end(va_local); + + rc = xo_escape_xml(xbp, rc, 1); + xbp->xb_curp += rc; + + xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1); + xo_buf_append(xbp, err_close, sizeof(err_close) - 1); + + if (code >= 0) { + const char *msg = strerror(code); + if (msg) { + xo_buf_append(xbp, ": ", 2); + xo_buf_append(xbp, msg, strlen(msg)); + } + } + + xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ + (void) xo_write(xop); + + } else { + vfprintf(stderr, newfmt, vap); + if (code >= 0) { + const char *msg = strerror(code); + if (msg) + fprintf(stderr, ": %s", msg); + } + fprintf(stderr, "\n"); + } +} + +void +xo_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_warn_hcv(xop, code, 0, fmt, vap); + va_end(vap); +} + +void +xo_warn_c (int code, const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_warn_hcv(NULL, code, 0, fmt, vap); + va_end(vap); +} + +void +xo_warn (const char *fmt, ...) +{ + int code = errno; + va_list vap; + + va_start(vap, fmt); + xo_warn_hcv(NULL, code, 0, fmt, vap); + va_end(vap); +} + +void +xo_warnx (const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_warn_hcv(NULL, -1, 0, fmt, vap); + va_end(vap); +} + +void +xo_err (int eval, const char *fmt, ...) +{ + int code = errno; + va_list vap; + + va_start(vap, fmt); + xo_warn_hcv(NULL, code, 0, fmt, vap); + va_end(vap); + xo_finish(); + exit(eval); +} + +void +xo_errx (int eval, const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_warn_hcv(NULL, -1, 0, fmt, vap); + va_end(vap); + xo_finish(); + exit(eval); +} + +void +xo_errc (int eval, int code, const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_warn_hcv(NULL, code, 0, fmt, vap); + va_end(vap); + xo_finish(); + exit(eval); +} + +/* + * Generate a warning. Normally, this is a text message written to + * standard error. If the XOF_WARN_XML flag is set, then we generate + * XMLified content on standard output. + */ +void +xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap) +{ + static char msg_open[] = ""; + static char msg_close[] = ""; + xo_buffer_t *xbp; + int rc; + va_list va_local; + + xop = xo_default(xop); + + if (fmt == NULL || *fmt == '\0') + return; + + int need_nl = (fmt[strlen(fmt) - 1] != '\n'); + + switch (xo_style(xop)) { + case XO_STYLE_XML: + xbp = &xop->xo_data; + if (XOF_ISSET(xop, XOF_PRETTY)) + xo_buf_indent(xop, xop->xo_indent_by); + xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1); + + va_copy(va_local, vap); + + int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); + rc = vsnprintf(xbp->xb_curp, left, fmt, vap); + if (rc >= left) { + if (!xo_buf_has_room(xbp, rc)) { + va_end(va_local); + return; + } + + va_end(vap); /* Reset vap to the start */ + va_copy(vap, va_local); + + left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); + rc = vsnprintf(xbp->xb_curp, left, fmt, vap); + } + va_end(va_local); + + rc = xo_escape_xml(xbp, rc, 0); + xbp->xb_curp += rc; + + if (need_nl && code > 0) { + const char *msg = strerror(code); + if (msg) { + xo_buf_append(xbp, ": ", 2); + xo_buf_append(xbp, msg, strlen(msg)); + } + } + + if (need_nl) + xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ + + xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1); + + if (XOF_ISSET(xop, XOF_PRETTY)) + xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ + + (void) xo_write(xop); + break; + + case XO_STYLE_HTML: + { + char buf[BUFSIZ], *bp = buf, *cp; + int bufsiz = sizeof(buf); + int rc2; + + va_copy(va_local, vap); + + rc = vsnprintf(bp, bufsiz, fmt, va_local); + if (rc > bufsiz) { + bufsiz = rc + BUFSIZ; + bp = alloca(bufsiz); + va_end(va_local); + va_copy(va_local, vap); + rc = vsnprintf(bp, bufsiz, fmt, va_local); + } + va_end(va_local); + cp = bp + rc; + + if (need_nl) { + rc2 = snprintf(cp, bufsiz - rc, "%s%s\n", + (code > 0) ? ": " : "", + (code > 0) ? strerror(code) : ""); + if (rc2 > 0) + rc += rc2; + } + + xo_buf_append_div(xop, "message", 0, NULL, 0, bp, rc, NULL, 0); + } + break; + + case XO_STYLE_JSON: + case XO_STYLE_SDPARAMS: + case XO_STYLE_ENCODER: + /* No means of representing messages */ + return; + + case XO_STYLE_TEXT: + rc = xo_printf_v(xop, fmt, vap); + /* + * XXX need to handle UTF-8 widths + */ + if (rc > 0) { + if (XOF_ISSET(xop, XOF_COLUMNS)) + xop->xo_columns += rc; + if (XOIF_ISSET(xop, XOIF_ANCHOR)) + xop->xo_anchor_columns += rc; + } + + if (need_nl && code > 0) { + const char *msg = strerror(code); + if (msg) { + xo_printf(xop, ": %s", msg); + } + } + if (need_nl) + xo_printf(xop, "\n"); + + break; + } + + (void) xo_flush_h(xop); +} + +void +xo_message_hc (xo_handle_t *xop, int code, const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_message_hcv(xop, code, fmt, vap); + va_end(vap); +} + +void +xo_message_c (int code, const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_message_hcv(NULL, code, fmt, vap); + va_end(vap); +} + +void +xo_message_e (const char *fmt, ...) +{ + int code = errno; + va_list vap; + + va_start(vap, fmt); + xo_message_hcv(NULL, code, fmt, vap); + va_end(vap); +} + +void +xo_message (const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_message_hcv(NULL, 0, fmt, vap); + va_end(vap); +} + +static void +xo_failure (xo_handle_t *xop, const char *fmt, ...) +{ + if (!XOF_ISSET(xop, XOF_WARN)) + return; + + va_list vap; + + va_start(vap, fmt); + xo_warn_hcv(xop, -1, 1, fmt, vap); + va_end(vap); +} + +/** + * Create a handle for use by later libxo functions. + * + * Note: normal use of libxo does not require a distinct handle, since + * the default handle (used when NULL is passed) generates text on stdout. + * + * @style Style of output desired (XO_STYLE_* value) + * @flags Set of XOF_* flags in use with this handle + */ +xo_handle_t * +xo_create (xo_style_t style, xo_xof_flags_t flags) +{ + xo_handle_t *xop = xo_realloc(NULL, sizeof(*xop)); + + if (xop) { + bzero(xop, sizeof(*xop)); + + xop->xo_style = style; + XOF_SET(xop, flags); + xo_init_handle(xop); + xop->xo_style = style; /* Reset style (see LIBXO_OPTIONS) */ + } + + return xop; +} + +/** + * Create a handle that will write to the given file. Use + * the XOF_CLOSE_FP flag to have the file closed on xo_destroy(). + * @fp FILE pointer to use + * @style Style of output desired (XO_STYLE_* value) + * @flags Set of XOF_* flags to use with this handle + */ +xo_handle_t * +xo_create_to_file (FILE *fp, xo_style_t style, xo_xof_flags_t flags) +{ + xo_handle_t *xop = xo_create(style, flags); + + if (xop) { + xop->xo_opaque = fp; + xop->xo_write = xo_write_to_file; + xop->xo_close = xo_close_file; + xop->xo_flush = xo_flush_file; + } + + return xop; +} + +/** + * Release any resources held by the handle. + * @xop XO handle to alter (or NULL for default handle) + */ +void +xo_destroy (xo_handle_t *xop_arg) +{ + xo_handle_t *xop = xo_default(xop_arg); + + xo_flush_h(xop); + + if (xop->xo_close && XOF_ISSET(xop, XOF_CLOSE_FP)) + xop->xo_close(xop->xo_opaque); + + xo_free(xop->xo_stack); + xo_buf_cleanup(&xop->xo_data); + xo_buf_cleanup(&xop->xo_fmt); + xo_buf_cleanup(&xop->xo_predicate); + xo_buf_cleanup(&xop->xo_attrs); + xo_buf_cleanup(&xop->xo_color_buf); + + if (xop->xo_version) + xo_free(xop->xo_version); + + if (xop_arg == NULL) { + bzero(&xo_default_handle, sizeof(xo_default_handle)); + xo_default_inited = 0; + } else + xo_free(xop); +} + +/** + * Record a new output style to use for the given handle (or default if + * handle is NULL). This output style will be used for any future output. + * + * @xop XO handle to alter (or NULL for default handle) + * @style new output style (XO_STYLE_*) + */ +void +xo_set_style (xo_handle_t *xop, xo_style_t style) +{ + xop = xo_default(xop); + xop->xo_style = style; +} + +xo_style_t +xo_get_style (xo_handle_t *xop) +{ + xop = xo_default(xop); + return xo_style(xop); +} + +static int +xo_name_to_style (const char *name) +{ + if (strcmp(name, "xml") == 0) + return XO_STYLE_XML; + else if (strcmp(name, "json") == 0) + return XO_STYLE_JSON; + else if (strcmp(name, "encoder") == 0) + return XO_STYLE_ENCODER; + else if (strcmp(name, "text") == 0) + return XO_STYLE_TEXT; + else if (strcmp(name, "html") == 0) + return XO_STYLE_HTML; + else if (strcmp(name, "sdparams") == 0) + return XO_STYLE_SDPARAMS; + + return -1; +} + +/* + * Indicate if the style is an "encoding" one as opposed to a "display" one. + */ +static int +xo_style_is_encoding (xo_handle_t *xop) +{ + if (xo_style(xop) == XO_STYLE_JSON + || xo_style(xop) == XO_STYLE_XML + || xo_style(xop) == XO_STYLE_SDPARAMS + || xo_style(xop) == XO_STYLE_ENCODER) + return 1; + return 0; +} + +/* Simple name-value mapping */ +typedef struct xo_mapping_s { + xo_xff_flags_t xm_value; + const char *xm_name; +} xo_mapping_t; + +static xo_xff_flags_t +xo_name_lookup (xo_mapping_t *map, const char *value, int len) +{ + if (len == 0) + return 0; + + if (len < 0) + len = strlen(value); + + while (isspace((int) *value)) { + value += 1; + len -= 1; + } + + while (isspace((int) value[len])) + len -= 1; + + if (*value == '\0') + return 0; + + for ( ; map->xm_name; map++) + if (strncmp(map->xm_name, value, len) == 0) + return map->xm_value; + + return 0; +} + +#ifdef NOT_NEEDED_YET +static const char * +xo_value_lookup (xo_mapping_t *map, xo_xff_flags_t value) +{ + if (value == 0) + return NULL; + + for ( ; map->xm_name; map++) + if (map->xm_value == value) + return map->xm_name; + + return NULL; +} +#endif /* NOT_NEEDED_YET */ + +static xo_mapping_t xo_xof_names[] = { + { XOF_COLOR_ALLOWED, "color" }, + { XOF_COLUMNS, "columns" }, + { XOF_DTRT, "dtrt" }, + { XOF_FLUSH, "flush" }, + { XOF_IGNORE_CLOSE, "ignore-close" }, + { XOF_INFO, "info" }, + { XOF_KEYS, "keys" }, + { XOF_LOG_GETTEXT, "log-gettext" }, + { XOF_LOG_SYSLOG, "log-syslog" }, + { XOF_NO_HUMANIZE, "no-humanize" }, + { XOF_NO_LOCALE, "no-locale" }, + { XOF_NO_TOP, "no-top" }, + { XOF_NOT_FIRST, "not-first" }, + { XOF_PRETTY, "pretty" }, + { XOF_UNDERSCORES, "underscores" }, + { XOF_UNITS, "units" }, + { XOF_WARN, "warn" }, + { XOF_WARN_XML, "warn-xml" }, + { XOF_XPATH, "xpath" }, + { 0, NULL } +}; + +/* + * Convert string name to XOF_* flag value. + * Not all are useful. Or safe. Or sane. + */ +static unsigned +xo_name_to_flag (const char *name) +{ + return (unsigned) xo_name_lookup(xo_xof_names, name, -1); +} + +int +xo_set_style_name (xo_handle_t *xop, const char *name) +{ + if (name == NULL) + return -1; + + int style = xo_name_to_style(name); + if (style < 0) + return -1; + + xo_set_style(xop, style); + return 0; +} + +/* + * Set the options for a handle using a string of options + * passed in. The input is a comma-separated set of names + * and optional values: "xml,pretty,indent=4" + */ +int +xo_set_options (xo_handle_t *xop, const char *input) +{ + char *cp, *ep, *vp, *np, *bp; + int style = -1, new_style, len, rc = 0; + xo_xof_flags_t new_flag; + + if (input == NULL) + return 0; + + xop = xo_default(xop); + +#ifdef LIBXO_COLOR_ON_BY_DEFAULT + /* If the installer used --enable-color-on-by-default, then we allow it */ + XOF_SET(xop, XOF_COLOR_ALLOWED); +#endif /* LIBXO_COLOR_ON_BY_DEFAULT */ + + /* + * We support a simpler, old-school style of giving option + * also, using a single character for each option. It's + * ideal for lazy people, such as myself. + */ + if (*input == ':') { + int sz; + + for (input++ ; *input; input++) { + switch (*input) { + case 'c': + XOF_SET(xop, XOF_COLOR_ALLOWED); + break; + + case 'f': + XOF_SET(xop, XOF_FLUSH); + break; + + case 'F': + XOF_SET(xop, XOF_FLUSH_LINE); + break; + + case 'g': + XOF_SET(xop, XOF_LOG_GETTEXT); + break; + + case 'H': + xop->xo_style = XO_STYLE_HTML; + break; + + case 'I': + XOF_SET(xop, XOF_INFO); + break; + + case 'i': + sz = strspn(input + 1, "0123456789"); + if (sz > 0) { + xop->xo_indent_by = atoi(input + 1); + input += sz - 1; /* Skip value */ + } + break; + + case 'J': + xop->xo_style = XO_STYLE_JSON; + break; + + case 'k': + XOF_SET(xop, XOF_KEYS); + break; + + case 'n': + XOF_SET(xop, XOF_NO_HUMANIZE); + break; + + case 'P': + XOF_SET(xop, XOF_PRETTY); + break; + + case 'T': + xop->xo_style = XO_STYLE_TEXT; + break; + + case 'U': + XOF_SET(xop, XOF_UNITS); + break; + + case 'u': + XOF_SET(xop, XOF_UNDERSCORES); + break; + + case 'W': + XOF_SET(xop, XOF_WARN); + break; + + case 'X': + xop->xo_style = XO_STYLE_XML; + break; + + case 'x': + XOF_SET(xop, XOF_XPATH); + break; + } + } + return 0; + } + + len = strlen(input) + 1; + bp = alloca(len); + memcpy(bp, input, len); + + for (cp = bp, ep = cp + len - 1; cp && cp < ep; cp = np) { + np = strchr(cp, ','); + if (np) + *np++ = '\0'; + + vp = strchr(cp, '='); + if (vp) + *vp++ = '\0'; + + if (strcmp("colors", cp) == 0) { + /* XXX Look for colors=red-blue+green-yellow */ + continue; + } + + /* + * For options, we don't allow "encoder" since we want to + * handle it explicitly below as "encoder=xxx". + */ + new_style = xo_name_to_style(cp); + if (new_style >= 0 && new_style != XO_STYLE_ENCODER) { + if (style >= 0) + xo_warnx("ignoring multiple styles: '%s'", cp); + else + style = new_style; + } else { + new_flag = xo_name_to_flag(cp); + if (new_flag != 0) + XOF_SET(xop, new_flag); + else { + if (strcmp(cp, "no-color") == 0) { + XOF_CLEAR(xop, XOF_COLOR_ALLOWED); + } else if (strcmp(cp, "indent") == 0) { + if (vp) + xop->xo_indent_by = atoi(vp); + else + xo_failure(xop, "missing value for indent option"); + } else if (strcmp(cp, "encoder") == 0) { + if (vp == NULL) + xo_failure(xop, "missing value for encoder option"); + else { + if (xo_encoder_init(xop, vp)) { + xo_failure(xop, "encoder not found: %s", vp); + rc = -1; + } + } + + } else { + xo_warnx("unknown libxo option value: '%s'", cp); + rc = -1; + } + } + } + } + + if (style > 0) + xop->xo_style= style; + + return rc; +} + +/** + * Set one or more flags for a given handle (or default if handle is NULL). + * These flags will affect future output. + * + * @xop XO handle to alter (or NULL for default handle) + * @flags Flags to be set (XOF_*) + */ +void +xo_set_flags (xo_handle_t *xop, xo_xof_flags_t flags) +{ + xop = xo_default(xop); + + XOF_SET(xop, flags); +} + +xo_xof_flags_t +xo_get_flags (xo_handle_t *xop) +{ + xop = xo_default(xop); + + return xop->xo_flags; +} + +/* + * strndup with a twist: len < 0 means strlen + */ +static char * +xo_strndup (const char *str, int len) +{ + if (len < 0) + len = strlen(str); + + char *cp = xo_realloc(NULL, len + 1); + if (cp) { + memcpy(cp, str, len); + cp[len] = '\0'; + } + + return cp; +} + +/** + * Record a leading prefix for the XPath we generate. This allows the + * generated data to be placed within an XML hierarchy but still have + * accurate XPath expressions. + * + * @xop XO handle to alter (or NULL for default handle) + * @path The XPath expression + */ +void +xo_set_leading_xpath (xo_handle_t *xop, const char *path) +{ + xop = xo_default(xop); + + if (xop->xo_leading_xpath) { + xo_free(xop->xo_leading_xpath); + xop->xo_leading_xpath = NULL; + } + + if (path == NULL) + return; + + xop->xo_leading_xpath = xo_strndup(path, -1); +} + +/** + * Record the info data for a set of tags + * + * @xop XO handle to alter (or NULL for default handle) + * @info Info data (xo_info_t) to be recorded (or NULL) (MUST BE SORTED) + * @count Number of entries in info (or -1 to count them ourselves) + */ +void +xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count) +{ + xop = xo_default(xop); + + if (count < 0 && infop) { + xo_info_t *xip; + + for (xip = infop, count = 0; xip->xi_name; xip++, count++) + continue; + } + + xop->xo_info = infop; + xop->xo_info_count = count; +} + +/** + * Set the formatter callback for a handle. The callback should + * return a newly formatting contents of a formatting instruction, + * meaning the bits inside the braces. + */ +void +xo_set_formatter (xo_handle_t *xop, xo_formatter_t func, + xo_checkpointer_t cfunc) +{ + xop = xo_default(xop); + + xop->xo_formatter = func; + xop->xo_checkpointer = cfunc; +} + +/** + * Clear one or more flags for a given handle (or default if handle is NULL). + * These flags will affect future output. + * + * @xop XO handle to alter (or NULL for default handle) + * @flags Flags to be cleared (XOF_*) + */ +void +xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags) +{ + xop = xo_default(xop); + + XOF_CLEAR(xop, flags); +} + +static const char * +xo_state_name (xo_state_t state) +{ + static const char *names[] = { + "init", + "open_container", + "close_container", + "open_list", + "close_list", + "open_instance", + "close_instance", + "open_leaf_list", + "close_leaf_list", + "discarding", + "marker", + "emit", + "emit_leaf_list", + "finish", + NULL + }; + + if (state < (sizeof(names) / sizeof(names[0]))) + return names[state]; + + return "unknown"; +} + +static void +xo_line_ensure_open (xo_handle_t *xop, xo_xff_flags_t flags UNUSED) +{ + static char div_open[] = "
"; + static char div_open_blank[] = "
"; + + if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) + return; + + if (xo_style(xop) != XO_STYLE_HTML) + return; + + XOIF_SET(xop, XOIF_DIV_OPEN); + if (flags & XFF_BLANK_LINE) + xo_data_append(xop, div_open_blank, sizeof(div_open_blank) - 1); + else + xo_data_append(xop, div_open, sizeof(div_open) - 1); + + if (XOF_ISSET(xop, XOF_PRETTY)) + xo_data_append(xop, "\n", 1); +} + +static void +xo_line_close (xo_handle_t *xop) +{ + static char div_close[] = "
"; + + switch (xo_style(xop)) { + case XO_STYLE_HTML: + if (!XOIF_ISSET(xop, XOIF_DIV_OPEN)) + xo_line_ensure_open(xop, 0); + + XOIF_CLEAR(xop, XOIF_DIV_OPEN); + xo_data_append(xop, div_close, sizeof(div_close) - 1); + + if (XOF_ISSET(xop, XOF_PRETTY)) + xo_data_append(xop, "\n", 1); + break; + + case XO_STYLE_TEXT: + xo_data_append(xop, "\n", 1); + break; + } +} + +static int +xo_info_compare (const void *key, const void *data) +{ + const char *name = key; + const xo_info_t *xip = data; + + return strcmp(name, xip->xi_name); +} + + +static xo_info_t * +xo_info_find (xo_handle_t *xop, const char *name, int nlen) +{ + xo_info_t *xip; + char *cp = alloca(nlen + 1); /* Need local copy for NUL termination */ + + memcpy(cp, name, nlen); + cp[nlen] = '\0'; + + xip = bsearch(cp, xop->xo_info, xop->xo_info_count, + sizeof(xop->xo_info[0]), xo_info_compare); + return xip; +} + +#define CONVERT(_have, _need) (((_have) << 8) | (_need)) + +/* + * Check to see that the conversion is safe and sane. + */ +static int +xo_check_conversion (xo_handle_t *xop, int have_enc, int need_enc) +{ + switch (CONVERT(have_enc, need_enc)) { + case CONVERT(XF_ENC_UTF8, XF_ENC_UTF8): + case CONVERT(XF_ENC_UTF8, XF_ENC_LOCALE): + case CONVERT(XF_ENC_WIDE, XF_ENC_UTF8): + case CONVERT(XF_ENC_WIDE, XF_ENC_LOCALE): + case CONVERT(XF_ENC_LOCALE, XF_ENC_LOCALE): + case CONVERT(XF_ENC_LOCALE, XF_ENC_UTF8): + return 0; + + default: + xo_failure(xop, "invalid conversion (%c:%c)", have_enc, need_enc); + return 1; + } +} + +static int +xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp, + xo_xff_flags_t flags, + const wchar_t *wcp, const char *cp, int len, int max, + int need_enc, int have_enc) +{ + int cols = 0; + wchar_t wc = 0; + int ilen, olen, width; + int attr = (flags & XFF_ATTR); + const char *sp; + + if (len > 0 && !xo_buf_has_room(xbp, len)) + return 0; + + for (;;) { + if (len == 0) + break; + + if (cp) { + if (*cp == '\0') + break; + if ((flags & XFF_UNESCAPE) && (*cp == '\\' || *cp == '%')) { + cp += 1; + len -= 1; + } + } + + if (wcp && *wcp == L'\0') + break; + + ilen = 0; + + switch (have_enc) { + case XF_ENC_WIDE: /* Wide character */ + wc = *wcp++; + ilen = 1; + break; + + case XF_ENC_UTF8: /* UTF-8 */ + ilen = xo_utf8_to_wc_len(cp); + if (ilen < 0) { + xo_failure(xop, "invalid UTF-8 character: %02hhx", *cp); + return -1; /* Can't continue; we can't find the end */ + } + + if (len > 0 && len < ilen) { + len = 0; /* Break out of the loop */ + continue; + } + + wc = xo_utf8_char(cp, ilen); + if (wc == (wchar_t) -1) { + xo_failure(xop, "invalid UTF-8 character: %02hhx/%d", + *cp, ilen); + return -1; /* Can't continue; we can't find the end */ + } + cp += ilen; + break; + + case XF_ENC_LOCALE: /* Native locale */ + ilen = (len > 0) ? len : MB_LEN_MAX; + ilen = mbrtowc(&wc, cp, ilen, &xop->xo_mbstate); + if (ilen < 0) { /* Invalid data; skip */ + xo_failure(xop, "invalid mbs char: %02hhx", *cp); + wc = L'?'; + ilen = 1; + } + + if (ilen == 0) { /* Hit a wide NUL character */ + len = 0; + continue; + } + + cp += ilen; + break; + } + + /* Reduce len, but not below zero */ + if (len > 0) { + len -= ilen; + if (len < 0) + len = 0; + } + + /* + * Find the width-in-columns of this character, which must be done + * in wide characters, since we lack a mbswidth() function. If + * it doesn't fit + */ + width = xo_wcwidth(wc); + if (width < 0) + width = iswcntrl(wc) ? 0 : 1; + + if (xo_style(xop) == XO_STYLE_TEXT || xo_style(xop) == XO_STYLE_HTML) { + if (max > 0 && cols + width > max) + break; + } + + switch (need_enc) { + case XF_ENC_UTF8: + + /* Output in UTF-8 needs to be escaped, based on the style */ + switch (xo_style(xop)) { + case XO_STYLE_XML: + case XO_STYLE_HTML: + if (wc == '<') + sp = xo_xml_lt; + else if (wc == '>') + sp = xo_xml_gt; + else if (wc == '&') + sp = xo_xml_amp; + else if (attr && wc == '"') + sp = xo_xml_quot; + else + break; + + int slen = strlen(sp); + if (!xo_buf_has_room(xbp, slen - 1)) + return -1; + + memcpy(xbp->xb_curp, sp, slen); + xbp->xb_curp += slen; + goto done_with_encoding; /* Need multi-level 'break' */ + + case XO_STYLE_JSON: + if (wc != '\\' && wc != '"' && wc != '\n' && wc != '\r') + break; + + if (!xo_buf_has_room(xbp, 2)) + return -1; + + *xbp->xb_curp++ = '\\'; + if (wc == '\n') + wc = 'n'; + else if (wc == '\r') + wc = 'r'; + else wc = wc & 0x7f; + + *xbp->xb_curp++ = wc; + goto done_with_encoding; + + case XO_STYLE_SDPARAMS: + if (wc != '\\' && wc != '"' && wc != ']') + break; + + if (!xo_buf_has_room(xbp, 2)) + return -1; + + *xbp->xb_curp++ = '\\'; + wc = wc & 0x7f; + *xbp->xb_curp++ = wc; + goto done_with_encoding; + } + + olen = xo_utf8_emit_len(wc); + if (olen < 0) { + xo_failure(xop, "ignoring bad length"); + continue; + } + + if (!xo_buf_has_room(xbp, olen)) + return -1; + + xo_utf8_emit_char(xbp->xb_curp, olen, wc); + xbp->xb_curp += olen; + break; + + case XF_ENC_LOCALE: + if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1)) + return -1; + + olen = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate); + if (olen <= 0) { + xo_failure(xop, "could not convert wide char: %lx", + (unsigned long) wc); + width = 1; + *xbp->xb_curp++ = '?'; + } else + xbp->xb_curp += olen; + break; + } + + done_with_encoding: + cols += width; + } + + return cols; +} + +static int +xo_needed_encoding (xo_handle_t *xop) +{ + if (XOF_ISSET(xop, XOF_UTF8)) /* Check the override flag */ + return XF_ENC_UTF8; + + if (xo_style(xop) == XO_STYLE_TEXT) /* Text means locale */ + return XF_ENC_LOCALE; + + return XF_ENC_UTF8; /* Otherwise, we love UTF-8 */ +} + +static int +xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags, + xo_format_t *xfp) +{ + static char null[] = "(null)"; + static char null_no_quotes[] = "null"; + + char *cp = NULL; + wchar_t *wcp = NULL; + int len, cols = 0, rc = 0; + int off = xbp->xb_curp - xbp->xb_bufp, off2; + int need_enc = xo_needed_encoding(xop); + + if (xo_check_conversion(xop, xfp->xf_enc, need_enc)) + return 0; + + len = xfp->xf_width[XF_WIDTH_SIZE]; + + if (xfp->xf_fc == 'm') { + cp = strerror(xop->xo_errno); + if (len < 0) + len = cp ? strlen(cp) : 0; + goto normal_string; + + } else if (xfp->xf_enc == XF_ENC_WIDE) { + wcp = va_arg(xop->xo_vap, wchar_t *); + if (xfp->xf_skip) + return 0; + + /* + * Dont' deref NULL; use the traditional "(null)" instead + * of the more accurate "who's been a naughty boy, then?". + */ + if (wcp == NULL) { + cp = null; + len = sizeof(null) - 1; + } + + } else { + cp = va_arg(xop->xo_vap, char *); /* UTF-8 or native */ + + normal_string: + if (xfp->xf_skip) + return 0; + + /* Echo "Dont' deref NULL" logic */ + if (cp == NULL) { + if ((flags & XFF_NOQUOTE) && xo_style_is_encoding(xop)) { + cp = null_no_quotes; + len = sizeof(null_no_quotes) - 1; + } else { + cp = null; + len = sizeof(null) - 1; + } + } + + /* + * Optimize the most common case, which is "%s". We just + * need to copy the complete string to the output buffer. + */ + if (xfp->xf_enc == need_enc + && xfp->xf_width[XF_WIDTH_MIN] < 0 + && xfp->xf_width[XF_WIDTH_SIZE] < 0 + && xfp->xf_width[XF_WIDTH_MAX] < 0 + && !(XOIF_ISSET(xop, XOIF_ANCHOR) + || XOF_ISSET(xop, XOF_COLUMNS))) { + len = strlen(cp); + xo_buf_escape(xop, xbp, cp, len, flags); + + /* + * Our caller expects xb_curp left untouched, so we have + * to reset it and return the number of bytes written to + * the buffer. + */ + off2 = xbp->xb_curp - xbp->xb_bufp; + rc = off2 - off; + xbp->xb_curp = xbp->xb_bufp + off; + + return rc; + } + } + + cols = xo_format_string_direct(xop, xbp, flags, wcp, cp, len, + xfp->xf_width[XF_WIDTH_MAX], + need_enc, xfp->xf_enc); + if (cols < 0) + goto bail; + + /* + * xo_buf_append* will move xb_curp, so we save/restore it. + */ + off2 = xbp->xb_curp - xbp->xb_bufp; + rc = off2 - off; + xbp->xb_curp = xbp->xb_bufp + off; + + if (cols < xfp->xf_width[XF_WIDTH_MIN]) { + /* + * Find the number of columns needed to display the string. + * If we have the original wide string, we just call wcswidth, + * but if we did the work ourselves, then we need to do it. + */ + int delta = xfp->xf_width[XF_WIDTH_MIN] - cols; + if (!xo_buf_has_room(xbp, delta)) + goto bail; + + /* + * If seen_minus, then pad on the right; otherwise move it so + * we can pad on the left. + */ + if (xfp->xf_seen_minus) { + cp = xbp->xb_curp + rc; + } else { + cp = xbp->xb_curp; + memmove(xbp->xb_curp + delta, xbp->xb_curp, rc); + } + + /* Set the padding */ + memset(cp, (xfp->xf_leading_zero > 0) ? '0' : ' ', delta); + rc += delta; + cols += delta; + } + + if (XOF_ISSET(xop, XOF_COLUMNS)) + xop->xo_columns += cols; + if (XOIF_ISSET(xop, XOIF_ANCHOR)) + xop->xo_anchor_columns += cols; + + return rc; + + bail: + xbp->xb_curp = xbp->xb_bufp + off; + return 0; +} + +/* + * Look backwards in a buffer to find a numeric value + */ +static int +xo_buf_find_last_number (xo_buffer_t *xbp, int start_offset) +{ + int rc = 0; /* Fail with zero */ + int digit = 1; + char *sp = xbp->xb_bufp; + char *cp = sp + start_offset; + + while (--cp >= sp) + if (isdigit((int) *cp)) + break; + + for ( ; cp >= sp; cp--) { + if (!isdigit((int) *cp)) + break; + rc += (*cp - '0') * digit; + digit *= 10; + } + + return rc; +} + +static int +xo_count_utf8_cols (const char *str, int len) +{ + int tlen; + wchar_t wc; + int cols = 0; + const char *ep = str + len; + + while (str < ep) { + tlen = xo_utf8_to_wc_len(str); + if (tlen < 0) /* Broken input is very bad */ + return cols; + + wc = xo_utf8_char(str, tlen); + if (wc == (wchar_t) -1) + return cols; + + /* We only print printable characters */ + if (iswprint((wint_t) wc)) { + /* + * Find the width-in-columns of this character, which must be done + * in wide characters, since we lack a mbswidth() function. + */ + int width = xo_wcwidth(wc); + if (width < 0) + width = iswcntrl(wc) ? 0 : 1; + + cols += width; + } + + str += tlen; + } + + return cols; +} + +#ifdef HAVE_GETTEXT +static inline const char * +xo_dgettext (xo_handle_t *xop, const char *str) +{ + const char *domainname = xop->xo_gt_domain; + const char *res; + + res = dgettext(domainname, str); + + if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) + fprintf(stderr, "xo: gettext: %s%s%smsgid \"%s\" returns \"%s\"\n", + domainname ? "domain \"" : "", xo_printable(domainname), + domainname ? "\", " : "", xo_printable(str), xo_printable(res)); + + return res; +} + +static inline const char * +xo_dngettext (xo_handle_t *xop, const char *sing, const char *plural, + unsigned long int n) +{ + const char *domainname = xop->xo_gt_domain; + const char *res; + + res = dngettext(domainname, sing, plural, n); + if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) + fprintf(stderr, "xo: gettext: %s%s%s" + "msgid \"%s\", msgid_plural \"%s\" (%lu) returns \"%s\"\n", + domainname ? "domain \"" : "", + xo_printable(domainname), domainname ? "\", " : "", + xo_printable(sing), + xo_printable(plural), n, xo_printable(res)); + + return res; +} +#else /* HAVE_GETTEXT */ +static inline const char * +xo_dgettext (xo_handle_t *xop UNUSED, const char *str) +{ + return str; +} + +static inline const char * +xo_dngettext (xo_handle_t *xop UNUSED, const char *singular, + const char *plural, unsigned long int n) +{ + return (n == 1) ? singular : plural; +} +#endif /* HAVE_GETTEXT */ + +/* + * This is really _re_formatting, since the normal format code has + * generated a beautiful string into xo_data, starting at + * start_offset. We need to see if it's plural, which means + * comma-separated options, or singular. Then we make the appropriate + * call to d[n]gettext() to get the locale-based version. Note that + * both input and output of gettext() this should be UTF-8. + */ +static int +xo_format_gettext (xo_handle_t *xop, xo_xff_flags_t flags, + int start_offset, int cols, int need_enc) +{ + xo_buffer_t *xbp = &xop->xo_data; + + if (!xo_buf_has_room(xbp, 1)) + return cols; + + xbp->xb_curp[0] = '\0'; /* NUL-terminate the input string */ + + char *cp = xbp->xb_bufp + start_offset; + int len = xbp->xb_curp - cp; + const char *newstr = NULL; + + /* + * The plural flag asks us to look backwards at the last numeric + * value rendered and disect the string into two pieces. + */ + if (flags & XFF_GT_PLURAL) { + int n = xo_buf_find_last_number(xbp, start_offset); + char *two = memchr(cp, (int) ',', len); + if (two == NULL) { + xo_failure(xop, "no comma in plural gettext field: '%s'", cp); + return cols; + } + + if (two == cp) { + xo_failure(xop, "nothing before comma in plural gettext " + "field: '%s'", cp); + return cols; + } + + if (two == xbp->xb_curp) { + xo_failure(xop, "nothing after comma in plural gettext " + "field: '%s'", cp); + return cols; + } + + *two++ = '\0'; + if (flags & XFF_GT_FIELD) { + newstr = xo_dngettext(xop, cp, two, n); + } else { + /* Don't do a gettext() look up, just get the plural form */ + newstr = (n == 1) ? cp : two; + } + + /* + * If we returned the first string, optimize a bit by + * backing up over comma + */ + if (newstr == cp) { + xbp->xb_curp = two - 1; /* One for comma */ + /* + * If the caller wanted UTF8, we're done; nothing changed, + * but we need to count the columns used. + */ + if (need_enc == XF_ENC_UTF8) + return xo_count_utf8_cols(cp, xbp->xb_curp - cp); + } + + } else { + /* The simple case (singular) */ + newstr = xo_dgettext(xop, cp); + + if (newstr == cp) { + /* If the caller wanted UTF8, we're done; nothing changed */ + if (need_enc == XF_ENC_UTF8) + return cols; + } + } + + /* + * Since the new string string might be in gettext's buffer or + * in the buffer (as the plural form), we make a copy. + */ + int nlen = strlen(newstr); + char *newcopy = alloca(nlen + 1); + memcpy(newcopy, newstr, nlen + 1); + + xbp->xb_curp = xbp->xb_bufp + start_offset; /* Reset the buffer */ + return xo_format_string_direct(xop, xbp, flags, NULL, newcopy, nlen, 0, + need_enc, XF_ENC_UTF8); +} + +static void +xo_data_append_content (xo_handle_t *xop, const char *str, int len, + xo_xff_flags_t flags) +{ + int cols; + int need_enc = xo_needed_encoding(xop); + int start_offset = xo_buf_offset(&xop->xo_data); + + cols = xo_format_string_direct(xop, &xop->xo_data, XFF_UNESCAPE | flags, + NULL, str, len, -1, + need_enc, XF_ENC_UTF8); + if (flags & XFF_GT_FLAGS) + cols = xo_format_gettext(xop, flags, start_offset, cols, need_enc); + + if (XOF_ISSET(xop, XOF_COLUMNS)) + xop->xo_columns += cols; + if (XOIF_ISSET(xop, XOIF_ANCHOR)) + xop->xo_anchor_columns += cols; +} + +static void +xo_bump_width (xo_format_t *xfp, int digit) +{ + int *ip = &xfp->xf_width[xfp->xf_dots]; + + *ip = ((*ip > 0) ? *ip : 0) * 10 + digit; +} + +static int +xo_trim_ws (xo_buffer_t *xbp, int len) +{ + char *cp, *sp, *ep; + int delta; + + /* First trim leading space */ + for (cp = sp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { + if (*cp != ' ') + break; + } + + delta = cp - sp; + if (delta) { + len -= delta; + memmove(sp, cp, len); + } + + /* Then trim off the end */ + for (cp = xbp->xb_curp, sp = ep = cp + len; cp < ep; ep--) { + if (ep[-1] != ' ') + break; + } + + delta = sp - ep; + if (delta) { + len -= delta; + cp[len] = '\0'; + } + + return len; +} + +/* + * Interface to format a single field. The arguments are in xo_vap, + * and the format is in 'fmt'. If 'xbp' is null, we use xop->xo_data; + * this is the most common case. + */ +static int +xo_do_format_field (xo_handle_t *xop, xo_buffer_t *xbp, + const char *fmt, int flen, xo_xff_flags_t flags) +{ + xo_format_t xf; + const char *cp, *ep, *sp, *xp = NULL; + int rc, cols; + int style = (flags & XFF_XML) ? XO_STYLE_XML : xo_style(xop); + unsigned make_output = !(flags & XFF_NO_OUTPUT); + int need_enc = xo_needed_encoding(xop); + int real_need_enc = need_enc; + int old_cols = xop->xo_columns; + + /* The gettext interface is UTF-8, so we'll need that for now */ + if (flags & XFF_GT_FIELD) + need_enc = XF_ENC_UTF8; + + if (xbp == NULL) + xbp = &xop->xo_data; + + unsigned start_offset = xo_buf_offset(xbp); + + for (cp = fmt, ep = fmt + flen; cp < ep; cp++) { + /* + * Since we're starting a new field, save the starting offset. + * We'll need this later for field-related operations. + */ + + if (*cp != '%') { + add_one: + if (xp == NULL) + xp = cp; + + if (*cp == '\\' && cp[1] != '\0') + cp += 1; + continue; + + } if (cp + 1 < ep && cp[1] == '%') { + cp += 1; + goto add_one; + } + + if (xp) { + if (make_output) { + cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE, + NULL, xp, cp - xp, -1, + need_enc, XF_ENC_UTF8); + if (XOF_ISSET(xop, XOF_COLUMNS)) + xop->xo_columns += cols; + if (XOIF_ISSET(xop, XOIF_ANCHOR)) + xop->xo_anchor_columns += cols; + } + + xp = NULL; + } + + bzero(&xf, sizeof(xf)); + xf.xf_leading_zero = -1; + xf.xf_width[0] = xf.xf_width[1] = xf.xf_width[2] = -1; + + /* + * "%@" starts an XO-specific set of flags: + * @X@ - XML-only field; ignored if style isn't XML + */ + if (cp[1] == '@') { + for (cp += 2; cp < ep; cp++) { + if (*cp == '@') { + break; + } + if (*cp == '*') { + /* + * '*' means there's a "%*.*s" value in vap that + * we want to ignore + */ + if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) + va_arg(xop->xo_vap, int); + } + } + } + + /* Hidden fields are only visible to JSON and XML */ + if (XOF_ISSET(xop, XFF_ENCODE_ONLY)) { + if (style != XO_STYLE_XML + && !xo_style_is_encoding(xop)) + xf.xf_skip = 1; + } else if (XOF_ISSET(xop, XFF_DISPLAY_ONLY)) { + if (style != XO_STYLE_TEXT + && xo_style(xop) != XO_STYLE_HTML) + xf.xf_skip = 1; + } + + if (!make_output) + xf.xf_skip = 1; + + /* + * Looking at one piece of a format; find the end and + * call snprintf. Then advance xo_vap on our own. + * + * Note that 'n', 'v', and '$' are not supported. + */ + sp = cp; /* Save start pointer */ + for (cp += 1; cp < ep; cp++) { + if (*cp == 'l') + xf.xf_lflag += 1; + else if (*cp == 'h') + xf.xf_hflag += 1; + else if (*cp == 'j') + xf.xf_jflag += 1; + else if (*cp == 't') + xf.xf_tflag += 1; + else if (*cp == 'z') + xf.xf_zflag += 1; + else if (*cp == 'q') + xf.xf_qflag += 1; + else if (*cp == '.') { + if (++xf.xf_dots >= XF_WIDTH_NUM) { + xo_failure(xop, "Too many dots in format: '%s'", fmt); + return -1; + } + } else if (*cp == '-') + xf.xf_seen_minus = 1; + else if (isdigit((int) *cp)) { + if (xf.xf_leading_zero < 0) + xf.xf_leading_zero = (*cp == '0'); + xo_bump_width(&xf, *cp - '0'); + } else if (*cp == '*') { + xf.xf_stars += 1; + xf.xf_star[xf.xf_dots] = 1; + } else if (strchr("diouxXDOUeEfFgGaAcCsSpm", *cp) != NULL) + break; + else if (*cp == 'n' || *cp == 'v') { + xo_failure(xop, "unsupported format: '%s'", fmt); + return -1; + } + } + + if (cp == ep) + xo_failure(xop, "field format missing format character: %s", + fmt); + + xf.xf_fc = *cp; + + if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) { + if (*cp == 's' || *cp == 'S') { + /* Handle "%*.*.*s" */ + int s; + for (s = 0; s < XF_WIDTH_NUM; s++) { + if (xf.xf_star[s]) { + xf.xf_width[s] = va_arg(xop->xo_vap, int); + + /* Normalize a negative width value */ + if (xf.xf_width[s] < 0) { + if (s == 0) { + xf.xf_width[0] = -xf.xf_width[0]; + xf.xf_seen_minus = 1; + } else + xf.xf_width[s] = -1; /* Ignore negative values */ + } + } + } + } + } + + /* If no max is given, it defaults to size */ + if (xf.xf_width[XF_WIDTH_MAX] < 0 && xf.xf_width[XF_WIDTH_SIZE] >= 0) + xf.xf_width[XF_WIDTH_MAX] = xf.xf_width[XF_WIDTH_SIZE]; + + if (xf.xf_fc == 'D' || xf.xf_fc == 'O' || xf.xf_fc == 'U') + xf.xf_lflag = 1; + + if (!xf.xf_skip) { + xo_buffer_t *fbp = &xop->xo_fmt; + int len = cp - sp + 1; + if (!xo_buf_has_room(fbp, len + 1)) + return -1; + + char *newfmt = fbp->xb_curp; + memcpy(newfmt, sp, len); + newfmt[0] = '%'; /* If we skipped over a "%@...@s" format */ + newfmt[len] = '\0'; + + /* + * Bad news: our strings are UTF-8, but the stock printf + * functions won't handle field widths for wide characters + * correctly. So we have to handle this ourselves. + */ + if (xop->xo_formatter == NULL + && (xf.xf_fc == 's' || xf.xf_fc == 'S' + || xf.xf_fc == 'm')) { + + xf.xf_enc = (xf.xf_fc == 'm') ? XF_ENC_UTF8 + : (xf.xf_lflag || (xf.xf_fc == 'S')) ? XF_ENC_WIDE + : xf.xf_hflag ? XF_ENC_LOCALE : XF_ENC_UTF8; + + rc = xo_format_string(xop, xbp, flags, &xf); + + if ((flags & XFF_TRIM_WS) && xo_style_is_encoding(xop)) + rc = xo_trim_ws(xbp, rc); + + } else { + int columns = rc = xo_vsnprintf(xop, xbp, newfmt, xop->xo_vap); + + /* + * For XML and HTML, we need "&<>" processing; for JSON, + * it's quotes. Text gets nothing. + */ + switch (style) { + case XO_STYLE_XML: + if (flags & XFF_TRIM_WS) + columns = rc = xo_trim_ws(xbp, rc); + /* fall thru */ + case XO_STYLE_HTML: + rc = xo_escape_xml(xbp, rc, (flags & XFF_ATTR)); + break; + + case XO_STYLE_JSON: + if (flags & XFF_TRIM_WS) + columns = rc = xo_trim_ws(xbp, rc); + rc = xo_escape_json(xbp, rc, 0); + break; + + case XO_STYLE_SDPARAMS: + if (flags & XFF_TRIM_WS) + columns = rc = xo_trim_ws(xbp, rc); + rc = xo_escape_sdparams(xbp, rc, 0); + break; + + case XO_STYLE_ENCODER: + if (flags & XFF_TRIM_WS) + columns = rc = xo_trim_ws(xbp, rc); + break; + } + + /* + * We can assume all the non-%s data we've + * added is ASCII, so the columns and bytes are the + * same. xo_format_string handles all the fancy + * string conversions and updates xo_anchor_columns + * accordingly. + */ + if (XOF_ISSET(xop, XOF_COLUMNS)) + xop->xo_columns += columns; + if (XOIF_ISSET(xop, XOIF_ANCHOR)) + xop->xo_anchor_columns += columns; + } + + xbp->xb_curp += rc; + } + + /* + * Now for the tricky part: we need to move the argument pointer + * along by the amount needed. + */ + if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) { + + if (xf.xf_fc == 's' ||xf.xf_fc == 'S') { + /* + * The 'S' and 's' formats are normally handled in + * xo_format_string, but if we skipped it, then we + * need to pop it. + */ + if (xf.xf_skip) + va_arg(xop->xo_vap, char *); + + } else if (xf.xf_fc == 'm') { + /* Nothing on the stack for "%m" */ + + } else { + int s; + for (s = 0; s < XF_WIDTH_NUM; s++) { + if (xf.xf_star[s]) + va_arg(xop->xo_vap, int); + } + + if (strchr("diouxXDOU", xf.xf_fc) != NULL) { + if (xf.xf_hflag > 1) { + va_arg(xop->xo_vap, int); + + } else if (xf.xf_hflag > 0) { + va_arg(xop->xo_vap, int); + + } else if (xf.xf_lflag > 1) { + va_arg(xop->xo_vap, unsigned long long); + + } else if (xf.xf_lflag > 0) { + va_arg(xop->xo_vap, unsigned long); + + } else if (xf.xf_jflag > 0) { + va_arg(xop->xo_vap, intmax_t); + + } else if (xf.xf_tflag > 0) { + va_arg(xop->xo_vap, ptrdiff_t); + + } else if (xf.xf_zflag > 0) { + va_arg(xop->xo_vap, size_t); + + } else if (xf.xf_qflag > 0) { + va_arg(xop->xo_vap, quad_t); + + } else { + va_arg(xop->xo_vap, int); + } + } else if (strchr("eEfFgGaA", xf.xf_fc) != NULL) + if (xf.xf_lflag) + va_arg(xop->xo_vap, long double); + else + va_arg(xop->xo_vap, double); + + else if (xf.xf_fc == 'C' || (xf.xf_fc == 'c' && xf.xf_lflag)) + va_arg(xop->xo_vap, wint_t); + + else if (xf.xf_fc == 'c') + va_arg(xop->xo_vap, int); + + else if (xf.xf_fc == 'p') + va_arg(xop->xo_vap, void *); + } + } + } + + if (xp) { + if (make_output) { + cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE, + NULL, xp, cp - xp, -1, + need_enc, XF_ENC_UTF8); + + if (XOF_ISSET(xop, XOF_COLUMNS)) + xop->xo_columns += cols; + if (XOIF_ISSET(xop, XOIF_ANCHOR)) + xop->xo_anchor_columns += cols; + } + + xp = NULL; + } + + if (flags & XFF_GT_FLAGS) { + /* + * Handle gettext()ing the field by looking up the value + * and then copying it in, while converting to locale, if + * needed. + */ + int new_cols = xo_format_gettext(xop, flags, start_offset, + old_cols, real_need_enc); + + if (XOF_ISSET(xop, XOF_COLUMNS)) + xop->xo_columns += new_cols - old_cols; + if (XOIF_ISSET(xop, XOIF_ANCHOR)) + xop->xo_anchor_columns += new_cols - old_cols; + } + + return 0; +} + +static char * +xo_fix_encoding (xo_handle_t *xop UNUSED, char *encoding) +{ + char *cp = encoding; + + if (cp[0] != '%' || !isdigit((int) cp[1])) + return encoding; + + for (cp += 2; *cp; cp++) { + if (!isdigit((int) *cp)) + break; + } + + cp -= 1; + *cp = '%'; + + return cp; +} + +static void +xo_color_append_html (xo_handle_t *xop) +{ + /* + * If the color buffer has content, we add it now. It's already + * prebuilt and ready, since we want to add it to every
. + */ + if (!xo_buf_is_empty(&xop->xo_color_buf)) { + xo_buffer_t *xbp = &xop->xo_color_buf; + + xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp); + } +} + +/* + * A wrapper for humanize_number that autoscales, since the + * HN_AUTOSCALE flag scales as needed based on the size of + * the output buffer, not the size of the value. I also + * wish HN_DECIMAL was more imperative, without the <10 + * test. But the boat only goes where we want when we hold + * the rudder, so xo_humanize fixes part of the problem. + */ +static int +xo_humanize (char *buf, int len, uint64_t value, int flags) +{ + int scale = 0; + + if (value) { + uint64_t left = value; + + if (flags & HN_DIVISOR_1000) { + for ( ; left; scale++) + left /= 1000; + } else { + for ( ; left; scale++) + left /= 1024; + } + scale -= 1; + } + + return xo_humanize_number(buf, len, value, "", scale, flags); +} + +/* + * This is an area where we can save information from the handle for + * later restoration. We need to know what data was rendered to know + * what needs cleaned up. + */ +typedef struct xo_humanize_save_s { + unsigned xhs_offset; /* Saved xo_offset */ + unsigned xhs_columns; /* Saved xo_columns */ + unsigned xhs_anchor_columns; /* Saved xo_anchor_columns */ +} xo_humanize_save_t; + +/* + * Format a "humanized" value for a numeric, meaning something nice + * like "44M" instead of "44470272". We autoscale, choosing the + * most appropriate value for K/M/G/T/P/E based on the value given. + */ +static void +xo_format_humanize (xo_handle_t *xop, xo_buffer_t *xbp, + xo_humanize_save_t *savep, xo_xff_flags_t flags) +{ + if (XOF_ISSET(xop, XOF_NO_HUMANIZE)) + return; + + unsigned end_offset = xbp->xb_curp - xbp->xb_bufp; + if (end_offset == savep->xhs_offset) /* Huh? Nothing to render */ + return; + + /* + * We have a string that's allegedly a number. We want to + * humanize it, which means turning it back into a number + * and calling xo_humanize_number on it. + */ + uint64_t value; + char *ep; + + xo_buf_append(xbp, "", 1); /* NUL-terminate it */ + + value = strtoull(xbp->xb_bufp + savep->xhs_offset, &ep, 0); + if (!(value == ULLONG_MAX && errno == ERANGE) + && (ep != xbp->xb_bufp + savep->xhs_offset)) { + /* + * There are few values where humanize_number needs + * more bytes than the original value. I've used + * 10 as a rectal number to cover those scenarios. + */ + if (xo_buf_has_room(xbp, 10)) { + xbp->xb_curp = xbp->xb_bufp + savep->xhs_offset; + + int rc; + int left = (xbp->xb_bufp + xbp->xb_size) - xbp->xb_curp; + int hn_flags = HN_NOSPACE; /* On by default */ + + if (flags & XFF_HN_SPACE) + hn_flags &= ~HN_NOSPACE; + + if (flags & XFF_HN_DECIMAL) + hn_flags |= HN_DECIMAL; + + if (flags & XFF_HN_1000) + hn_flags |= HN_DIVISOR_1000; + + rc = xo_humanize(xbp->xb_curp, + left, value, hn_flags); + if (rc > 0) { + xbp->xb_curp += rc; + xop->xo_columns = savep->xhs_columns + rc; + xop->xo_anchor_columns = savep->xhs_anchor_columns + rc; + } + } + } +} + +static void +xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, + const char *name, int nlen, + const char *value, int vlen, + const char *encoding, int elen) +{ + static char div_start[] = "
"; + static char div_close[] = "
"; + + /* + * To build our XPath predicate, we need to save the va_list before + * we format our data, and then restore it before we format the + * xpath expression. + * Display-only keys implies that we've got an encode-only key + * elsewhere, so we don't use them from making predicates. + */ + int need_predidate = + (name && (flags & XFF_KEY) && !(flags & XFF_DISPLAY_ONLY) + && XOF_ISSET(xop, XOF_XPATH)); + + if (need_predidate) { + va_list va_local; + + va_copy(va_local, xop->xo_vap); + if (xop->xo_checkpointer) + xop->xo_checkpointer(xop, xop->xo_vap, 0); + + /* + * Build an XPath predicate expression to match this key. + * We use the format buffer. + */ + xo_buffer_t *pbp = &xop->xo_predicate; + pbp->xb_curp = pbp->xb_bufp; /* Restart buffer */ + + xo_buf_append(pbp, "[", 1); + xo_buf_escape(xop, pbp, name, nlen, 0); + if (XOF_ISSET(xop, XOF_PRETTY)) + xo_buf_append(pbp, " = '", 4); + else + xo_buf_append(pbp, "='", 2); + + /* The encoding format defaults to the normal format */ + if (encoding == NULL) { + char *enc = alloca(vlen + 1); + memcpy(enc, value, vlen); + enc[vlen] = '\0'; + encoding = xo_fix_encoding(xop, enc); + elen = strlen(encoding); + } + + xo_xff_flags_t pflags = flags | XFF_XML | XFF_ATTR; + pflags &= ~(XFF_NO_OUTPUT | XFF_ENCODE_ONLY); + xo_do_format_field(xop, pbp, encoding, elen, pflags); + + xo_buf_append(pbp, "']", 2); + + /* Now we record this predicate expression in the stack */ + xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; + int olen = xsp->xs_keys ? strlen(xsp->xs_keys) : 0; + int dlen = pbp->xb_curp - pbp->xb_bufp; + + char *cp = xo_realloc(xsp->xs_keys, olen + dlen + 1); + if (cp) { + memcpy(cp + olen, pbp->xb_bufp, dlen); + cp[olen + dlen] = '\0'; + xsp->xs_keys = cp; + } + + /* Now we reset the xo_vap as if we were never here */ + va_end(xop->xo_vap); + va_copy(xop->xo_vap, va_local); + va_end(va_local); + if (xop->xo_checkpointer) + xop->xo_checkpointer(xop, xop->xo_vap, 1); + } + + if (flags & XFF_ENCODE_ONLY) { + /* + * Even if this is encode-only, we need to go thru the + * work of formatting it to make sure the args are cleared + * from xo_vap. + */ + xo_do_format_field(xop, NULL, encoding, elen, + flags | XFF_NO_OUTPUT); + return; + } + + xo_line_ensure_open(xop, 0); + + if (XOF_ISSET(xop, XOF_PRETTY)) + xo_buf_indent(xop, xop->xo_indent_by); + + xo_data_append(xop, div_start, sizeof(div_start) - 1); + xo_data_append(xop, class, strlen(class)); + + /* + * If the color buffer has content, we add it now. It's already + * prebuilt and ready, since we want to add it to every
. + */ + if (!xo_buf_is_empty(&xop->xo_color_buf)) { + xo_buffer_t *xbp = &xop->xo_color_buf; + + xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp); + } + + if (name) { + xo_data_append(xop, div_tag, sizeof(div_tag) - 1); + xo_data_escape(xop, name, nlen); + + /* + * Save the offset at which we'd place units. See xo_format_units. + */ + if (XOF_ISSET(xop, XOF_UNITS)) { + XOIF_SET(xop, XOIF_UNITS_PENDING); + /* + * Note: We need the '+1' here because we know we've not + * added the closing quote. We add one, knowing the quote + * will be added shortly. + */ + xop->xo_units_offset = + xop->xo_data.xb_curp -xop->xo_data.xb_bufp + 1; + } + + if (XOF_ISSET(xop, XOF_XPATH)) { + int i; + xo_stack_t *xsp; + + xo_data_append(xop, div_xpath, sizeof(div_xpath) - 1); + if (xop->xo_leading_xpath) + xo_data_append(xop, xop->xo_leading_xpath, + strlen(xop->xo_leading_xpath)); + + for (i = 0; i <= xop->xo_depth; i++) { + xsp = &xop->xo_stack[i]; + if (xsp->xs_name == NULL) + continue; + + /* + * XSS_OPEN_LIST and XSS_OPEN_LEAF_LIST stack frames + * are directly under XSS_OPEN_INSTANCE frames so we + * don't need to put these in our XPath expressions. + */ + if (xsp->xs_state == XSS_OPEN_LIST + || xsp->xs_state == XSS_OPEN_LEAF_LIST) + continue; + + xo_data_append(xop, "/", 1); + xo_data_escape(xop, xsp->xs_name, strlen(xsp->xs_name)); + if (xsp->xs_keys) { + /* Don't show keys for the key field */ + if (i != xop->xo_depth || !(flags & XFF_KEY)) + xo_data_append(xop, xsp->xs_keys, strlen(xsp->xs_keys)); + } + } + + xo_data_append(xop, "/", 1); + xo_data_escape(xop, name, nlen); + } + + if (XOF_ISSET(xop, XOF_INFO) && xop->xo_info) { + static char in_type[] = "\" data-type=\""; + static char in_help[] = "\" data-help=\""; + + xo_info_t *xip = xo_info_find(xop, name, nlen); + if (xip) { + if (xip->xi_type) { + xo_data_append(xop, in_type, sizeof(in_type) - 1); + xo_data_escape(xop, xip->xi_type, strlen(xip->xi_type)); + } + if (xip->xi_help) { + xo_data_append(xop, in_help, sizeof(in_help) - 1); + xo_data_escape(xop, xip->xi_help, strlen(xip->xi_help)); + } + } + } + + if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS)) + xo_data_append(xop, div_key, sizeof(div_key) - 1); + } + + xo_buffer_t *xbp = &xop->xo_data; + unsigned base_offset = xbp->xb_curp - xbp->xb_bufp; + + xo_data_append(xop, div_end, sizeof(div_end) - 1); + + xo_humanize_save_t save; /* Save values for humanizing logic */ + + save.xhs_offset = xbp->xb_curp - xbp->xb_bufp; + save.xhs_columns = xop->xo_columns; + save.xhs_anchor_columns = xop->xo_anchor_columns; + + xo_do_format_field(xop, NULL, value, vlen, flags); + + if (flags & XFF_HUMANIZE) { + /* + * Unlike text style, we want to retain the original value and + * stuff it into the "data-number" attribute. + */ + static const char div_number[] = "\" data-number=\""; + int div_len = sizeof(div_number) - 1; + + unsigned end_offset = xbp->xb_curp - xbp->xb_bufp; + int olen = end_offset - save.xhs_offset; + + char *cp = alloca(olen + 1); + memcpy(cp, xbp->xb_bufp + save.xhs_offset, olen); + cp[olen] = '\0'; + + xo_format_humanize(xop, xbp, &save, flags); + + if (xo_buf_has_room(xbp, div_len + olen)) { + unsigned new_offset = xbp->xb_curp - xbp->xb_bufp; + + + /* Move the humanized string off to the left */ + memmove(xbp->xb_bufp + base_offset + div_len + olen, + xbp->xb_bufp + base_offset, new_offset - base_offset); + + /* Copy the data_number attribute name */ + memcpy(xbp->xb_bufp + base_offset, div_number, div_len); + + /* Copy the original long value */ + memcpy(xbp->xb_bufp + base_offset + div_len, cp, olen); + xbp->xb_curp += div_len + olen; + } + } + + xo_data_append(xop, div_close, sizeof(div_close) - 1); + + if (XOF_ISSET(xop, XOF_PRETTY)) + xo_data_append(xop, "\n", 1); +} + +static void +xo_format_text (xo_handle_t *xop, const char *str, int len) +{ + switch (xo_style(xop)) { + case XO_STYLE_TEXT: + xo_buf_append_locale(xop, &xop->xo_data, str, len); + break; + + case XO_STYLE_HTML: + xo_buf_append_div(xop, "text", 0, NULL, 0, str, len, NULL, 0); + break; + } +} + +static void +xo_format_title (xo_handle_t *xop, xo_field_info_t *xfip) +{ + const char *str = xfip->xfi_content; + unsigned len = xfip->xfi_clen; + const char *fmt = xfip->xfi_format; + unsigned flen = xfip->xfi_flen; + xo_xff_flags_t flags = xfip->xfi_flags; + + static char div_open[] = "
"; + static char div_close[] = "
"; + + if (flen == 0) { + fmt = "%s"; + flen = 2; + } + + switch (xo_style(xop)) { + case XO_STYLE_XML: + case XO_STYLE_JSON: + case XO_STYLE_SDPARAMS: + case XO_STYLE_ENCODER: + /* + * Even though we don't care about text, we need to do + * enough parsing work to skip over the right bits of xo_vap. + */ + if (len == 0) + xo_do_format_field(xop, NULL, fmt, flen, flags | XFF_NO_OUTPUT); + return; + } + + xo_buffer_t *xbp = &xop->xo_data; + int start = xbp->xb_curp - xbp->xb_bufp; + int left = xbp->xb_size - start; + int rc; + + if (xo_style(xop) == XO_STYLE_HTML) { + xo_line_ensure_open(xop, 0); + if (XOF_ISSET(xop, XOF_PRETTY)) + xo_buf_indent(xop, xop->xo_indent_by); + xo_buf_append(&xop->xo_data, div_open, sizeof(div_open) - 1); + xo_color_append_html(xop); + xo_buf_append(&xop->xo_data, div_middle, sizeof(div_middle) - 1); + } + + start = xbp->xb_curp - xbp->xb_bufp; /* Reset start */ + if (len) { + char *newfmt = alloca(flen + 1); + memcpy(newfmt, fmt, flen); + newfmt[flen] = '\0'; + + /* If len is non-zero, the format string apply to the name */ + char *newstr = alloca(len + 1); + memcpy(newstr, str, len); + newstr[len] = '\0'; + + if (newstr[len - 1] == 's') { + char *bp; + + rc = snprintf(NULL, 0, newfmt, newstr); + if (rc > 0) { + /* + * We have to do this the hard way, since we might need + * the columns. + */ + bp = alloca(rc + 1); + rc = snprintf(bp, rc + 1, newfmt, newstr); + + xo_data_append_content(xop, bp, rc, flags); + } + goto move_along; + + } else { + rc = snprintf(xbp->xb_curp, left, newfmt, newstr); + if (rc >= left) { + if (!xo_buf_has_room(xbp, rc)) + return; + left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); + rc = snprintf(xbp->xb_curp, left, newfmt, newstr); + } + + if (rc > 0) { + if (XOF_ISSET(xop, XOF_COLUMNS)) + xop->xo_columns += rc; + if (XOIF_ISSET(xop, XOIF_ANCHOR)) + xop->xo_anchor_columns += rc; + } + } + + } else { + xo_do_format_field(xop, NULL, fmt, flen, flags); + + /* xo_do_format_field moved curp, so we need to reset it */ + rc = xbp->xb_curp - (xbp->xb_bufp + start); + xbp->xb_curp = xbp->xb_bufp + start; + } + + /* If we're styling HTML, then we need to escape it */ + if (xo_style(xop) == XO_STYLE_HTML) { + rc = xo_escape_xml(xbp, rc, 0); + } + + if (rc > 0) + xbp->xb_curp += rc; + + move_along: + if (xo_style(xop) == XO_STYLE_HTML) { + xo_data_append(xop, div_close, sizeof(div_close) - 1); + if (XOF_ISSET(xop, XOF_PRETTY)) + xo_data_append(xop, "\n", 1); + } +} + +static void +xo_format_prep (xo_handle_t *xop, xo_xff_flags_t flags) +{ + if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) { + xo_data_append(xop, ",", 1); + if (!(flags & XFF_LEAF_LIST) && XOF_ISSET(xop, XOF_PRETTY)) + xo_data_append(xop, "\n", 1); + } else + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; +} + +#if 0 +/* Useful debugging function */ +void +xo_arg (xo_handle_t *xop); +void +xo_arg (xo_handle_t *xop) +{ + xop = xo_default(xop); + fprintf(stderr, "0x%x", va_arg(xop->xo_vap, unsigned)); +} +#endif /* 0 */ + +static void +xo_format_value (xo_handle_t *xop, const char *name, int nlen, + const char *format, int flen, + const char *encoding, int elen, xo_xff_flags_t flags) +{ + int pretty = XOF_ISSET(xop, XOF_PRETTY); + int quote; + + /* + * Before we emit a value, we need to know that the frame is ready. + */ + xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; + + if (flags & XFF_LEAF_LIST) { + /* + * Check if we've already started to emit normal leafs + * or if we're not in a leaf list. + */ + if ((xsp->xs_flags & (XSF_EMIT | XSF_EMIT_KEY)) + || !(xsp->xs_flags & XSF_EMIT_LEAF_LIST)) { + char nbuf[nlen + 1]; + memcpy(nbuf, name, nlen); + nbuf[nlen] = '\0'; + + int rc = xo_transition(xop, 0, nbuf, XSS_EMIT_LEAF_LIST); + if (rc < 0) + flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; + else + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_LEAF_LIST; + } + + xsp = &xop->xo_stack[xop->xo_depth]; + if (xsp->xs_name) { + name = xsp->xs_name; + nlen = strlen(name); + } + + } else if (flags & XFF_KEY) { + /* Emitting a 'k' (key) field */ + if ((xsp->xs_flags & XSF_EMIT) && !(flags & XFF_DISPLAY_ONLY)) { + xo_failure(xop, "key field emitted after normal value field: '%.*s'", + nlen, name); + + } else if (!(xsp->xs_flags & XSF_EMIT_KEY)) { + char nbuf[nlen + 1]; + memcpy(nbuf, name, nlen); + nbuf[nlen] = '\0'; + + int rc = xo_transition(xop, 0, nbuf, XSS_EMIT); + if (rc < 0) + flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; + else + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_KEY; + + xsp = &xop->xo_stack[xop->xo_depth]; + xsp->xs_flags |= XSF_EMIT_KEY; + } + + } else { + /* Emitting a normal value field */ + if ((xsp->xs_flags & XSF_EMIT_LEAF_LIST) + || !(xsp->xs_flags & XSF_EMIT)) { + char nbuf[nlen + 1]; + memcpy(nbuf, name, nlen); + nbuf[nlen] = '\0'; + + int rc = xo_transition(xop, 0, nbuf, XSS_EMIT); + if (rc < 0) + flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; + else + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT; + + xsp = &xop->xo_stack[xop->xo_depth]; + xsp->xs_flags |= XSF_EMIT; + } + } + + xo_buffer_t *xbp = &xop->xo_data; + xo_humanize_save_t save; /* Save values for humanizing logic */ + + switch (xo_style(xop)) { + case XO_STYLE_TEXT: + if (flags & XFF_ENCODE_ONLY) + flags |= XFF_NO_OUTPUT; + + save.xhs_offset = xbp->xb_curp - xbp->xb_bufp; + save.xhs_columns = xop->xo_columns; + save.xhs_anchor_columns = xop->xo_anchor_columns; + + xo_do_format_field(xop, NULL, format, flen, flags); + + if (flags & XFF_HUMANIZE) + xo_format_humanize(xop, xbp, &save, flags); + break; + + case XO_STYLE_HTML: + if (flags & XFF_ENCODE_ONLY) + flags |= XFF_NO_OUTPUT; + + xo_buf_append_div(xop, "data", flags, name, nlen, + format, flen, encoding, elen); + break; + + case XO_STYLE_XML: + /* + * Even though we're not making output, we still need to + * let the formatting code handle the va_arg popping. + */ + if (flags & XFF_DISPLAY_ONLY) { + flags |= XFF_NO_OUTPUT; + xo_do_format_field(xop, NULL, format, flen, flags); + break; + } + + if (encoding) { + format = encoding; + flen = elen; + } else { + char *enc = alloca(flen + 1); + memcpy(enc, format, flen); + enc[flen] = '\0'; + format = xo_fix_encoding(xop, enc); + flen = strlen(format); + } + + if (nlen == 0) { + static char missing[] = "missing-field-name"; + xo_failure(xop, "missing field name: %s", format); + name = missing; + nlen = sizeof(missing) - 1; + } + + if (pretty) + xo_buf_indent(xop, -1); + xo_data_append(xop, "<", 1); + xo_data_escape(xop, name, nlen); + + if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { + xo_data_append(xop, xop->xo_attrs.xb_bufp, + xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); + xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; + } + + /* + * We indicate 'key' fields using the 'key' attribute. While + * this is really committing the crime of mixing meta-data with + * data, it's often useful. Especially when format meta-data is + * difficult to come by. + */ + if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS)) { + static char attr[] = " key=\"key\""; + xo_data_append(xop, attr, sizeof(attr) - 1); + } + + /* + * Save the offset at which we'd place units. See xo_format_units. + */ + if (XOF_ISSET(xop, XOF_UNITS)) { + XOIF_SET(xop, XOIF_UNITS_PENDING); + xop->xo_units_offset = xop->xo_data.xb_curp -xop->xo_data.xb_bufp; + } + + xo_data_append(xop, ">", 1); + xo_do_format_field(xop, NULL, format, flen, flags); + xo_data_append(xop, "", 1); + if (pretty) + xo_data_append(xop, "\n", 1); + break; + + case XO_STYLE_JSON: + if (flags & XFF_DISPLAY_ONLY) { + flags |= XFF_NO_OUTPUT; + xo_do_format_field(xop, NULL, format, flen, flags); + break; + } + + if (encoding) { + format = encoding; + flen = elen; + } else { + char *enc = alloca(flen + 1); + memcpy(enc, format, flen); + enc[flen] = '\0'; + format = xo_fix_encoding(xop, enc); + flen = strlen(format); + } + + int first = !(xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST); + + xo_format_prep(xop, flags); + + if (flags & XFF_QUOTE) + quote = 1; + else if (flags & XFF_NOQUOTE) + quote = 0; + else if (flen == 0) { + quote = 0; + format = "true"; /* JSON encodes empty tags as a boolean true */ + flen = 4; + } else if (strchr("diouxXDOUeEfFgGaAcCp", format[flen - 1]) == NULL) + quote = 1; + else + quote = 0; + + if (nlen == 0) { + static char missing[] = "missing-field-name"; + xo_failure(xop, "missing field name: %s", format); + name = missing; + nlen = sizeof(missing) - 1; + } + + if (flags & XFF_LEAF_LIST) { + if (!first && pretty) + xo_data_append(xop, "\n", 1); + if (pretty) + xo_buf_indent(xop, -1); + } else { + if (pretty) + xo_buf_indent(xop, -1); + xo_data_append(xop, "\"", 1); + + xbp = &xop->xo_data; + int off = xbp->xb_curp - xbp->xb_bufp; + + xo_data_escape(xop, name, nlen); + + if (XOF_ISSET(xop, XOF_UNDERSCORES)) { + int now = xbp->xb_curp - xbp->xb_bufp; + for ( ; off < now; off++) + if (xbp->xb_bufp[off] == '-') + xbp->xb_bufp[off] = '_'; + } + xo_data_append(xop, "\":", 2); + if (pretty) + xo_data_append(xop, " ", 1); + } + + if (quote) + xo_data_append(xop, "\"", 1); + + xo_do_format_field(xop, NULL, format, flen, flags); + + if (quote) + xo_data_append(xop, "\"", 1); + break; + + case XO_STYLE_SDPARAMS: + if (flags & XFF_DISPLAY_ONLY) { + flags |= XFF_NO_OUTPUT; + xo_do_format_field(xop, NULL, format, flen, flags); + break; + } + + if (encoding) { + format = encoding; + flen = elen; + } else { + char *enc = alloca(flen + 1); + memcpy(enc, format, flen); + enc[flen] = '\0'; + format = xo_fix_encoding(xop, enc); + flen = strlen(format); + } + + if (nlen == 0) { + static char missing[] = "missing-field-name"; + xo_failure(xop, "missing field name: %s", format); + name = missing; + nlen = sizeof(missing) - 1; + } + + xo_data_escape(xop, name, nlen); + xo_data_append(xop, "=\"", 2); + xo_do_format_field(xop, NULL, format, flen, flags); + xo_data_append(xop, "\" ", 2); + break; + + case XO_STYLE_ENCODER: + if (flags & XFF_DISPLAY_ONLY) { + flags |= XFF_NO_OUTPUT; + xo_do_format_field(xop, NULL, format, flen, flags); + break; + } + + if (flags & XFF_QUOTE) + quote = 1; + else if (flags & XFF_NOQUOTE) + quote = 0; + else if (flen == 0) { + quote = 0; + format = "true"; /* JSON encodes empty tags as a boolean true */ + flen = 4; + } else if (strchr("diouxXDOUeEfFgGaAcCp", format[flen - 1]) == NULL) + quote = 1; + else + quote = 0; + + if (encoding) { + format = encoding; + flen = elen; + } else { + char *enc = alloca(flen + 1); + memcpy(enc, format, flen); + enc[flen] = '\0'; + format = xo_fix_encoding(xop, enc); + flen = strlen(format); + } + + if (nlen == 0) { + static char missing[] = "missing-field-name"; + xo_failure(xop, "missing field name: %s", format); + name = missing; + nlen = sizeof(missing) - 1; + } + + unsigned name_offset = xo_buf_offset(&xop->xo_data); + xo_data_append(xop, name, nlen); + xo_data_append(xop, "", 1); + + unsigned value_offset = xo_buf_offset(&xop->xo_data); + xo_do_format_field(xop, NULL, format, flen, flags); + xo_data_append(xop, "", 1); + + xo_encoder_handle(xop, quote ? XO_OP_STRING : XO_OP_CONTENT, + xo_buf_data(&xop->xo_data, name_offset), + xo_buf_data(&xop->xo_data, value_offset)); + xo_buf_reset(&xop->xo_data); + break; + } +} + +static void +xo_set_gettext_domain (xo_handle_t *xop, xo_field_info_t *xfip) +{ + const char *str = xfip->xfi_content; + unsigned len = xfip->xfi_clen; + const char *fmt = xfip->xfi_format; + unsigned flen = xfip->xfi_flen; + + /* Start by discarding previous domain */ + if (xop->xo_gt_domain) { + xo_free(xop->xo_gt_domain); + xop->xo_gt_domain = NULL; + } + + /* An empty {G:} means no domainname */ + if (len == 0 && flen == 0) + return; + + int start_offset = -1; + if (len == 0 && flen != 0) { + /* Need to do format the data to get the domainname from args */ + start_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp; + xo_do_format_field(xop, NULL, fmt, flen, 0); + + int end_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp; + len = end_offset - start_offset; + str = xop->xo_data.xb_bufp + start_offset; + } + + xop->xo_gt_domain = xo_strndup(str, len); + + /* Reset the current buffer point to avoid emitting the name as output */ + if (start_offset >= 0) + xop->xo_data.xb_curp = xop->xo_data.xb_bufp + start_offset; +} + +static void +xo_format_content (xo_handle_t *xop, const char *class_name, + const char *tag_name, + const char *str, int len, const char *fmt, int flen, + xo_xff_flags_t flags) +{ + switch (xo_style(xop)) { + case XO_STYLE_TEXT: + if (len) + xo_data_append_content(xop, str, len, flags); + else + xo_do_format_field(xop, NULL, fmt, flen, flags); + break; + + case XO_STYLE_HTML: + if (len == 0) { + str = fmt; + len = flen; + } + + xo_buf_append_div(xop, class_name, flags, NULL, 0, str, len, NULL, 0); + break; + + case XO_STYLE_XML: + case XO_STYLE_JSON: + case XO_STYLE_SDPARAMS: + if (tag_name) { + if (len == 0) { + str = fmt; + len = flen; + } + + xo_open_container_h(xop, tag_name); + xo_format_value(xop, "message", 7, str, len, NULL, 0, flags); + xo_close_container_h(xop, tag_name); + + } else { + /* + * Even though we don't care about labels, we need to do + * enough parsing work to skip over the right bits of xo_vap. + */ + if (len == 0) + xo_do_format_field(xop, NULL, fmt, flen, + flags | XFF_NO_OUTPUT); + } + break; + + case XO_STYLE_ENCODER: + if (len == 0) + xo_do_format_field(xop, NULL, fmt, flen, + flags | XFF_NO_OUTPUT); + break; + } +} + +static const char *xo_color_names[] = { + "default", /* XO_COL_DEFAULT */ + "black", /* XO_COL_BLACK */ + "red", /* XO_CLOR_RED */ + "green", /* XO_COL_GREEN */ + "yellow", /* XO_COL_YELLOW */ + "blue", /* XO_COL_BLUE */ + "magenta", /* XO_COL_MAGENTA */ + "cyan", /* XO_COL_CYAN */ + "white", /* XO_COL_WHITE */ + NULL +}; + +static int +xo_color_find (const char *str) +{ + int i; + + for (i = 0; xo_color_names[i]; i++) { + if (strcmp(xo_color_names[i], str) == 0) + return i; + } + + return -1; +} + +static const char *xo_effect_names[] = { + "reset", /* XO_EFF_RESET */ + "normal", /* XO_EFF_NORMAL */ + "bold", /* XO_EFF_BOLD */ + "underline", /* XO_EFF_UNDERLINE */ + "inverse", /* XO_EFF_INVERSE */ + NULL +}; + +static const char *xo_effect_on_codes[] = { + "0", /* XO_EFF_RESET */ + "0", /* XO_EFF_NORMAL */ + "1", /* XO_EFF_BOLD */ + "4", /* XO_EFF_UNDERLINE */ + "7", /* XO_EFF_INVERSE */ + NULL +}; + +#if 0 +/* + * See comment below re: joy of terminal standards. These can + * be use by just adding: + * + if (newp->xoc_effects & bit) + * code = xo_effect_on_codes[i]; + * + else + * + code = xo_effect_off_codes[i]; + * in xo_color_handle_text. + */ +static const char *xo_effect_off_codes[] = { + "0", /* XO_EFF_RESET */ + "0", /* XO_EFF_NORMAL */ + "21", /* XO_EFF_BOLD */ + "24", /* XO_EFF_UNDERLINE */ + "27", /* XO_EFF_INVERSE */ + NULL +}; +#endif /* 0 */ + +static int +xo_effect_find (const char *str) +{ + int i; + + for (i = 0; xo_effect_names[i]; i++) { + if (strcmp(xo_effect_names[i], str) == 0) + return i; + } + + return -1; +} + +static void +xo_colors_parse (xo_handle_t *xop, xo_colors_t *xocp, char *str) +{ +#ifdef LIBXO_TEXT_ONLY + return; +#endif /* LIBXO_TEXT_ONLY */ + + char *cp, *ep, *np, *xp; + int len = strlen(str); + int rc; + + /* + * Possible tokens: colors, bg-colors, effects, no-effects, "reset". + */ + for (cp = str, ep = cp + len - 1; cp && cp < ep; cp = np) { + /* Trim leading whitespace */ + while (isspace((int) *cp)) + cp += 1; + + np = strchr(cp, ','); + if (np) + *np++ = '\0'; + + /* Trim trailing whitespace */ + xp = cp + strlen(cp) - 1; + while (isspace(*xp) && xp > cp) + *xp-- = '\0'; + + if (cp[0] == 'f' && cp[1] == 'g' && cp[2] == '-') { + rc = xo_color_find(cp + 3); + if (rc < 0) + goto unknown; + + xocp->xoc_col_fg = rc; + + } else if (cp[0] == 'b' && cp[1] == 'g' && cp[2] == '-') { + rc = xo_color_find(cp + 3); + if (rc < 0) + goto unknown; + xocp->xoc_col_bg = rc; + + } else if (cp[0] == 'n' && cp[1] == 'o' && cp[2] == '-') { + rc = xo_effect_find(cp + 3); + if (rc < 0) + goto unknown; + xocp->xoc_effects &= ~(1 << rc); + + } else { + rc = xo_effect_find(cp); + if (rc < 0) + goto unknown; + xocp->xoc_effects |= 1 << rc; + + switch (1 << rc) { + case XO_EFF_RESET: + xocp->xoc_col_fg = xocp->xoc_col_bg = 0; + /* Note: not "|=" since we want to wipe out the old value */ + xocp->xoc_effects = XO_EFF_RESET; + break; + + case XO_EFF_NORMAL: + xocp->xoc_effects &= ~(XO_EFF_BOLD | XO_EFF_UNDERLINE + | XO_EFF_INVERSE | XO_EFF_NORMAL); + break; + } + } + continue; + + unknown: + if (XOF_ISSET(xop, XOF_WARN)) + xo_failure(xop, "unknown color/effect string detected: '%s'", cp); + } +} + +static inline int +xo_colors_enabled (xo_handle_t *xop UNUSED) +{ +#ifdef LIBXO_TEXT_ONLY + return 0; +#else /* LIBXO_TEXT_ONLY */ + return XOF_ISSET(xop, XOF_COLOR); +#endif /* LIBXO_TEXT_ONLY */ +} + +static void +xo_colors_handle_text (xo_handle_t *xop UNUSED, xo_colors_t *newp) +{ + char buf[BUFSIZ]; + char *cp = buf, *ep = buf + sizeof(buf); + unsigned i, bit; + xo_colors_t *oldp = &xop->xo_colors; + const char *code = NULL; + + /* + * Start the buffer with an escape. We don't want to add the '[' + * now, since we let xo_effect_text_add unconditionally add the ';'. + * We'll replace the first ';' with a '[' when we're done. + */ + *cp++ = 0x1b; /* Escape */ + + /* + * Terminals were designed back in the age before "certainty" was + * invented, when standards were more what you'd call "guidelines" + * than actual rules. Anyway we can't depend on them to operate + * correctly. So when display attributes are changed, we punt, + * reseting them all and turning back on the ones we want to keep. + * Longer, but should be completely reliable. Savvy? + */ + if (oldp->xoc_effects != (newp->xoc_effects & oldp->xoc_effects)) { + newp->xoc_effects |= XO_EFF_RESET; + oldp->xoc_effects = 0; + } + + for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) { + if ((newp->xoc_effects & bit) == (oldp->xoc_effects & bit)) + continue; + + code = xo_effect_on_codes[i]; + + cp += snprintf(cp, ep - cp, ";%s", code); + if (cp >= ep) + return; /* Should not occur */ + + if (bit == XO_EFF_RESET) { + /* Mark up the old value so we can detect current values as new */ + oldp->xoc_effects = 0; + oldp->xoc_col_fg = oldp->xoc_col_bg = XO_COL_DEFAULT; + } + } + + if (newp->xoc_col_fg != oldp->xoc_col_fg) { + cp += snprintf(cp, ep - cp, ";3%u", + (newp->xoc_col_fg != XO_COL_DEFAULT) + ? newp->xoc_col_fg - 1 : 9); + } + + if (newp->xoc_col_bg != oldp->xoc_col_bg) { + cp += snprintf(cp, ep - cp, ";4%u", + (newp->xoc_col_bg != XO_COL_DEFAULT) + ? newp->xoc_col_bg - 1 : 9); + } + + if (cp - buf != 1 && cp < ep - 3) { + buf[1] = '['; /* Overwrite leading ';' */ + *cp++ = 'm'; + *cp = '\0'; + xo_buf_append(&xop->xo_data, buf, cp - buf); + } +} + +static void +xo_colors_handle_html (xo_handle_t *xop, xo_colors_t *newp) +{ + xo_colors_t *oldp = &xop->xo_colors; + + /* + * HTML colors are mostly trivial: fill in xo_color_buf with + * a set of class tags representing the colors and effects. + */ + + /* If nothing changed, then do nothing */ + if (oldp->xoc_effects == newp->xoc_effects + && oldp->xoc_col_fg == newp->xoc_col_fg + && oldp->xoc_col_bg == newp->xoc_col_bg) + return; + + unsigned i, bit; + xo_buffer_t *xbp = &xop->xo_color_buf; + + xo_buf_reset(xbp); /* We rebuild content after each change */ + + for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) { + if (!(newp->xoc_effects & bit)) + continue; + + xo_buf_append_str(xbp, " effect-"); + xo_buf_append_str(xbp, xo_effect_names[i]); + } + + const char *fg = NULL; + const char *bg = NULL; + + if (newp->xoc_col_fg != XO_COL_DEFAULT) + fg = xo_color_names[newp->xoc_col_fg]; + if (newp->xoc_col_bg != XO_COL_DEFAULT) + bg = xo_color_names[newp->xoc_col_bg]; + + if (newp->xoc_effects & XO_EFF_INVERSE) { + const char *tmp = fg; + fg = bg; + bg = tmp; + if (fg == NULL) + fg = "inverse"; + if (bg == NULL) + bg = "inverse"; + + } + + if (fg) { + xo_buf_append_str(xbp, " color-fg-"); + xo_buf_append_str(xbp, fg); + } + + if (bg) { + xo_buf_append_str(xbp, " color-bg-"); + xo_buf_append_str(xbp, bg); + } +} + +static void +xo_format_colors (xo_handle_t *xop, xo_field_info_t *xfip) +{ + const char *str = xfip->xfi_content; + unsigned len = xfip->xfi_clen; + const char *fmt = xfip->xfi_format; + unsigned flen = xfip->xfi_flen; + + xo_buffer_t xb; + + /* If the string is static and we've in an encoding style, bail */ + if (len != 0 && xo_style_is_encoding(xop)) + return; + + xo_buf_init(&xb); + + if (len) + xo_buf_append(&xb, str, len); + else if (flen) + xo_do_format_field(xop, &xb, fmt, flen, 0); + else + xo_buf_append(&xb, "reset", 6); /* Default if empty */ + + if (xo_colors_enabled(xop)) { + switch (xo_style(xop)) { + case XO_STYLE_TEXT: + case XO_STYLE_HTML: + xo_buf_append(&xb, "", 1); + + xo_colors_t xoc = xop->xo_colors; + xo_colors_parse(xop, &xoc, xb.xb_bufp); + + if (xo_style(xop) == XO_STYLE_TEXT) { + /* + * Text mode means emitting the colors as ANSI character + * codes. This will allow people who like colors to have + * colors. The issue is, of course conflicting with the + * user's perfectly reasonable color scheme. Which leads + * to the hell of LSCOLORS, where even app need to have + * customization hooks for adjusting colors. Instead we + * provide a simpler-but-still-annoying answer where one + * can map colors to other colors. + */ + xo_colors_handle_text(xop, &xoc); + xoc.xoc_effects &= ~XO_EFF_RESET; /* After handling it */ + + } else { + /* + * HTML output is wrapped in divs, so the color information + * must appear in every div until cleared. Most pathetic. + * Most unavoidable. + */ + xoc.xoc_effects &= ~XO_EFF_RESET; /* Before handling effects */ + xo_colors_handle_html(xop, &xoc); + } + + xop->xo_colors = xoc; + break; + + case XO_STYLE_XML: + case XO_STYLE_JSON: + case XO_STYLE_SDPARAMS: + case XO_STYLE_ENCODER: + /* + * Nothing to do; we did all that work just to clear the stack of + * formatting arguments. + */ + break; + } + } + + xo_buf_cleanup(&xb); +} + +static void +xo_format_units (xo_handle_t *xop, xo_field_info_t *xfip) +{ + const char *str = xfip->xfi_content; + unsigned len = xfip->xfi_clen; + const char *fmt = xfip->xfi_format; + unsigned flen = xfip->xfi_flen; + xo_xff_flags_t flags = xfip->xfi_flags; + + static char units_start_xml[] = " units=\""; + static char units_start_html[] = " data-units=\""; + + if (!XOIF_ISSET(xop, XOIF_UNITS_PENDING)) { + xo_format_content(xop, "units", NULL, str, len, fmt, flen, flags); + return; + } + + xo_buffer_t *xbp = &xop->xo_data; + int start = xop->xo_units_offset; + int stop = xbp->xb_curp - xbp->xb_bufp; + + if (xo_style(xop) == XO_STYLE_XML) + xo_buf_append(xbp, units_start_xml, sizeof(units_start_xml) - 1); + else if (xo_style(xop) == XO_STYLE_HTML) + xo_buf_append(xbp, units_start_html, sizeof(units_start_html) - 1); + else + return; + + if (len) + xo_data_escape(xop, str, len); + else + xo_do_format_field(xop, NULL, fmt, flen, flags); + + xo_buf_append(xbp, "\"", 1); + + int now = xbp->xb_curp - xbp->xb_bufp; + int delta = now - stop; + if (delta <= 0) { /* Strange; no output to move */ + xbp->xb_curp = xbp->xb_bufp + stop; /* Reset buffer to prior state */ + return; + } + + /* + * Now we're in it alright. We've need to insert the unit value + * we just created into the right spot. We make a local copy, + * move it and then insert our copy. We know there's room in the + * buffer, since we're just moving this around. + */ + char *buf = alloca(delta); + + memcpy(buf, xbp->xb_bufp + stop, delta); + memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start); + memmove(xbp->xb_bufp + start, buf, delta); +} + +static int +xo_find_width (xo_handle_t *xop, xo_field_info_t *xfip) +{ + const char *str = xfip->xfi_content; + unsigned len = xfip->xfi_clen; + const char *fmt = xfip->xfi_format; + unsigned flen = xfip->xfi_flen; + + long width = 0; + char *bp; + char *cp; + + if (len) { + bp = alloca(len + 1); /* Make local NUL-terminated copy of str */ + memcpy(bp, str, len); + bp[len] = '\0'; + + width = strtol(bp, &cp, 0); + if (width == LONG_MIN || width == LONG_MAX + || bp == cp || *cp != '\0' ) { + width = 0; + xo_failure(xop, "invalid width for anchor: '%s'", bp); + } + } else if (flen) { + if (flen != 2 || strncmp("%d", fmt, flen) != 0) + xo_failure(xop, "invalid width format: '%*.*s'", flen, flen, fmt); + if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) + width = va_arg(xop->xo_vap, int); + } + + return width; +} + +static void +xo_anchor_clear (xo_handle_t *xop) +{ + XOIF_CLEAR(xop, XOIF_ANCHOR); + xop->xo_anchor_offset = 0; + xop->xo_anchor_columns = 0; + xop->xo_anchor_min_width = 0; +} + +/* + * An anchor is a marker used to delay field width implications. + * Imagine the format string "{[:10}{min:%d}/{cur:%d}/{max:%d}{:]}". + * We are looking for output like " 1/4/5" + * + * To make this work, we record the anchor and then return to + * format it when the end anchor tag is seen. + */ +static void +xo_anchor_start (xo_handle_t *xop, xo_field_info_t *xfip) +{ + if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML) + return; + + if (XOIF_ISSET(xop, XOIF_ANCHOR)) + xo_failure(xop, "the anchor already recording is discarded"); + + XOIF_SET(xop, XOIF_ANCHOR); + xo_buffer_t *xbp = &xop->xo_data; + xop->xo_anchor_offset = xbp->xb_curp - xbp->xb_bufp; + xop->xo_anchor_columns = 0; + + /* + * Now we find the width, if possible. If it's not there, + * we'll get it on the end anchor. + */ + xop->xo_anchor_min_width = xo_find_width(xop, xfip); +} + +static void +xo_anchor_stop (xo_handle_t *xop, xo_field_info_t *xfip) +{ + if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML) + return; + + if (!XOIF_ISSET(xop, XOIF_ANCHOR)) { + xo_failure(xop, "no start anchor"); + return; + } + + XOIF_CLEAR(xop, XOIF_UNITS_PENDING); + + int width = xo_find_width(xop, xfip); + if (width == 0) + width = xop->xo_anchor_min_width; + + if (width == 0) /* No width given; nothing to do */ + goto done; + + xo_buffer_t *xbp = &xop->xo_data; + int start = xop->xo_anchor_offset; + int stop = xbp->xb_curp - xbp->xb_bufp; + int abswidth = (width > 0) ? width : -width; + int blen = abswidth - xop->xo_anchor_columns; + + if (blen <= 0) /* Already over width */ + goto done; + + if (abswidth > XO_MAX_ANCHOR_WIDTH) { + xo_failure(xop, "width over %u are not supported", + XO_MAX_ANCHOR_WIDTH); + goto done; + } + + /* Make a suitable padding field and emit it */ + char *buf = alloca(blen); + memset(buf, ' ', blen); + xo_format_content(xop, "padding", NULL, buf, blen, NULL, 0, 0); + + if (width < 0) /* Already left justified */ + goto done; + + int now = xbp->xb_curp - xbp->xb_bufp; + int delta = now - stop; + if (delta <= 0) /* Strange; no output to move */ + goto done; + + /* + * Now we're in it alright. We've need to insert the padding data + * we just created (which might be an HTML
or text) before + * the formatted data. We make a local copy, move it and then + * insert our copy. We know there's room in the buffer, since + * we're just moving this around. + */ + if (delta > blen) + buf = alloca(delta); /* Expand buffer if needed */ + + memcpy(buf, xbp->xb_bufp + stop, delta); + memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start); + memmove(xbp->xb_bufp + start, buf, delta); + + done: + xo_anchor_clear(xop); +} + +static const char * +xo_class_name (int ftype) +{ + switch (ftype) { + case 'D': return "decoration"; + case 'E': return "error"; + case 'L': return "label"; + case 'N': return "note"; + case 'P': return "padding"; + case 'W': return "warning"; + } + + return NULL; +} + +static const char * +xo_tag_name (int ftype) +{ + switch (ftype) { + case 'E': return "__error"; + case 'W': return "__warning"; + } + + return NULL; +} + +static int +xo_role_wants_default_format (int ftype) +{ + switch (ftype) { + /* These roles can be completely empty and/or without formatting */ + case 'C': + case 'G': + case '[': + case ']': + return 0; + } + + return 1; +} + +static xo_mapping_t xo_role_names[] = { + { 'C', "color" }, + { 'D', "decoration" }, + { 'E', "error" }, + { 'L', "label" }, + { 'N', "note" }, + { 'P', "padding" }, + { 'T', "title" }, + { 'U', "units" }, + { 'V', "value" }, + { 'W', "warning" }, + { '[', "start-anchor" }, + { ']', "stop-anchor" }, + { 0, NULL } +}; + +#define XO_ROLE_EBRACE '{' /* Escaped braces */ +#define XO_ROLE_TEXT '+' +#define XO_ROLE_NEWLINE '\n' + +static xo_mapping_t xo_modifier_names[] = { + { XFF_COLON, "colon" }, + { XFF_COMMA, "comma" }, + { XFF_DISPLAY_ONLY, "display" }, + { XFF_ENCODE_ONLY, "encoding" }, + { XFF_GT_FIELD, "gettext" }, + { XFF_HUMANIZE, "humanize" }, + { XFF_HUMANIZE, "hn" }, + { XFF_HN_SPACE, "hn-space" }, + { XFF_HN_DECIMAL, "hn-decimal" }, + { XFF_HN_1000, "hn-1000" }, + { XFF_KEY, "key" }, + { XFF_LEAF_LIST, "leaf-list" }, + { XFF_LEAF_LIST, "list" }, + { XFF_NOQUOTE, "no-quotes" }, + { XFF_NOQUOTE, "no-quote" }, + { XFF_GT_PLURAL, "plural" }, + { XFF_QUOTE, "quotes" }, + { XFF_QUOTE, "quote" }, + { XFF_TRIM_WS, "trim" }, + { XFF_WS, "white" }, + { 0, NULL } +}; + +#ifdef NOT_NEEDED_YET +static xo_mapping_t xo_modifier_short_names[] = { + { XFF_COLON, "c" }, + { XFF_DISPLAY_ONLY, "d" }, + { XFF_ENCODE_ONLY, "e" }, + { XFF_GT_FIELD, "g" }, + { XFF_HUMANIZE, "h" }, + { XFF_KEY, "k" }, + { XFF_LEAF_LIST, "l" }, + { XFF_NOQUOTE, "n" }, + { XFF_GT_PLURAL, "p" }, + { XFF_QUOTE, "q" }, + { XFF_TRIM_WS, "t" }, + { XFF_WS, "w" }, + { 0, NULL } +}; +#endif /* NOT_NEEDED_YET */ + +static int +xo_count_fields (xo_handle_t *xop UNUSED, const char *fmt) +{ + int rc = 1; + const char *cp; + + for (cp = fmt; *cp; cp++) + if (*cp == '{' || *cp == '\n') + rc += 1; + + return rc * 2 + 1; +} + +/* + * The field format is: + * '{' modifiers ':' content [ '/' print-fmt [ '/' encode-fmt ]] '}' + * Roles are optional and include the following field types: + * 'D': decoration; something non-text and non-data (colons, commmas) + * 'E': error message + * 'G': gettext() the entire string; optional domainname as content + * 'L': label; text preceding data + * 'N': note; text following data + * 'P': padding; whitespace + * 'T': Title, where 'content' is a column title + * 'U': Units, where 'content' is the unit label + * 'V': value, where 'content' is the name of the field (the default) + * 'W': warning message + * '[': start a section of anchored text + * ']': end a section of anchored text + * The following modifiers are also supported: + * 'c': flag: emit a colon after the label + * 'd': field is only emitted for display styles (text and html) + * 'e': field is only emitted for encoding styles (xml and json) + * 'g': gettext() the field + * 'h': humanize a numeric value (only for display styles) + * 'k': this field is a key, suitable for XPath predicates + * 'l': a leaf-list, a simple list of values + * 'n': no quotes around this field + * 'p': the field has plural gettext semantics (ngettext) + * 'q': add quotes around this field + * 't': trim whitespace around the value + * 'w': emit a blank after the label + * The print-fmt and encode-fmt strings is the printf-style formating + * for this data. JSON and XML will use the encoding-fmt, if present. + * If the encode-fmt is not provided, it defaults to the print-fmt. + * If the print-fmt is not provided, it defaults to 's'. + */ +static const char * +xo_parse_roles (xo_handle_t *xop, const char *fmt, + const char *basep, xo_field_info_t *xfip) +{ + const char *sp; + unsigned ftype = 0; + xo_xff_flags_t flags = 0; + uint8_t fnum = 0; + + for (sp = basep; sp; sp++) { + if (*sp == ':' || *sp == '/' || *sp == '}') + break; + + if (*sp == '\\') { + if (sp[1] == '\0') { + xo_failure(xop, "backslash at the end of string"); + return NULL; + } + + /* Anything backslashed is ignored */ + sp += 1; + continue; + } + + if (*sp == ',') { + const char *np; + for (np = ++sp; *np; np++) + if (*np == ':' || *np == '/' || *np == '}' || *np == ',') + break; + + int slen = np - sp; + if (slen > 0) { + xo_xff_flags_t value; + + value = xo_name_lookup(xo_role_names, sp, slen); + if (value) + ftype = value; + else { + value = xo_name_lookup(xo_modifier_names, sp, slen); + if (value) + flags |= value; + else + xo_failure(xop, "unknown keyword ignored: '%.*s'", + slen, sp); + } + } + + sp = np - 1; + continue; + } + + switch (*sp) { + case 'C': + case 'D': + case 'E': + case 'G': + case 'L': + case 'N': + case 'P': + case 'T': + case 'U': + case 'V': + case 'W': + case '[': + case ']': + if (ftype != 0) { + xo_failure(xop, "field descriptor uses multiple types: '%s'", + xo_printable(fmt)); + return NULL; + } + ftype = *sp; + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + fnum = (fnum * 10) + (*sp - '0'); + break; + + case 'c': + flags |= XFF_COLON; + break; + + case 'd': + flags |= XFF_DISPLAY_ONLY; + break; + + case 'e': + flags |= XFF_ENCODE_ONLY; + break; + + case 'g': + flags |= XFF_GT_FIELD; + break; + + case 'h': + flags |= XFF_HUMANIZE; + break; + + case 'k': + flags |= XFF_KEY; + break; + + case 'l': + flags |= XFF_LEAF_LIST; + break; + + case 'n': + flags |= XFF_NOQUOTE; + break; + + case 'p': + flags |= XFF_GT_PLURAL; + break; + + case 'q': + flags |= XFF_QUOTE; + break; + + case 't': + flags |= XFF_TRIM_WS; + break; + + case 'w': + flags |= XFF_WS; + break; + + default: + xo_failure(xop, "field descriptor uses unknown modifier: '%s'", + xo_printable(fmt)); + /* + * No good answer here; a bad format will likely + * mean a core file. We just return and hope + * the caller notices there's no output, and while + * that seems, well, bad, there's nothing better. + */ + return NULL; + } + + if (ftype == 'N' || ftype == 'U') { + if (flags & XFF_COLON) { + xo_failure(xop, "colon modifier on 'N' or 'U' field ignored: " + "'%s'", xo_printable(fmt)); + flags &= ~XFF_COLON; + } + } + } + + xfip->xfi_flags = flags; + xfip->xfi_ftype = ftype ?: 'V'; + xfip->xfi_fnum = fnum; + + return sp; +} + +/* + * Number any remaining fields that need numbers. Note that some + * field types (text, newline, escaped braces) never get numbers. + */ +static void +xo_gettext_finish_numbering_fields (xo_handle_t *xop UNUSED, + const char *fmt UNUSED, + xo_field_info_t *fields) +{ + xo_field_info_t *xfip; + unsigned fnum, max_fields; + uint64_t bits = 0; + + /* First make a list of add the explicitly used bits */ + for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) { + switch (xfip->xfi_ftype) { + case XO_ROLE_NEWLINE: /* Don't get numbered */ + case XO_ROLE_TEXT: + case XO_ROLE_EBRACE: + case 'G': + continue; + } + + fnum += 1; + if (fnum >= 63) + break; + + if (xfip->xfi_fnum) + bits |= 1 << xfip->xfi_fnum; + } + + max_fields = fnum; + + for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) { + switch (xfip->xfi_ftype) { + case XO_ROLE_NEWLINE: /* Don't get numbered */ + case XO_ROLE_TEXT: + case XO_ROLE_EBRACE: + case 'G': + continue; + } + + if (xfip->xfi_fnum != 0) + continue; + + /* Find the next unassigned field */ + for (fnum++; bits & (1 << fnum); fnum++) + continue; + + if (fnum > max_fields) + break; + + xfip->xfi_fnum = fnum; /* Mark the field number */ + bits |= 1 << fnum; /* Mark it used */ + } +} + +/* + * The format string uses field numbers, so we need to whiffle thru it + * and make sure everything's sane and lovely. + */ +static int +xo_parse_field_numbers (xo_handle_t *xop, const char *fmt, + xo_field_info_t *fields, unsigned num_fields) +{ + xo_field_info_t *xfip; + unsigned field, fnum; + uint64_t bits = 0; + + for (xfip = fields, field = 0; field < num_fields; xfip++, field++) { + /* Fields default to 1:1 with natural position */ + if (xfip->xfi_fnum == 0) + xfip->xfi_fnum = field + 1; + else if (xfip->xfi_fnum > num_fields) { + xo_failure(xop, "field number exceeds number of fields: '%s'", fmt); + return -1; + } + + fnum = xfip->xfi_fnum - 1; /* Move to zero origin */ + if (fnum < 64) { /* Only test what fits */ + if (bits & (1 << fnum)) { + xo_failure(xop, "field number %u reused: '%s'", + xfip->xfi_fnum, fmt); + return -1; + } + bits |= 1 << fnum; + } + } + + return 0; +} + +static int +xo_parse_fields (xo_handle_t *xop, xo_field_info_t *fields, + unsigned num_fields, const char *fmt) +{ + static const char default_format[] = "%s"; + const char *cp, *sp, *ep, *basep; + unsigned field = 0; + xo_field_info_t *xfip = fields; + unsigned seen_fnum = 0; + + for (cp = fmt; *cp && field < num_fields; field++, xfip++) { + xfip->xfi_start = cp; + + if (*cp == '\n') { + xfip->xfi_ftype = XO_ROLE_NEWLINE; + xfip->xfi_len = 1; + cp += 1; + continue; + } + + if (*cp != '{') { + /* Normal text */ + for (sp = cp; *sp; sp++) { + if (*sp == '{' || *sp == '\n') + break; + } + + xfip->xfi_ftype = XO_ROLE_TEXT; + xfip->xfi_content = cp; + xfip->xfi_clen = sp - cp; + xfip->xfi_next = sp; + + cp = sp; + continue; + } + + if (cp[1] == '{') { /* Start of {{escaped braces}} */ + xfip->xfi_start = cp + 1; /* Start at second brace */ + xfip->xfi_ftype = XO_ROLE_EBRACE; + + cp += 2; /* Skip over _both_ characters */ + for (sp = cp; *sp; sp++) { + if (*sp == '}' && sp[1] == '}') + break; + } + if (*sp == '\0') { + xo_failure(xop, "missing closing '}}': '%s'", + xo_printable(fmt)); + return -1; + } + + xfip->xfi_len = sp - xfip->xfi_start + 1; + + /* Move along the string, but don't run off the end */ + if (*sp == '}' && sp[1] == '}') + sp += 2; + cp = *sp ? sp : sp; + xfip->xfi_next = cp; + continue; + } + + /* We are looking at the start of a field definition */ + xfip->xfi_start = basep = cp + 1; + + const char *format = NULL; + int flen = 0; + + /* Looking at roles and modifiers */ + sp = xo_parse_roles(xop, fmt, basep, xfip); + if (sp == NULL) { + /* xo_failure has already been called */ + return -1; + } + + if (xfip->xfi_fnum) + seen_fnum = 1; + + /* Looking at content */ + if (*sp == ':') { + for (ep = ++sp; *sp; sp++) { + if (*sp == '}' || *sp == '/') + break; + if (*sp == '\\') { + if (sp[1] == '\0') { + xo_failure(xop, "backslash at the end of string"); + return -1; + } + sp += 1; + continue; + } + } + if (ep != sp) { + xfip->xfi_clen = sp - ep; + xfip->xfi_content = ep; + } + } else { + xo_failure(xop, "missing content (':'): '%s'", xo_printable(fmt)); + return -1; + } + + /* Looking at main (display) format */ + if (*sp == '/') { + for (ep = ++sp; *sp; sp++) { + if (*sp == '}' || *sp == '/') + break; + if (*sp == '\\') { + if (sp[1] == '\0') { + xo_failure(xop, "backslash at the end of string"); + return -1; + } + sp += 1; + continue; + } + } + flen = sp - ep; + format = ep; + } + + /* Looking at encoding format */ + if (*sp == '/') { + for (ep = ++sp; *sp; sp++) { + if (*sp == '}') + break; + } + + xfip->xfi_encoding = ep; + xfip->xfi_elen = sp - ep; + } + + if (*sp != '}') { + xo_failure(xop, "missing closing '}': %s", xo_printable(fmt)); + return -1; + } + + xfip->xfi_len = sp - xfip->xfi_start; + xfip->xfi_next = ++sp; + + /* If we have content, then we have a default format */ + if (xfip->xfi_clen || format) { + if (format) { + xfip->xfi_format = format; + xfip->xfi_flen = flen; + } else if (xo_role_wants_default_format(xfip->xfi_ftype)) { + xfip->xfi_format = default_format; + xfip->xfi_flen = 2; + } + } + + cp = sp; + } + + int rc = 0; + + /* + * If we saw a field number on at least one field, then we need + * to enforce some rules and/or guidelines. + */ + if (seen_fnum) + rc = xo_parse_field_numbers(xop, fmt, fields, field); + + return rc; +} + +/* + * We are passed a pointer to a format string just past the "{G:}" + * field. We build a simplified version of the format string. + */ +static int +xo_gettext_simplify_format (xo_handle_t *xop UNUSED, + xo_buffer_t *xbp, + xo_field_info_t *fields, + int this_field, + const char *fmt UNUSED, + xo_simplify_field_func_t field_cb) +{ + unsigned ftype; + xo_xff_flags_t flags; + int field = this_field + 1; + xo_field_info_t *xfip; + char ch; + + for (xfip = &fields[field]; xfip->xfi_ftype; xfip++, field++) { + ftype = xfip->xfi_ftype; + flags = xfip->xfi_flags; + + if ((flags & XFF_GT_FIELD) && xfip->xfi_content && ftype != 'V') { + if (field_cb) + field_cb(xfip->xfi_content, xfip->xfi_clen, + (flags & XFF_GT_PLURAL) ? 1 : 0); + } + + switch (ftype) { + case 'G': + /* Ignore gettext roles */ + break; + + case XO_ROLE_NEWLINE: + xo_buf_append(xbp, "\n", 1); + break; + + case XO_ROLE_EBRACE: + xo_buf_append(xbp, "{", 1); + xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); + xo_buf_append(xbp, "}", 1); + break; + + case XO_ROLE_TEXT: + xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); + break; + + default: + xo_buf_append(xbp, "{", 1); + if (ftype != 'V') { + ch = ftype; + xo_buf_append(xbp, &ch, 1); + } + + unsigned fnum = xfip->xfi_fnum ?: 0; + if (fnum) { + char num[12]; + /* Field numbers are origin 1, not 0, following printf(3) */ + snprintf(num, sizeof(num), "%u", fnum); + xo_buf_append(xbp, num, strlen(num)); + } + + xo_buf_append(xbp, ":", 1); + xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); + xo_buf_append(xbp, "}", 1); + } + } + + xo_buf_append(xbp, "", 1); + return 0; +} + +void +xo_dump_fields (xo_field_info_t *); /* Fake prototype for debug function */ +void +xo_dump_fields (xo_field_info_t *fields) +{ + xo_field_info_t *xfip; + + for (xfip = fields; xfip->xfi_ftype; xfip++) { + printf("%lu(%u): %lx [%c/%u] [%.*s] [%.*s] [%.*s]\n", + (unsigned long) (xfip - fields), xfip->xfi_fnum, + (unsigned long) xfip->xfi_flags, + isprint((int) xfip->xfi_ftype) ? xfip->xfi_ftype : ' ', + xfip->xfi_ftype, + xfip->xfi_clen, xfip->xfi_content ?: "", + xfip->xfi_flen, xfip->xfi_format ?: "", + xfip->xfi_elen, xfip->xfi_encoding ?: ""); + } +} + +#ifdef HAVE_GETTEXT +/* + * Find the field that matches the given field number + */ +static xo_field_info_t * +xo_gettext_find_field (xo_field_info_t *fields, unsigned fnum) +{ + xo_field_info_t *xfip; + + for (xfip = fields; xfip->xfi_ftype; xfip++) + if (xfip->xfi_fnum == fnum) + return xfip; + + return NULL; +} + +/* + * At this point, we need to consider if the fields have been reordered, + * such as "The {:adjective} {:noun}" to "La {:noun} {:adjective}". + * + * We need to rewrite the new_fields using the old fields order, + * so that we can render the message using the arguments as they + * appear on the stack. It's a lot of work, but we don't really + * want to (eventually) fall into the standard printf code which + * means using the arguments straight (and in order) from the + * varargs we were originally passed. + */ +static void +xo_gettext_rewrite_fields (xo_handle_t *xop UNUSED, + xo_field_info_t *fields, unsigned max_fields) +{ + xo_field_info_t tmp[max_fields]; + bzero(tmp, max_fields * sizeof(tmp[0])); + + unsigned fnum = 0; + xo_field_info_t *newp, *outp, *zp; + for (newp = fields, outp = tmp; newp->xfi_ftype; newp++, outp++) { + switch (newp->xfi_ftype) { + case XO_ROLE_NEWLINE: /* Don't get numbered */ + case XO_ROLE_TEXT: + case XO_ROLE_EBRACE: + case 'G': + *outp = *newp; + outp->xfi_renum = 0; + continue; + } + + zp = xo_gettext_find_field(fields, ++fnum); + if (zp == NULL) { /* Should not occur */ + *outp = *newp; + outp->xfi_renum = 0; + continue; + } + + *outp = *zp; + outp->xfi_renum = newp->xfi_fnum; + } + + memcpy(fields, tmp, max_fields * sizeof(tmp[0])); +} + +/* + * We've got two lists of fields, the old list from the original + * format string and the new one from the parsed gettext reply. The + * new list has the localized words, where the old list has the + * formatting information. We need to combine them into a single list + * (the new list). + * + * If the list needs to be reordered, then we've got more serious work + * to do. + */ +static int +xo_gettext_combine_formats (xo_handle_t *xop, const char *fmt UNUSED, + const char *gtfmt, xo_field_info_t *old_fields, + xo_field_info_t *new_fields, unsigned new_max_fields, + int *reorderedp) +{ + int reordered = 0; + xo_field_info_t *newp, *oldp, *startp = old_fields; + + xo_gettext_finish_numbering_fields(xop, fmt, old_fields); + + for (newp = new_fields; newp->xfi_ftype; newp++) { + switch (newp->xfi_ftype) { + case XO_ROLE_NEWLINE: + case XO_ROLE_TEXT: + case XO_ROLE_EBRACE: + continue; + + case 'V': + for (oldp = startp; oldp->xfi_ftype; oldp++) { + if (oldp->xfi_ftype != 'V') + continue; + if (newp->xfi_clen != oldp->xfi_clen + || strncmp(newp->xfi_content, oldp->xfi_content, + oldp->xfi_clen) != 0) { + reordered = 1; + continue; + } + startp = oldp + 1; + break; + } + + /* Didn't find it on the first pass (starting from start) */ + if (oldp->xfi_ftype == 0) { + for (oldp = old_fields; oldp < startp; oldp++) { + if (oldp->xfi_ftype != 'V') + continue; + if (newp->xfi_clen != oldp->xfi_clen) + continue; + if (strncmp(newp->xfi_content, oldp->xfi_content, + oldp->xfi_clen) != 0) + continue; + reordered = 1; + break; + } + if (oldp == startp) { + /* Field not found */ + xo_failure(xop, "post-gettext format can't find field " + "'%.*s' in format '%s'", + newp->xfi_clen, newp->xfi_content, + xo_printable(gtfmt)); + return -1; + } + } + break; + + default: + /* + * Other fields don't have names for us to use, so if + * the types aren't the same, then we'll have to assume + * the original field is a match. + */ + for (oldp = startp; oldp->xfi_ftype; oldp++) { + if (oldp->xfi_ftype == 'V') /* Can't go past these */ + break; + if (oldp->xfi_ftype == newp->xfi_ftype) + goto copy_it; /* Assumably we have a match */ + } + continue; + } + + /* + * Found a match; copy over appropriate fields + */ + copy_it: + newp->xfi_flags = oldp->xfi_flags; + newp->xfi_fnum = oldp->xfi_fnum; + newp->xfi_format = oldp->xfi_format; + newp->xfi_flen = oldp->xfi_flen; + newp->xfi_encoding = oldp->xfi_encoding; + newp->xfi_elen = oldp->xfi_elen; + } + + *reorderedp = reordered; + if (reordered) { + xo_gettext_finish_numbering_fields(xop, fmt, new_fields); + xo_gettext_rewrite_fields(xop, new_fields, new_max_fields); + } + + return 0; +} + +/* + * We don't want to make gettext() calls here with a complete format + * string, since that means changing a flag would mean a + * labor-intensive re-translation expense. Instead we build a + * simplified form with a reduced level of detail, perform a lookup on + * that string and then re-insert the formating info. + * + * So something like: + * xo_emit("{G:}close {:fd/%ld} returned {g:error/%m} {:test/%6.6s}\n", ...) + * would have a lookup string of: + * "close {:fd} returned {:error} {:test}\n" + * + * We also need to handling reordering of fields, where the gettext() + * reply string uses fields in a different order than the original + * format string: + * "cluse-a {:fd} retoorned {:test}. Bork {:error} Bork. Bork.\n" + * If we have to reorder fields within the message, then things get + * complicated. See xo_gettext_rewrite_fields. + * + * Summary: i18n aighn't cheap. + */ +static const char * +xo_gettext_build_format (xo_handle_t *xop UNUSED, + xo_field_info_t *fields UNUSED, + int this_field UNUSED, + const char *fmt, char **new_fmtp) +{ + if (xo_style_is_encoding(xop)) + goto bail; + + xo_buffer_t xb; + xo_buf_init(&xb); + + if (xo_gettext_simplify_format(xop, &xb, fields, + this_field, fmt, NULL)) + goto bail2; + + const char *gtfmt = xo_dgettext(xop, xb.xb_bufp); + if (gtfmt == NULL || gtfmt == fmt || strcmp(gtfmt, fmt) == 0) + goto bail2; + + xo_buf_cleanup(&xb); + + char *new_fmt = xo_strndup(gtfmt, -1); + if (new_fmt == NULL) + goto bail2; + + *new_fmtp = new_fmt; + return new_fmt; + + bail2: + xo_buf_cleanup(&xb); + bail: + *new_fmtp = NULL; + return fmt; +} + +static void +xo_gettext_rebuild_content (xo_handle_t *xop, xo_field_info_t *fields, + unsigned *fstart, unsigned min_fstart, + unsigned *fend, unsigned max_fend) +{ + xo_field_info_t *xfip; + char *buf; + unsigned base = fstart[min_fstart]; + unsigned blen = fend[max_fend] - base; + xo_buffer_t *xbp = &xop->xo_data; + + if (blen == 0) + return; + + buf = xo_realloc(NULL, blen); + if (buf == NULL) + return; + + memcpy(buf, xbp->xb_bufp + fstart[min_fstart], blen); /* Copy our data */ + + unsigned field = min_fstart, soff, doff = base, len, fnum; + xo_field_info_t *zp; + + /* + * Be aware there are two competing views of "field number": we + * want the user to thing in terms of "The {1:size}" where {G:}, + * newlines, escaped braces, and text don't have numbers. But is + * also the internal view, where we have an array of + * xo_field_info_t and every field have an index. fnum, fstart[] + * and fend[] are the latter, but xfi_renum is the former. + */ + for (xfip = fields + field; xfip->xfi_ftype; xfip++, field++) { + fnum = field; + if (xfip->xfi_renum) { + zp = xo_gettext_find_field(fields, xfip->xfi_renum); + fnum = zp ? zp - fields : field; + } + + soff = fstart[fnum]; + len = fend[fnum] - soff; + + if (len > 0) { + soff -= base; + memcpy(xbp->xb_bufp + doff, buf + soff, len); + doff += len; + } + } + + xo_free(buf); +} +#else /* HAVE_GETTEXT */ +static const char * +xo_gettext_build_format (xo_handle_t *xop UNUSED, + xo_field_info_t *fields UNUSED, + int this_field UNUSED, + const char *fmt UNUSED, char **new_fmtp) +{ + *new_fmtp = NULL; + return fmt; +} + +static int +xo_gettext_combine_formats (xo_handle_t *xop UNUSED, const char *fmt UNUSED, + const char *gtfmt UNUSED, + xo_field_info_t *old_fields UNUSED, + xo_field_info_t *new_fields UNUSED, + unsigned new_max_fields UNUSED, + int *reorderedp UNUSED) +{ + return -1; +} + +static void +xo_gettext_rebuild_content (xo_handle_t *xop UNUSED, + xo_field_info_t *fields UNUSED, + unsigned *fstart UNUSED, unsigned min_fstart UNUSED, + unsigned *fend UNUSED, unsigned max_fend UNUSED) +{ + return; +} +#endif /* HAVE_GETTEXT */ + +/* + * The central function for emitting libxo output. + */ +static int +xo_do_emit (xo_handle_t *xop, const char *fmt) +{ + int gettext_inuse = 0; + int gettext_changed = 0; + int gettext_reordered = 0; + xo_field_info_t *new_fields = NULL; + + int rc = 0; + int flush = XOF_ISSET(xop, XOF_FLUSH); + int flush_line = XOF_ISSET(xop, XOF_FLUSH_LINE); + char *new_fmt = NULL; + + if (XOIF_ISSET(xop, XOIF_REORDER) || xo_style(xop) == XO_STYLE_ENCODER) + flush_line = 0; + + xop->xo_columns = 0; /* Always reset it */ + xop->xo_errno = errno; /* Save for "%m" */ + + unsigned max_fields = xo_count_fields(xop, fmt), field; + xo_field_info_t fields[max_fields], *xfip; + + bzero(fields, max_fields * sizeof(fields[0])); + + if (xo_parse_fields(xop, fields, max_fields, fmt)) + return -1; /* Warning already displayed */ + + unsigned ftype; + xo_xff_flags_t flags; + + /* + * Some overhead for gettext; if the fields in the msgstr returned + * by gettext are reordered, then we need to record start and end + * for each field. We'll go ahead and render the fields in the + * normal order, but later we can then reconstruct the reordered + * fields using these fstart/fend values. + */ + unsigned flimit = max_fields * 2; /* Pessimistic limit */ + unsigned min_fstart = flimit - 1; + unsigned max_fend = 0; /* Highest recorded fend[] entry */ + unsigned fstart[flimit]; + bzero(fstart, flimit * sizeof(fstart[0])); + unsigned fend[flimit]; + bzero(fend, flimit * sizeof(fend[0])); + + for (xfip = fields, field = 0; xfip->xfi_ftype && field < max_fields; + xfip++, field++) { + ftype = xfip->xfi_ftype; + flags = xfip->xfi_flags; + + /* Record field start offset */ + if (gettext_reordered) { + fstart[field] = xo_buf_offset(&xop->xo_data); + if (min_fstart > field) + min_fstart = field; + } + + if (ftype == XO_ROLE_NEWLINE) { + xo_line_close(xop); + if (flush_line && xo_flush_h(xop) < 0) + return -1; + goto bottom; + + } else if (ftype == XO_ROLE_EBRACE) { + xo_format_text(xop, xfip->xfi_start, xfip->xfi_len); + goto bottom; + + } else if (ftype == XO_ROLE_TEXT) { + /* Normal text */ + xo_format_text(xop, xfip->xfi_content, xfip->xfi_clen); + goto bottom; + } + + /* + * Notes and units need the 'w' flag handled before the content. + */ + if (ftype == 'N' || ftype == 'U') { + if (flags & XFF_WS) { + xo_format_content(xop, "padding", NULL, " ", 1, + NULL, 0, flags); + flags &= ~XFF_WS; /* Block later handling of this */ + } + } + + if (ftype == 'V') + xo_format_value(xop, xfip->xfi_content, xfip->xfi_clen, + xfip->xfi_format, xfip->xfi_flen, + xfip->xfi_encoding, xfip->xfi_elen, flags); + else if (ftype == '[') + xo_anchor_start(xop, xfip); + else if (ftype == ']') + xo_anchor_stop(xop, xfip); + else if (ftype == 'C') + xo_format_colors(xop, xfip); + + else if (ftype == 'G') { + /* + * A {G:domain} field; disect the domain name and translate + * the remaining portion of the input string. If the user + * didn't put the {G:} at the start of the format string, then + * assumably they just want us to translate the rest of it. + * Since gettext returns strings in a static buffer, we make + * a copy in new_fmt. + */ + xo_set_gettext_domain(xop, xfip); + + if (!gettext_inuse) { /* Only translate once */ + gettext_inuse = 1; + if (new_fmt) { + xo_free(new_fmt); + new_fmt = NULL; + } + + xo_gettext_build_format(xop, fields, field, + xfip->xfi_next, &new_fmt); + if (new_fmt) { + gettext_changed = 1; + + unsigned new_max_fields = xo_count_fields(xop, new_fmt); + + if (++new_max_fields < max_fields) + new_max_fields = max_fields; + + /* Leave a blank slot at the beginning */ + int sz = (new_max_fields + 1) * sizeof(xo_field_info_t); + new_fields = alloca(sz); + bzero(new_fields, sz); + + if (!xo_parse_fields(xop, new_fields + 1, + new_max_fields, new_fmt)) { + gettext_reordered = 0; + + if (!xo_gettext_combine_formats(xop, fmt, new_fmt, + fields, new_fields + 1, + new_max_fields, &gettext_reordered)) { + + if (gettext_reordered) { + if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) + xo_failure(xop, "gettext finds reordered " + "fields in '%s' and '%s'", + xo_printable(fmt), + xo_printable(new_fmt)); + flush_line = 0; /* Must keep at content */ + XOIF_SET(xop, XOIF_REORDER); + } + + field = -1; /* Will be incremented at top of loop */ + xfip = new_fields; + max_fields = new_max_fields; + } + } + } + } + continue; + + } else if (xfip->xfi_clen || xfip->xfi_format) { + + const char *class_name = xo_class_name(ftype); + if (class_name) + xo_format_content(xop, class_name, xo_tag_name(ftype), + xfip->xfi_content, xfip->xfi_clen, + xfip->xfi_format, xfip->xfi_flen, flags); + else if (ftype == 'T') + xo_format_title(xop, xfip); + else if (ftype == 'U') + xo_format_units(xop, xfip); + else + xo_failure(xop, "unknown field type: '%c'", ftype); + } + + if (flags & XFF_COLON) + xo_format_content(xop, "decoration", NULL, ":", 1, NULL, 0, 0); + + if (flags & XFF_WS) + xo_format_content(xop, "padding", NULL, " ", 1, NULL, 0, 0); + + bottom: + /* Record the end-of-field offset */ + if (gettext_reordered) { + fend[field] = xo_buf_offset(&xop->xo_data); + max_fend = field; + } + } + + if (gettext_changed && gettext_reordered) { + /* Final step: rebuild the content using the rendered fields */ + xo_gettext_rebuild_content(xop, new_fields + 1, fstart, min_fstart, + fend, max_fend); + } + + XOIF_CLEAR(xop, XOIF_REORDER); + + /* If we don't have an anchor, write the text out */ + if (flush && !XOIF_ISSET(xop, XOIF_ANCHOR)) { + if (xo_write(xop) < 0) + rc = -1; /* Report failure */ + else if (xop->xo_flush && xop->xo_flush(xop->xo_opaque) < 0) + rc = -1; + } + + if (new_fmt) + xo_free(new_fmt); + + /* + * We've carried the gettext domainname inside our handle just for + * convenience, but we need to ensure it doesn't survive across + * xo_emit calls. + */ + if (xop->xo_gt_domain) { + xo_free(xop->xo_gt_domain); + xop->xo_gt_domain = NULL; + } + + return (rc < 0) ? rc : (int) xop->xo_columns; +} + +/* + * Rebuild a format string in a gettext-friendly format. This function + * is exposed to tools can perform this function. See xo(1). + */ +char * +xo_simplify_format (xo_handle_t *xop, const char *fmt, int with_numbers, + xo_simplify_field_func_t field_cb) +{ + xop = xo_default(xop); + + xop->xo_columns = 0; /* Always reset it */ + xop->xo_errno = errno; /* Save for "%m" */ + + unsigned max_fields = xo_count_fields(xop, fmt); + xo_field_info_t fields[max_fields]; + + bzero(fields, max_fields * sizeof(fields[0])); + + if (xo_parse_fields(xop, fields, max_fields, fmt)) + return NULL; /* Warning already displayed */ + + xo_buffer_t xb; + xo_buf_init(&xb); + + if (with_numbers) + xo_gettext_finish_numbering_fields(xop, fmt, fields); + + if (xo_gettext_simplify_format(xop, &xb, fields, -1, fmt, field_cb)) + return NULL; + + return xb.xb_bufp; +} + +int +xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap) +{ + int rc; + + xop = xo_default(xop); + va_copy(xop->xo_vap, vap); + rc = xo_do_emit(xop, fmt); + va_end(xop->xo_vap); + bzero(&xop->xo_vap, sizeof(xop->xo_vap)); + + return rc; +} + +int +xo_emit_h (xo_handle_t *xop, const char *fmt, ...) +{ + int rc; + + xop = xo_default(xop); + va_start(xop->xo_vap, fmt); + rc = xo_do_emit(xop, fmt); + va_end(xop->xo_vap); + bzero(&xop->xo_vap, sizeof(xop->xo_vap)); + + return rc; +} + +int +xo_emit (const char *fmt, ...) +{ + xo_handle_t *xop = xo_default(NULL); + int rc; + + va_start(xop->xo_vap, fmt); + rc = xo_do_emit(xop, fmt); + va_end(xop->xo_vap); + bzero(&xop->xo_vap, sizeof(xop->xo_vap)); + + return rc; +} + +int +xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap) +{ + const int extra = 5; /* space, equals, quote, quote, and nul */ + xop = xo_default(xop); + + int rc = 0; + int nlen = strlen(name); + xo_buffer_t *xbp = &xop->xo_attrs; + unsigned name_offset, value_offset; + + switch (xo_style(xop)) { + case XO_STYLE_XML: + if (!xo_buf_has_room(xbp, nlen + extra)) + return -1; + + *xbp->xb_curp++ = ' '; + memcpy(xbp->xb_curp, name, nlen); + xbp->xb_curp += nlen; + *xbp->xb_curp++ = '='; + *xbp->xb_curp++ = '"'; + + rc = xo_vsnprintf(xop, xbp, fmt, vap); + + if (rc >= 0) { + rc = xo_escape_xml(xbp, rc, 1); + xbp->xb_curp += rc; + } + + if (!xo_buf_has_room(xbp, 2)) + return -1; + + *xbp->xb_curp++ = '"'; + *xbp->xb_curp = '\0'; + + rc += nlen + extra; + break; + + case XO_STYLE_ENCODER: + name_offset = xo_buf_offset(xbp); + xo_buf_append(xbp, name, nlen); + xo_buf_append(xbp, "", 1); + + value_offset = xo_buf_offset(xbp); + rc = xo_vsnprintf(xop, xbp, fmt, vap); + if (rc >= 0) { + xbp->xb_curp += rc; + *xbp->xb_curp = '\0'; + rc = xo_encoder_handle(xop, XO_OP_ATTRIBUTE, + xo_buf_data(xbp, name_offset), + xo_buf_data(xbp, value_offset)); + } + } + + return rc; +} + +int +xo_attr_h (xo_handle_t *xop, const char *name, const char *fmt, ...) +{ + int rc; + va_list vap; + + va_start(vap, fmt); + rc = xo_attr_hv(xop, name, fmt, vap); + va_end(vap); + + return rc; +} + +int +xo_attr (const char *name, const char *fmt, ...) +{ + int rc; + va_list vap; + + va_start(vap, fmt); + rc = xo_attr_hv(NULL, name, fmt, vap); + va_end(vap); + + return rc; +} + +static void +xo_stack_set_flags (xo_handle_t *xop) +{ + if (XOF_ISSET(xop, XOF_NOT_FIRST)) { + xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; + + xsp->xs_flags |= XSF_NOT_FIRST; + XOF_CLEAR(xop, XOF_NOT_FIRST); + } +} + +static void +xo_depth_change (xo_handle_t *xop, const char *name, + int delta, int indent, xo_state_t state, xo_xsf_flags_t flags) +{ + if (xo_style(xop) == XO_STYLE_HTML || xo_style(xop) == XO_STYLE_TEXT) + indent = 0; + + if (XOF_ISSET(xop, XOF_DTRT)) + flags |= XSF_DTRT; + + if (delta >= 0) { /* Push operation */ + if (xo_depth_check(xop, xop->xo_depth + delta)) + return; + + xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth + delta]; + xsp->xs_flags = flags; + xsp->xs_state = state; + xo_stack_set_flags(xop); + + if (name == NULL) + name = XO_FAILURE_NAME; + + xsp->xs_name = xo_strndup(name, -1); + + } else { /* Pop operation */ + if (xop->xo_depth == 0) { + if (!XOF_ISSET(xop, XOF_IGNORE_CLOSE)) + xo_failure(xop, "close with empty stack: '%s'", name); + return; + } + + xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; + if (XOF_ISSET(xop, XOF_WARN)) { + const char *top = xsp->xs_name; + if (top && strcmp(name, top) != 0) { + xo_failure(xop, "incorrect close: '%s' .vs. '%s'", + name, top); + return; + } + if ((xsp->xs_flags & XSF_LIST) != (flags & XSF_LIST)) { + xo_failure(xop, "list close on list confict: '%s'", + name); + return; + } + if ((xsp->xs_flags & XSF_INSTANCE) != (flags & XSF_INSTANCE)) { + xo_failure(xop, "list close on instance confict: '%s'", + name); + return; + } + } + + if (xsp->xs_name) { + xo_free(xsp->xs_name); + xsp->xs_name = NULL; + } + if (xsp->xs_keys) { + xo_free(xsp->xs_keys); + xsp->xs_keys = NULL; + } + } + + xop->xo_depth += delta; /* Record new depth */ + xop->xo_indent += indent; +} + +void +xo_set_depth (xo_handle_t *xop, int depth) +{ + xop = xo_default(xop); + + if (xo_depth_check(xop, depth)) + return; + + xop->xo_depth += depth; + xop->xo_indent += depth; +} + +static xo_xsf_flags_t +xo_stack_flags (unsigned xflags) +{ + if (xflags & XOF_DTRT) + return XSF_DTRT; + return 0; +} + +static void +xo_emit_top (xo_handle_t *xop, const char *ppn) +{ + xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn); + XOIF_SET(xop, XOIF_TOP_EMITTED); + + if (xop->xo_version) { + xo_printf(xop, "%*s\"__version\": \"%s\", %s", + xo_indent(xop), "", xop->xo_version, ppn); + xo_free(xop->xo_version); + xop->xo_version = NULL; + } +} + +static int +xo_do_open_container (xo_handle_t *xop, xo_xof_flags_t flags, const char *name) +{ + int rc = 0; + const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; + const char *pre_nl = ""; + + if (name == NULL) { + xo_failure(xop, "NULL passed for container name"); + name = XO_FAILURE_NAME; + } + + flags |= xop->xo_flags; /* Pick up handle flags */ + + switch (xo_style(xop)) { + case XO_STYLE_XML: + rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name); + + if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { + rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp; + xo_data_append(xop, xop->xo_attrs.xb_bufp, + xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); + xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; + } + + rc += xo_printf(xop, ">%s", ppn); + break; + + case XO_STYLE_JSON: + xo_stack_set_flags(xop); + + if (!XOF_ISSET(xop, XOF_NO_TOP) + && !XOIF_ISSET(xop, XOIF_TOP_EMITTED)) + xo_emit_top(xop, ppn); + + if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + + rc = xo_printf(xop, "%s%*s\"%s\": {%s", + pre_nl, xo_indent(xop), "", name, ppn); + break; + + case XO_STYLE_SDPARAMS: + break; + + case XO_STYLE_ENCODER: + rc = xo_encoder_handle(xop, XO_OP_OPEN_CONTAINER, name, NULL); + break; + } + + xo_depth_change(xop, name, 1, 1, XSS_OPEN_CONTAINER, + xo_stack_flags(flags)); + + return rc; +} + +static int +xo_open_container_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name) +{ + return xo_transition(xop, flags, name, XSS_OPEN_CONTAINER); +} + +int +xo_open_container_h (xo_handle_t *xop, const char *name) +{ + return xo_open_container_hf(xop, 0, name); +} + +int +xo_open_container (const char *name) +{ + return xo_open_container_hf(NULL, 0, name); +} + +int +xo_open_container_hd (xo_handle_t *xop, const char *name) +{ + return xo_open_container_hf(xop, XOF_DTRT, name); +} + +int +xo_open_container_d (const char *name) +{ + return xo_open_container_hf(NULL, XOF_DTRT, name); +} + +static int +xo_do_close_container (xo_handle_t *xop, const char *name) +{ + xop = xo_default(xop); + + int rc = 0; + const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; + const char *pre_nl = ""; + + if (name == NULL) { + xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; + + name = xsp->xs_name; + if (name) { + int len = strlen(name) + 1; + /* We need to make a local copy; xo_depth_change will free it */ + char *cp = alloca(len); + memcpy(cp, name, len); + name = cp; + } else if (!(xsp->xs_flags & XSF_DTRT)) { + xo_failure(xop, "missing name without 'dtrt' mode"); + name = XO_FAILURE_NAME; + } + } + + switch (xo_style(xop)) { + case XO_STYLE_XML: + xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0); + rc = xo_printf(xop, "%*s%s", xo_indent(xop), "", name, ppn); + break; + + case XO_STYLE_JSON: + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; + ppn = (xop->xo_depth <= 1) ? "\n" : ""; + + xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0); + rc = xo_printf(xop, "%s%*s}%s", pre_nl, xo_indent(xop), "", ppn); + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + break; + + case XO_STYLE_HTML: + case XO_STYLE_TEXT: + xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0); + break; + + case XO_STYLE_SDPARAMS: + break; + + case XO_STYLE_ENCODER: + xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0); + rc = xo_encoder_handle(xop, XO_OP_CLOSE_CONTAINER, name, NULL); + break; + } + + return rc; +} + +int +xo_close_container_h (xo_handle_t *xop, const char *name) +{ + return xo_transition(xop, 0, name, XSS_CLOSE_CONTAINER); +} + +int +xo_close_container (const char *name) +{ + return xo_close_container_h(NULL, name); +} + +int +xo_close_container_hd (xo_handle_t *xop) +{ + return xo_close_container_h(xop, NULL); +} + +int +xo_close_container_d (void) +{ + return xo_close_container_h(NULL, NULL); +} + +static int +xo_do_open_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) +{ + int rc = 0; + int indent = 0; + + xop = xo_default(xop); + + const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; + const char *pre_nl = ""; + + switch (xo_style(xop)) { + case XO_STYLE_JSON: + + indent = 1; + if (!XOF_ISSET(xop, XOF_NO_TOP) + && !XOIF_ISSET(xop, XOIF_TOP_EMITTED)) + xo_emit_top(xop, ppn); + + if (name == NULL) { + xo_failure(xop, "NULL passed for list name"); + name = XO_FAILURE_NAME; + } + + xo_stack_set_flags(xop); + + if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + + rc = xo_printf(xop, "%s%*s\"%s\": [%s", + pre_nl, xo_indent(xop), "", name, ppn); + break; + + case XO_STYLE_ENCODER: + rc = xo_encoder_handle(xop, XO_OP_OPEN_LIST, name, NULL); + break; + } + + xo_depth_change(xop, name, 1, indent, XSS_OPEN_LIST, + XSF_LIST | xo_stack_flags(flags)); + + return rc; +} + +static int +xo_open_list_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) +{ + return xo_transition(xop, flags, name, XSS_OPEN_LIST); +} + +int +xo_open_list_h (xo_handle_t *xop, const char *name UNUSED) +{ + return xo_open_list_hf(xop, 0, name); +} + +int +xo_open_list (const char *name) +{ + return xo_open_list_hf(NULL, 0, name); +} + +int +xo_open_list_hd (xo_handle_t *xop, const char *name UNUSED) +{ + return xo_open_list_hf(xop, XOF_DTRT, name); +} + +int +xo_open_list_d (const char *name) +{ + return xo_open_list_hf(NULL, XOF_DTRT, name); +} + +static int +xo_do_close_list (xo_handle_t *xop, const char *name) +{ + int rc = 0; + const char *pre_nl = ""; + + if (name == NULL) { + xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; + + name = xsp->xs_name; + if (name) { + int len = strlen(name) + 1; + /* We need to make a local copy; xo_depth_change will free it */ + char *cp = alloca(len); + memcpy(cp, name, len); + name = cp; + } else if (!(xsp->xs_flags & XSF_DTRT)) { + xo_failure(xop, "missing name without 'dtrt' mode"); + name = XO_FAILURE_NAME; + } + } + + switch (xo_style(xop)) { + case XO_STYLE_JSON: + if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + + xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LIST, XSF_LIST); + rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), ""); + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + break; + + case XO_STYLE_ENCODER: + xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST); + rc = xo_encoder_handle(xop, XO_OP_CLOSE_LIST, name, NULL); + break; + + default: + xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST); + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + break; + } + + return rc; +} + +int +xo_close_list_h (xo_handle_t *xop, const char *name) +{ + return xo_transition(xop, 0, name, XSS_CLOSE_LIST); +} + +int +xo_close_list (const char *name) +{ + return xo_close_list_h(NULL, name); +} + +int +xo_close_list_hd (xo_handle_t *xop) +{ + return xo_close_list_h(xop, NULL); +} + +int +xo_close_list_d (void) +{ + return xo_close_list_h(NULL, NULL); +} + +static int +xo_do_open_leaf_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) +{ + int rc = 0; + int indent = 0; + + xop = xo_default(xop); + + const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; + const char *pre_nl = ""; + + switch (xo_style(xop)) { + case XO_STYLE_JSON: + indent = 1; + + if (!XOF_ISSET(xop, XOF_NO_TOP)) { + if (!XOIF_ISSET(xop, XOIF_TOP_EMITTED)) { + xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn); + XOIF_SET(xop, XOIF_TOP_EMITTED); + } + } + + if (name == NULL) { + xo_failure(xop, "NULL passed for list name"); + name = XO_FAILURE_NAME; + } + + xo_stack_set_flags(xop); + + if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + + rc = xo_printf(xop, "%s%*s\"%s\": [%s", + pre_nl, xo_indent(xop), "", name, ppn); + break; + + case XO_STYLE_ENCODER: + rc = xo_encoder_handle(xop, XO_OP_OPEN_LEAF_LIST, name, NULL); + break; + } + + xo_depth_change(xop, name, 1, indent, XSS_OPEN_LEAF_LIST, + XSF_LIST | xo_stack_flags(flags)); + + return rc; +} + +static int +xo_do_close_leaf_list (xo_handle_t *xop, const char *name) +{ + int rc = 0; + const char *pre_nl = ""; + + if (name == NULL) { + xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; + + name = xsp->xs_name; + if (name) { + int len = strlen(name) + 1; + /* We need to make a local copy; xo_depth_change will free it */ + char *cp = alloca(len); + memcpy(cp, name, len); + name = cp; + } else if (!(xsp->xs_flags & XSF_DTRT)) { + xo_failure(xop, "missing name without 'dtrt' mode"); + name = XO_FAILURE_NAME; + } + } + + switch (xo_style(xop)) { + case XO_STYLE_JSON: + if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + + xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LEAF_LIST, XSF_LIST); + rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), ""); + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + break; + + case XO_STYLE_ENCODER: + rc = xo_encoder_handle(xop, XO_OP_CLOSE_LEAF_LIST, name, NULL); + /*fallthru*/ + + default: + xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LEAF_LIST, XSF_LIST); + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + break; + } + + return rc; +} + +static int +xo_do_open_instance (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) +{ + xop = xo_default(xop); + + int rc = 0; + const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; + const char *pre_nl = ""; + + flags |= xop->xo_flags; + + if (name == NULL) { + xo_failure(xop, "NULL passed for instance name"); + name = XO_FAILURE_NAME; + } + + switch (xo_style(xop)) { + case XO_STYLE_XML: + rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name); + + if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { + rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp; + xo_data_append(xop, xop->xo_attrs.xb_bufp, + xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); + xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; + } + + rc += xo_printf(xop, ">%s", ppn); + break; + + case XO_STYLE_JSON: + xo_stack_set_flags(xop); + + if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + + rc = xo_printf(xop, "%s%*s{%s", + pre_nl, xo_indent(xop), "", ppn); + break; + + case XO_STYLE_SDPARAMS: + break; + + case XO_STYLE_ENCODER: + rc = xo_encoder_handle(xop, XO_OP_OPEN_INSTANCE, name, NULL); + break; + } + + xo_depth_change(xop, name, 1, 1, XSS_OPEN_INSTANCE, xo_stack_flags(flags)); + + return rc; +} + +static int +xo_open_instance_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) +{ + return xo_transition(xop, flags, name, XSS_OPEN_INSTANCE); +} + +int +xo_open_instance_h (xo_handle_t *xop, const char *name) +{ + return xo_open_instance_hf(xop, 0, name); +} + +int +xo_open_instance (const char *name) +{ + return xo_open_instance_hf(NULL, 0, name); +} + +int +xo_open_instance_hd (xo_handle_t *xop, const char *name) +{ + return xo_open_instance_hf(xop, XOF_DTRT, name); +} + +int +xo_open_instance_d (const char *name) +{ + return xo_open_instance_hf(NULL, XOF_DTRT, name); +} + +static int +xo_do_close_instance (xo_handle_t *xop, const char *name) +{ + xop = xo_default(xop); + + int rc = 0; + const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; + const char *pre_nl = ""; + + if (name == NULL) { + xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; + + name = xsp->xs_name; + if (name) { + int len = strlen(name) + 1; + /* We need to make a local copy; xo_depth_change will free it */ + char *cp = alloca(len); + memcpy(cp, name, len); + name = cp; + } else if (!(xsp->xs_flags & XSF_DTRT)) { + xo_failure(xop, "missing name without 'dtrt' mode"); + name = XO_FAILURE_NAME; + } + } + + switch (xo_style(xop)) { + case XO_STYLE_XML: + xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0); + rc = xo_printf(xop, "%*s%s", xo_indent(xop), "", name, ppn); + break; + + case XO_STYLE_JSON: + pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; + + xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0); + rc = xo_printf(xop, "%s%*s}", pre_nl, xo_indent(xop), ""); + xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; + break; + + case XO_STYLE_HTML: + case XO_STYLE_TEXT: + xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0); + break; + + case XO_STYLE_SDPARAMS: + break; + + case XO_STYLE_ENCODER: + xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0); + rc = xo_encoder_handle(xop, XO_OP_CLOSE_INSTANCE, name, NULL); + break; + } + + return rc; +} + +int +xo_close_instance_h (xo_handle_t *xop, const char *name) +{ + return xo_transition(xop, 0, name, XSS_CLOSE_INSTANCE); +} + +int +xo_close_instance (const char *name) +{ + return xo_close_instance_h(NULL, name); +} + +int +xo_close_instance_hd (xo_handle_t *xop) +{ + return xo_close_instance_h(xop, NULL); +} + +int +xo_close_instance_d (void) +{ + return xo_close_instance_h(NULL, NULL); +} + +static int +xo_do_close_all (xo_handle_t *xop, xo_stack_t *limit) +{ + xo_stack_t *xsp; + int rc = 0; + xo_xsf_flags_t flags; + + for (xsp = &xop->xo_stack[xop->xo_depth]; xsp >= limit; xsp--) { + switch (xsp->xs_state) { + case XSS_INIT: + /* Nothing */ + rc = 0; + break; + + case XSS_OPEN_CONTAINER: + rc = xo_do_close_container(xop, NULL); + break; + + case XSS_OPEN_LIST: + rc = xo_do_close_list(xop, NULL); + break; + + case XSS_OPEN_INSTANCE: + rc = xo_do_close_instance(xop, NULL); + break; + + case XSS_OPEN_LEAF_LIST: + rc = xo_do_close_leaf_list(xop, NULL); + break; + + case XSS_MARKER: + flags = xsp->xs_flags & XSF_MARKER_FLAGS; + xo_depth_change(xop, xsp->xs_name, -1, 0, XSS_MARKER, 0); + xop->xo_stack[xop->xo_depth].xs_flags |= flags; + rc = 0; + break; + } + + if (rc < 0) + xo_failure(xop, "close %d failed: %d", xsp->xs_state, rc); + } + + return 0; +} + +/* + * This function is responsible for clearing out whatever is needed + * to get to the desired state, if possible. + */ +static int +xo_do_close (xo_handle_t *xop, const char *name, xo_state_t new_state) +{ + xo_stack_t *xsp, *limit = NULL; + int rc; + xo_state_t need_state = new_state; + + if (new_state == XSS_CLOSE_CONTAINER) + need_state = XSS_OPEN_CONTAINER; + else if (new_state == XSS_CLOSE_LIST) + need_state = XSS_OPEN_LIST; + else if (new_state == XSS_CLOSE_INSTANCE) + need_state = XSS_OPEN_INSTANCE; + else if (new_state == XSS_CLOSE_LEAF_LIST) + need_state = XSS_OPEN_LEAF_LIST; + else if (new_state == XSS_MARKER) + need_state = XSS_MARKER; + else + return 0; /* Unknown or useless new states are ignored */ + + for (xsp = &xop->xo_stack[xop->xo_depth]; xsp > xop->xo_stack; xsp--) { + /* + * Marker's normally stop us from going any further, unless + * we are popping a marker (new_state == XSS_MARKER). + */ + if (xsp->xs_state == XSS_MARKER && need_state != XSS_MARKER) { + if (name) { + xo_failure(xop, "close (xo_%s) fails at marker '%s'; " + "not found '%s'", + xo_state_name(new_state), + xsp->xs_name, name); + return 0; + + } else { + limit = xsp; + xo_failure(xop, "close stops at marker '%s'", xsp->xs_name); + } + break; + } + + if (xsp->xs_state != need_state) + continue; + + if (name && xsp->xs_name && strcmp(name, xsp->xs_name) != 0) + continue; + + limit = xsp; + break; + } + + if (limit == NULL) { + xo_failure(xop, "xo_%s can't find match for '%s'", + xo_state_name(new_state), name); + return 0; + } + + rc = xo_do_close_all(xop, limit); + + return rc; +} + +/* + * We are in a given state and need to transition to the new state. + */ +static int +xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name, + xo_state_t new_state) +{ + xo_stack_t *xsp; + int rc; + int old_state, on_marker; + + xop = xo_default(xop); + + rc = 0; + xsp = &xop->xo_stack[xop->xo_depth]; + old_state = xsp->xs_state; + on_marker = (old_state == XSS_MARKER); + + /* If there's a marker on top of the stack, we need to find a real state */ + while (old_state == XSS_MARKER) { + if (xsp == xop->xo_stack) + break; + xsp -= 1; + old_state = xsp->xs_state; + } + + /* + * At this point, the list of possible states are: + * XSS_INIT, XSS_OPEN_CONTAINER, XSS_OPEN_LIST, + * XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST, XSS_DISCARDING + */ + switch (XSS_TRANSITION(old_state, new_state)) { + + open_container: + case XSS_TRANSITION(XSS_INIT, XSS_OPEN_CONTAINER): + case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_CONTAINER): + case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_CONTAINER): + rc = xo_do_open_container(xop, flags, name); + break; + + case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_CONTAINER): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close_list(xop, NULL); + if (rc >= 0) + goto open_container; + break; + + case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_CONTAINER): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close_leaf_list(xop, NULL); + if (rc >= 0) + goto open_container; + break; + + /*close_container:*/ + case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_CONTAINER): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close(xop, name, new_state); + break; + + case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_CONTAINER): + /* This is an exception for "xo --close" */ + rc = xo_do_close_container(xop, name); + break; + + case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_CONTAINER): + case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_CONTAINER): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close(xop, name, new_state); + break; + + case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_CONTAINER): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close_leaf_list(xop, NULL); + if (rc >= 0) + rc = xo_do_close(xop, name, new_state); + break; + + open_list: + case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LIST): + case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LIST): + case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LIST): + rc = xo_do_open_list(xop, flags, name); + break; + + case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LIST): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close_list(xop, NULL); + if (rc >= 0) + goto open_list; + break; + + case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LIST): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close_leaf_list(xop, NULL); + if (rc >= 0) + goto open_list; + break; + + /*close_list:*/ + case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LIST): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close(xop, name, new_state); + break; + + case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LIST): + case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LIST): + case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LIST): + case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LIST): + rc = xo_do_close(xop, name, new_state); + break; + + open_instance: + case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_INSTANCE): + rc = xo_do_open_instance(xop, flags, name); + break; + + case XSS_TRANSITION(XSS_INIT, XSS_OPEN_INSTANCE): + case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_INSTANCE): + rc = xo_do_open_list(xop, flags, name); + if (rc >= 0) + goto open_instance; + break; + + case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_INSTANCE): + if (on_marker) { + rc = xo_do_open_list(xop, flags, name); + } else { + rc = xo_do_close_instance(xop, NULL); + } + if (rc >= 0) + goto open_instance; + break; + + case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_INSTANCE): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close_leaf_list(xop, NULL); + if (rc >= 0) + goto open_instance; + break; + + /*close_instance:*/ + case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_INSTANCE): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close_instance(xop, name); + break; + + case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_INSTANCE): + /* This one makes no sense; ignore it */ + xo_failure(xop, "xo_close_instance ignored when called from " + "initial state ('%s')", name ?: "(unknown)"); + break; + + case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_INSTANCE): + case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_INSTANCE): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close(xop, name, new_state); + break; + + case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_INSTANCE): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close_leaf_list(xop, NULL); + if (rc >= 0) + rc = xo_do_close(xop, name, new_state); + break; + + open_leaf_list: + case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LEAF_LIST): + case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST): + case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LEAF_LIST): + rc = xo_do_open_leaf_list(xop, flags, name); + break; + + case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LEAF_LIST): + case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LEAF_LIST): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close_list(xop, NULL); + if (rc >= 0) + goto open_leaf_list; + break; + + /*close_leaf_list:*/ + case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LEAF_LIST): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close_leaf_list(xop, name); + break; + + case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LEAF_LIST): + /* Makes no sense; ignore */ + xo_failure(xop, "xo_close_leaf_list ignored when called from " + "initial state ('%s')", name ?: "(unknown)"); + break; + + case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LEAF_LIST): + case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LEAF_LIST): + case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LEAF_LIST): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close(xop, name, new_state); + break; + + /*emit:*/ + case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT): + case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT): + break; + + case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close(xop, NULL, XSS_CLOSE_LIST); + break; + + case XSS_TRANSITION(XSS_INIT, XSS_EMIT): + break; + + case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT): + if (on_marker) + goto marker_prevents_close; + rc = xo_do_close_leaf_list(xop, NULL); + break; + + /*emit_leaf_list:*/ + case XSS_TRANSITION(XSS_INIT, XSS_EMIT_LEAF_LIST): + case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT_LEAF_LIST): + case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT_LEAF_LIST): + rc = xo_do_open_leaf_list(xop, flags, name); + break; + + case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT_LEAF_LIST): + break; + + case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT_LEAF_LIST): + /* + * We need to be backward compatible with the pre-xo_open_leaf_list + * API, where both lists and leaf-lists were opened as lists. So + * if we find an open list that hasn't had anything written to it, + * we'll accept it. + */ + break; + + default: + xo_failure(xop, "unknown transition: (%u -> %u)", + xsp->xs_state, new_state); + } + + return rc; + + marker_prevents_close: + xo_failure(xop, "marker '%s' prevents transition from %s to %s", + xop->xo_stack[xop->xo_depth].xs_name, + xo_state_name(old_state), xo_state_name(new_state)); + return -1; +} + +int +xo_open_marker_h (xo_handle_t *xop, const char *name) +{ + xop = xo_default(xop); + + xo_depth_change(xop, name, 1, 0, XSS_MARKER, + xop->xo_stack[xop->xo_depth].xs_flags & XSF_MARKER_FLAGS); + + return 0; +} + +int +xo_open_marker (const char *name) +{ + return xo_open_marker_h(NULL, name); +} + +int +xo_close_marker_h (xo_handle_t *xop, const char *name) +{ + xop = xo_default(xop); + + return xo_do_close(xop, name, XSS_MARKER); +} + +int +xo_close_marker (const char *name) +{ + return xo_close_marker_h(NULL, name); +} + +/* + * Record custom output functions into the xo handle, allowing + * integration with a variety of output frameworks. + */ +void +xo_set_writer (xo_handle_t *xop, void *opaque, xo_write_func_t write_func, + xo_close_func_t close_func, xo_flush_func_t flush_func) +{ + xop = xo_default(xop); + + xop->xo_opaque = opaque; + xop->xo_write = write_func; + xop->xo_close = close_func; + xop->xo_flush = flush_func; +} + +void +xo_set_allocator (xo_realloc_func_t realloc_func, xo_free_func_t free_func) +{ + xo_realloc = realloc_func; + xo_free = free_func; +} + +int +xo_flush_h (xo_handle_t *xop) +{ + static char div_close[] = "
"; + int rc; + + xop = xo_default(xop); + + switch (xo_style(xop)) { + case XO_STYLE_HTML: + if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) { + XOIF_CLEAR(xop, XOIF_DIV_OPEN); + xo_data_append(xop, div_close, sizeof(div_close) - 1); + + if (XOF_ISSET(xop, XOF_PRETTY)) + xo_data_append(xop, "\n", 1); + } + break; + + case XO_STYLE_ENCODER: + xo_encoder_handle(xop, XO_OP_FLUSH, NULL, NULL); + } + + rc = xo_write(xop); + if (rc >= 0 && xop->xo_flush) + if (xop->xo_flush(xop->xo_opaque) < 0) + return -1; + + return rc; +} + +int +xo_flush (void) +{ + return xo_flush_h(NULL); +} + +int +xo_finish_h (xo_handle_t *xop) +{ + const char *cp = ""; + xop = xo_default(xop); + + if (!XOF_ISSET(xop, XOF_NO_CLOSE)) + xo_do_close_all(xop, xop->xo_stack); + + switch (xo_style(xop)) { + case XO_STYLE_JSON: + if (!XOF_ISSET(xop, XOF_NO_TOP)) { + if (XOIF_ISSET(xop, XOIF_TOP_EMITTED)) + XOIF_CLEAR(xop, XOIF_TOP_EMITTED); /* Turn off before output */ + else + cp = "{ "; + xo_printf(xop, "%*s%s}\n",xo_indent(xop), "", cp); + } + break; + + case XO_STYLE_ENCODER: + xo_encoder_handle(xop, XO_OP_FINISH, NULL, NULL); + break; + } + + return xo_flush_h(xop); +} + +int +xo_finish (void) +{ + return xo_finish_h(NULL); +} + +/* + * xo_finish_atexit is suitable for atexit() calls, to force clear up + * and finalizing output. + */ +void +xo_finish_atexit (void) +{ + (void) xo_finish_h(NULL); +} + +/* + * Generate an error message, such as would be displayed on stderr + */ +void +xo_error_hv (xo_handle_t *xop, const char *fmt, va_list vap) +{ + xop = xo_default(xop); + + /* + * If the format string doesn't end with a newline, we pop + * one on ourselves. + */ + int len = strlen(fmt); + if (len > 0 && fmt[len - 1] != '\n') { + char *newfmt = alloca(len + 2); + memcpy(newfmt, fmt, len); + newfmt[len] = '\n'; + newfmt[len] = '\0'; + fmt = newfmt; + } + + switch (xo_style(xop)) { + case XO_STYLE_TEXT: + vfprintf(stderr, fmt, vap); + break; + + case XO_STYLE_HTML: + va_copy(xop->xo_vap, vap); + + xo_buf_append_div(xop, "error", 0, NULL, 0, fmt, strlen(fmt), NULL, 0); + + if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) + xo_line_close(xop); + + xo_write(xop); + + va_end(xop->xo_vap); + bzero(&xop->xo_vap, sizeof(xop->xo_vap)); + break; + + case XO_STYLE_XML: + case XO_STYLE_JSON: + va_copy(xop->xo_vap, vap); + + xo_open_container_h(xop, "error"); + xo_format_value(xop, "message", 7, fmt, strlen(fmt), NULL, 0, 0); + xo_close_container_h(xop, "error"); + + va_end(xop->xo_vap); + bzero(&xop->xo_vap, sizeof(xop->xo_vap)); + break; + + case XO_STYLE_SDPARAMS: + case XO_STYLE_ENCODER: + break; + } +} + +void +xo_error_h (xo_handle_t *xop, const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_error_hv(xop, fmt, vap); + va_end(vap); +} + +/* + * Generate an error message, such as would be displayed on stderr + */ +void +xo_error (const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_error_hv(NULL, fmt, vap); + va_end(vap); +} + +/* + * Parse any libxo-specific options from the command line, removing them + * so the main() argument parsing won't see them. We return the new value + * for argc or -1 for error. If an error occurred, the program should + * exit. A suitable error message has already been displayed. + */ +int +xo_parse_args (int argc, char **argv) +{ + static char libxo_opt[] = "--libxo"; + char *cp; + int i, save; + + /* Save our program name for xo_err and friends */ + xo_program = argv[0]; + cp = strrchr(xo_program, '/'); + if (cp) + xo_program = cp + 1; + + for (save = i = 1; i < argc; i++) { + if (argv[i] == NULL + || strncmp(argv[i], libxo_opt, sizeof(libxo_opt) - 1) != 0) { + if (save != i) + argv[save] = argv[i]; + save += 1; + continue; + } + + cp = argv[i] + sizeof(libxo_opt) - 1; + if (*cp == 0) { + cp = argv[++i]; + if (cp == 0) { + xo_warnx("missing libxo option"); + return -1; + } + + if (xo_set_options(NULL, cp) < 0) + return -1; + } else if (*cp == ':') { + if (xo_set_options(NULL, cp) < 0) + return -1; + + } else if (*cp == '=') { + if (xo_set_options(NULL, ++cp) < 0) + return -1; + + } else if (*cp == '-') { + cp += 1; + if (strcmp(cp, "check") == 0) { + exit(XO_HAS_LIBXO); + + } else { + xo_warnx("unknown libxo option: '%s'", argv[i]); + return -1; + } + } else { + xo_warnx("unknown libxo option: '%s'", argv[i]); + return -1; + } + } + + argv[save] = NULL; + return save; +} + +/* + * Debugging function that dumps the current stack of open libxo constructs, + * suitable for calling from the debugger. + */ +void +xo_dump_stack (xo_handle_t *xop) +{ + int i; + xo_stack_t *xsp; + + xop = xo_default(xop); + + fprintf(stderr, "Stack dump:\n"); + + xsp = xop->xo_stack; + for (i = 1, xsp++; i <= xop->xo_depth; i++, xsp++) { + fprintf(stderr, " [%d] %s '%s' [%x]\n", + i, xo_state_name(xsp->xs_state), + xsp->xs_name ?: "--", xsp->xs_flags); + } +} + +/* + * Record the program name used for error messages + */ +void +xo_set_program (const char *name) +{ + xo_program = name; +} + +void +xo_set_version_h (xo_handle_t *xop, const char *version UNUSED) +{ + xop = xo_default(xop); + + if (version == NULL || strchr(version, '"') != NULL) + return; + + if (!xo_style_is_encoding(xop)) + return; + + switch (xo_style(xop)) { + case XO_STYLE_XML: + /* For XML, we record this as an attribute for the first tag */ + xo_attr_h(xop, "__version", "%s", version); + break; + + case XO_STYLE_JSON: + /* + * For JSON, we record the version string in our handle, and emit + * it in xo_emit_top. + */ + xop->xo_version = xo_strndup(version, -1); + break; + + case XO_STYLE_ENCODER: + xo_encoder_handle(xop, XO_OP_VERSION, NULL, version); + break; + } +} + +/* + * Set the version number for the API content being carried thru + * the xo handle. + */ +void +xo_set_version (const char *version) +{ + xo_set_version_h(NULL, version); +} + +/* + * Generate a warning. Normally, this is a text message written to + * standard error. If the XOF_WARN_XML flag is set, then we generate + * XMLified content on standard output. + */ +void +xo_emit_warn_hcv (xo_handle_t *xop, int as_warning, int code, + const char *fmt, va_list vap) +{ + xop = xo_default(xop); + + if (fmt == NULL) + return; + + xo_open_marker_h(xop, "xo_emit_warn_hcv"); + xo_open_container_h(xop, as_warning ? "__warning" : "__error"); + + if (xo_program) + xo_emit("{wc:program}", xo_program); + + if (xo_style(xop) == XO_STYLE_XML || xo_style(xop) == XO_STYLE_JSON) { + va_list ap; + xo_handle_t temp; + + bzero(&temp, sizeof(temp)); + temp.xo_style = XO_STYLE_TEXT; + xo_buf_init(&temp.xo_data); + xo_depth_check(&temp, XO_DEPTH); + + va_copy(ap, vap); + (void) xo_emit_hv(&temp, fmt, ap); + va_end(ap); + + xo_buffer_t *src = &temp.xo_data; + xo_format_value(xop, "message", 7, src->xb_bufp, + src->xb_curp - src->xb_bufp, NULL, 0, 0); + + xo_free(temp.xo_stack); + xo_buf_cleanup(src); + } + + (void) xo_emit_hv(xop, fmt, vap); + + int len = strlen(fmt); + if (len > 0 && fmt[len - 1] != '\n') { + if (code > 0) { + const char *msg = strerror(code); + if (msg) + xo_emit_h(xop, ": {G:strerror}{g:error/%s}", msg); + } + xo_emit("\n"); + } + + xo_close_marker_h(xop, "xo_emit_warn_hcv"); + xo_flush_h(xop); +} + +void +xo_emit_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_emit_warn_hcv(xop, 1, code, fmt, vap); + va_end(vap); +} + +void +xo_emit_warn_c (int code, const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 1, code, fmt, vap); + va_end(vap); +} + +void +xo_emit_warn (const char *fmt, ...) +{ + int code = errno; + va_list vap; + + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 1, code, fmt, vap); + va_end(vap); +} + +void +xo_emit_warnx (const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 1, -1, fmt, vap); + va_end(vap); +} + +void +xo_emit_err_v (int eval, int code, const char *fmt, va_list vap) +{ + xo_emit_warn_hcv(NULL, 0, code, fmt, vap); + xo_finish(); + exit(eval); +} + +void +xo_emit_err (int eval, const char *fmt, ...) +{ + int code = errno; + va_list vap; + va_start(vap, fmt); + xo_emit_err_v(0, code, fmt, vap); + va_end(vap); + exit(eval); +} + +void +xo_emit_errx (int eval, const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_emit_err_v(0, -1, fmt, vap); + va_end(vap); + xo_finish(); + exit(eval); +} + +void +xo_emit_errc (int eval, int code, const char *fmt, ...) +{ + va_list vap; + + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 0, code, fmt, vap); + va_end(vap); + xo_finish(); + exit(eval); +} + +/* + * Get the opaque private pointer for an xo handle + */ +void * +xo_get_private (xo_handle_t *xop) +{ + xop = xo_default(xop); + return xop->xo_private; +} + +/* + * Set the opaque private pointer for an xo handle. + */ +void +xo_set_private (xo_handle_t *xop, void *opaque) +{ + xop = xo_default(xop); + xop->xo_private = opaque; +} + +/* + * Get the encoder function + */ +xo_encoder_func_t +xo_get_encoder (xo_handle_t *xop) +{ + xop = xo_default(xop); + return xop->xo_encoder; +} + +/* + * Record an encoder callback function in an xo handle. + */ +void +xo_set_encoder (xo_handle_t *xop, xo_encoder_func_t encoder) +{ + xop = xo_default(xop); + + xop->xo_style = XO_STYLE_ENCODER; + xop->xo_encoder = encoder; +} Property changes on: vendor/Juniper/libxo/dist/libxo/libxo.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo.h =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo.h (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo.h (revision 296962) @@ -0,0 +1,596 @@ +/* + * Copyright (c) 2014-2015, Juniper Networks, Inc. + * All rights reserved. + * This SOFTWARE is licensed under the LICENSE provided in the + * ../Copyright file. By downloading, installing, copying, or otherwise + * using the SOFTWARE, you agree to be bound by the terms of that + * LICENSE. + * Phil Shafer, July 2014 + */ + +/** + * libxo provides a means of generating text, XML, JSON, and HTML output + * using a single set of function calls, maximizing the value of output + * while minimizing the cost/impact on the code. + * + * Full documentation is available in ./doc/libxo.txt or online at: + * http://juniper.github.io/libxo/libxo-manual.html + */ + +#ifndef INCLUDE_XO_H +#define INCLUDE_XO_H + +#include +#include +#include +#include +#include + +#ifdef __dead2 +#define NORETURN __dead2 +#else +#define NORETURN +#endif /* __dead2 */ + +/* + * Normally we'd use the HAVE_PRINTFLIKE define triggered by the + * --enable-printflike option to configure, but we don't install + * our internal "xoconfig.h", and I'd rather not. Taking the + * coward's path, we'll turn it on inside a #if that allows + * others to turn it off where needed. Not ideal, but functional. + */ +#if !defined(NO_PRINTFLIKE) && !defined(__linux__) +#define PRINTFLIKE(_x, _y) __printflike(_x, _y) +#else +#define PRINTFLIKE(_x, _y) +#endif /* NO_PRINTFLIKE */ + +/** Formatting types */ +typedef unsigned short xo_style_t; +#define XO_STYLE_TEXT 0 /** Generate text output */ +#define XO_STYLE_XML 1 /** Generate XML output */ +#define XO_STYLE_JSON 2 /** Generate JSON output */ +#define XO_STYLE_HTML 3 /** Generate HTML output */ +#define XO_STYLE_SDPARAMS 4 /* Generate syslog structured data params */ +#define XO_STYLE_ENCODER 5 /* Generate calls to external encoder */ + +/** Flags for libxo */ +typedef unsigned long long xo_xof_flags_t; +#define XOF_BIT(_n) ((xo_xof_flags_t) 1 << (_n)) +#define XOF_CLOSE_FP XOF_BIT(0) /** Close file pointer on xo_close() */ +#define XOF_PRETTY XOF_BIT(1) /** Make 'pretty printed' output */ +#define XOF_LOG_SYSLOG XOF_BIT(2) /** Log (on stderr) our syslog content */ +#define XOF_RESV3 XOF_BIT(3) /* Unused */ + +#define XOF_WARN XOF_BIT(4) /** Generate warnings for broken calls */ +#define XOF_XPATH XOF_BIT(5) /** Emit XPath attributes in HTML */ +#define XOF_INFO XOF_BIT(6) /** Emit additional info fields (HTML) */ +#define XOF_WARN_XML XOF_BIT(7) /** Emit warnings in XML (on stdout) */ + +#define XOF_NO_ENV XOF_BIT(8) /** Don't look at LIBXO_OPTIONS env var */ +#define XOF_NO_VA_ARG XOF_BIT(9) /** Don't advance va_list w/ va_arg() */ +#define XOF_DTRT XOF_BIT(10) /** Enable "do the right thing" mode */ +#define XOF_KEYS XOF_BIT(11) /** Flag 'key' fields for xml and json */ + +#define XOF_IGNORE_CLOSE XOF_BIT(12) /** Ignore errors on close tags */ +#define XOF_NOT_FIRST XOF_BIT(13) /* Not the first item (JSON) */ +#define XOF_NO_LOCALE XOF_BIT(14) /** Don't bother with locale */ +#define XOF_RESV15 XOF_BIT(15) /* Unused */ + +#define XOF_NO_TOP XOF_BIT(16) /** Don't emit the top braces in JSON */ +#define XOF_RESV17 XOF_BIT(17) /* Unused */ +#define XOF_UNITS XOF_BIT(18) /** Encode units in XML */ +#define XOF_RESV19 XOF_BIT(19) /* Unused */ + +#define XOF_UNDERSCORES XOF_BIT(20) /** Replace dashes with underscores (JSON)*/ +#define XOF_COLUMNS XOF_BIT(21) /** xo_emit should return a column count */ +#define XOF_FLUSH XOF_BIT(22) /** Flush after each xo_emit call */ +#define XOF_FLUSH_LINE XOF_BIT(23) /** Flush after each newline */ + +#define XOF_NO_CLOSE XOF_BIT(24) /** xo_finish won't close open elements */ +#define XOF_COLOR_ALLOWED XOF_BIT(25) /** Allow color/effects to be enabled */ +#define XOF_COLOR XOF_BIT(26) /** Enable color and effects */ +#define XOF_NO_HUMANIZE XOF_BIT(27) /** Block the {h:} modifier */ + +#define XOF_LOG_GETTEXT XOF_BIT(28) /** Log (stderr) gettext lookup strings */ +#define XOF_UTF8 XOF_BIT(29) /** Force text output to be UTF8 */ + +/* + * The xo_info_t structure provides a mapping between names and + * additional data emitted via HTML. + */ +typedef struct xo_info_s { + const char *xi_name; /* Name of the element */ + const char *xi_type; /* Type of field */ + const char *xi_help; /* Description of field */ +} xo_info_t; + +#define XO_INFO_NULL NULL, NULL, NULL /* Use '{ XO_INFO_NULL }' to end lists */ + +struct xo_handle_s; /* Opaque structure forward */ +typedef struct xo_handle_s xo_handle_t; /* Handle for XO output */ + +typedef int (*xo_write_func_t)(void *, const char *); +typedef void (*xo_close_func_t)(void *); +typedef int (*xo_flush_func_t)(void *); +typedef void *(*xo_realloc_func_t)(void *, size_t); +typedef void (*xo_free_func_t)(void *); + +/* + * The formatter function mirrors "vsnprintf", with an additional argument + * of the xo handle. The caller should return the number of bytes _needed_ + * to fit the data, even if this exceeds 'len'. + */ +typedef int (*xo_formatter_t)(xo_handle_t *, char *, int, + const char *, va_list); +typedef void (*xo_checkpointer_t)(xo_handle_t *, va_list, int); + +xo_handle_t * +xo_create (xo_style_t style, xo_xof_flags_t flags); + +xo_handle_t * +xo_create_to_file (FILE *fp, xo_style_t style, xo_xof_flags_t flags); + +void +xo_destroy (xo_handle_t *xop); + +void +xo_set_writer (xo_handle_t *xop, void *opaque, xo_write_func_t write_func, + xo_close_func_t close_func, xo_flush_func_t flush_func); + +void +xo_set_allocator (xo_realloc_func_t realloc_func, xo_free_func_t free_func); + +void +xo_set_style (xo_handle_t *xop, xo_style_t style); + +xo_style_t +xo_get_style (xo_handle_t *xop); + +int +xo_set_style_name (xo_handle_t *xop, const char *style); + +int +xo_set_options (xo_handle_t *xop, const char *input); + +xo_xof_flags_t +xo_get_flags (xo_handle_t *xop); + +void +xo_set_flags (xo_handle_t *xop, xo_xof_flags_t flags); + +void +xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags); + +void +xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count); + +void +xo_set_formatter (xo_handle_t *xop, xo_formatter_t func, xo_checkpointer_t); + +void +xo_set_depth (xo_handle_t *xop, int depth); + +int +xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap); + +int +xo_emit_h (xo_handle_t *xop, const char *fmt, ...); + +int +xo_emit (const char *fmt, ...); + +PRINTFLIKE(2, 0) +static inline int +xo_emit_hvp (xo_handle_t *xop, const char *fmt, va_list vap) +{ + return xo_emit_hv(xop, fmt, vap); +} + +PRINTFLIKE(2, 3) +static inline int +xo_emit_hp (xo_handle_t *xop, const char *fmt, ...) +{ + va_list vap; + va_start(vap, fmt); + int rc = xo_emit_hv(xop, fmt, vap); + va_end(vap); + return rc; +} + +PRINTFLIKE(1, 2) +static inline int +xo_emit_p (const char *fmt, ...) +{ + va_list vap; + va_start(vap, fmt); + int rc = xo_emit_hv(NULL, fmt, vap); + va_end(vap); + return rc; +} + +int +xo_open_container_h (xo_handle_t *xop, const char *name); + +int +xo_open_container (const char *name); + +int +xo_open_container_hd (xo_handle_t *xop, const char *name); + +int +xo_open_container_d (const char *name); + +int +xo_close_container_h (xo_handle_t *xop, const char *name); + +int +xo_close_container (const char *name); + +int +xo_close_container_hd (xo_handle_t *xop); + +int +xo_close_container_d (void); + +int +xo_open_list_h (xo_handle_t *xop, const char *name); + +int +xo_open_list (const char *name); + +int +xo_open_list_hd (xo_handle_t *xop, const char *name); + +int +xo_open_list_d (const char *name); + +int +xo_close_list_h (xo_handle_t *xop, const char *name); + +int +xo_close_list (const char *name); + +int +xo_close_list_hd (xo_handle_t *xop); + +int +xo_close_list_d (void); + +int +xo_open_instance_h (xo_handle_t *xop, const char *name); + +int +xo_open_instance (const char *name); + +int +xo_open_instance_hd (xo_handle_t *xop, const char *name); + +int +xo_open_instance_d (const char *name); + +int +xo_close_instance_h (xo_handle_t *xop, const char *name); + +int +xo_close_instance (const char *name); + +int +xo_close_instance_hd (xo_handle_t *xop); + +int +xo_close_instance_d (void); + +int +xo_open_marker_h (xo_handle_t *xop, const char *name); + +int +xo_open_marker (const char *name); + +int +xo_close_marker_h (xo_handle_t *xop, const char *name); + +int +xo_close_marker (const char *name); + +int +xo_attr_h (xo_handle_t *xop, const char *name, const char *fmt, ...); + +int +xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap); + +int +xo_attr (const char *name, const char *fmt, ...); + +void +xo_error_hv (xo_handle_t *xop, const char *fmt, va_list vap); + +void +xo_error_h (xo_handle_t *xop, const char *fmt, ...); + +void +xo_error (const char *fmt, ...); + +int +xo_flush_h (xo_handle_t *xop); + +int +xo_flush (void); + +int +xo_finish_h (xo_handle_t *xop); + +int +xo_finish (void); + +void +xo_finish_atexit (void); + +void +xo_set_leading_xpath (xo_handle_t *xop, const char *path); + +void +xo_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...) PRINTFLIKE(3, 4); + +void +xo_warn_c (int code, const char *fmt, ...) PRINTFLIKE(2, 3); + +void +xo_warn (const char *fmt, ...) PRINTFLIKE(1, 2); + +void +xo_warnx (const char *fmt, ...) PRINTFLIKE(1, 2); + +void +xo_err (int eval, const char *fmt, ...) NORETURN PRINTFLIKE(2, 3); + +void +xo_errx (int eval, const char *fmt, ...) NORETURN PRINTFLIKE(2, 3); + +void +xo_errc (int eval, int code, const char *fmt, ...) NORETURN PRINTFLIKE(3, 4); + +void +xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap) PRINTFLIKE(3, 0); + +void +xo_message_hc (xo_handle_t *xop, int code, const char *fmt, ...) PRINTFLIKE(3, 4); + +void +xo_message_c (int code, const char *fmt, ...) PRINTFLIKE(2, 3); + +void +xo_message_e (const char *fmt, ...) PRINTFLIKE(1, 2); + +void +xo_message (const char *fmt, ...) PRINTFLIKE(1, 2); + +void +xo_emit_warn_hcv (xo_handle_t *xop, int as_warning, int code, + const char *fmt, va_list vap); + +void +xo_emit_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...); + +void +xo_emit_warn_c (int code, const char *fmt, ...); + +void +xo_emit_warn (const char *fmt, ...); + +void +xo_emit_warnx (const char *fmt, ...); + +void +xo_emit_err (int eval, const char *fmt, ...) NORETURN; + +void +xo_emit_errx (int eval, const char *fmt, ...) NORETURN; + +void +xo_emit_errc (int eval, int code, const char *fmt, ...) NORETURN; + +PRINTFLIKE(4, 0) +static inline void +xo_emit_warn_hcvp (xo_handle_t *xop, int as_warning, int code, + const char *fmt, va_list vap) +{ + xo_emit_warn_hcv(xop, as_warning, code, fmt, vap); +} + +PRINTFLIKE(3, 4) +static inline void +xo_emit_warn_hcp (xo_handle_t *xop, int code, const char *fmt, ...) +{ + va_list vap; + va_start(vap, fmt); + xo_emit_warn_hcv(xop, 1, code, fmt, vap); + va_end(vap); +} + +PRINTFLIKE(2, 3) +static inline void +xo_emit_warn_cp (int code, const char *fmt, ...) +{ + va_list vap; + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 1, code, fmt, vap); + va_end(vap); +} + +PRINTFLIKE(1, 2) +static inline void +xo_emit_warn_p (const char *fmt, ...) +{ + int code = errno; + va_list vap; + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 1, code, fmt, vap); + va_end(vap); +} + +PRINTFLIKE(1, 2) +static inline void +xo_emit_warnx_p (const char *fmt, ...) +{ + va_list vap; + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 1, -1, fmt, vap); + va_end(vap); +} + +NORETURN PRINTFLIKE(2, 3) +static inline void +xo_emit_err_p (int eval, const char *fmt, ...) +{ + int code = errno; + va_list vap; + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 0, code, fmt, vap); + va_end(vap); + + exit(eval); +} + +PRINTFLIKE(2, 3) +static inline void +xo_emit_errx_p (int eval, const char *fmt, ...) +{ + va_list vap; + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 0, -1, fmt, vap); + va_end(vap); + exit(eval); +} + +PRINTFLIKE(3, 4) +static inline void +xo_emit_errc_p (int eval, int code, const char *fmt, ...) +{ + va_list vap; + va_start(vap, fmt); + xo_emit_warn_hcv(NULL, 0, code, fmt, vap); + va_end(vap); + exit(eval); +} + +void +xo_emit_err_v (int eval, int code, const char *fmt, va_list vap) NORETURN PRINTFLIKE(3, 0); + +void +xo_no_setlocale (void); + +/** + * @brief Lift libxo-specific arguments from a set of arguments + * + * libxo-enable programs typically use command line options to enable + * all the nifty-cool libxo features. xo_parse_args() makes this simple + * by pre-processing the command line arguments given to main(), handling + * and removing the libxo-specific ones, meaning anything starting with + * "--libxo". A full description of these arguments is in the base + * documentation. + * @param[in] argc Number of arguments (ala #main()) + * @param[in] argc Array of argument strings (ala #main()) + * @return New number of arguments, or -1 for failure. + */ +int +xo_parse_args (int argc, char **argv); + +/** + * This is the "magic" number returned by libxo-supporting commands + * when passed the equally magic "--libxo-check" option. If you + * return this, we can (unsafely) assume that since you know the magic + * handshake, you'll happily handle future --libxo options and not do + * something violent like reboot the box or create another hole in the + * ozone layer. + */ +#define XO_HAS_LIBXO 121 + +/** + * externs for libxo's version number strings + */ +extern const char xo_version[]; /** Base version triple string */ +extern const char xo_version_extra[]; /** Extra version magic content */ + +/** + * @brief Dump the internal stack of a libxo handle. + * + * This diagnostic function is something I will ask you to call from + * your program when you write to tell me libxo has gone bat-stink + * crazy and has discarded your list or container or content. Output + * content will be what we lovingly call "developer entertainment". + * @param[in] xop A valid libxo handle, or NULL for the default handle + */ +void +xo_dump_stack (xo_handle_t *xop); + +/** + * @brief Recode the name of the program, suitable for error output. + * + * libxo will record the given name for use while generating error + * messages. The contents are not copied, so the value must continue + * to point to a valid memory location. This allows the caller to change + * the value, but requires the caller to manage the memory. Typically + * this is called with argv[0] from main(). + * @param[in] name The name of the current application program + */ +void +xo_set_program (const char *name); + +/** + * @brief Add a version string to the output, where possible. + * + * Adds a version number to the output, suitable for tracking + * changes in the content. This is only important for the "encoding" + * format styles (XML and JSON) and allows a user of the data to + * discern which version of the data model is in use. + * @param[in] version The version number, encoded as a string + */ +void +xo_set_version (const char *version); + +/** + * #xo_set_version with a handle. + * @param[in] xop A valid libxo handle, or NULL for the default handle + * @param[in] version The version number, encoded as a string + */ +void +xo_set_version_h (xo_handle_t *xop, const char *version); + +void +xo_open_log (const char *ident, int logopt, int facility); + +void +xo_close_log (void); + +int +xo_set_logmask (int maskpri); + +void +xo_set_unit_test_mode (int value); + +void +xo_syslog (int priority, const char *name, const char *message, ...); + +void +xo_vsyslog (int priority, const char *name, const char *message, va_list args); + +typedef void (*xo_syslog_open_t)(void); +typedef void (*xo_syslog_send_t)(const char *full_msg, + const char *v0_hdr, const char *text_only); +typedef void (*xo_syslog_close_t)(void); + +void +xo_set_syslog_handler (xo_syslog_open_t open_func, xo_syslog_send_t send_func, + xo_syslog_close_t close_func); + +void +xo_set_syslog_enterprise_id (unsigned short eid); + +typedef void (*xo_simplify_field_func_t)(const char *, unsigned, int); + +char * +xo_simplify_format (xo_handle_t *xop, const char *fmt, int with_numbers, + xo_simplify_field_func_t field_cb); + +#endif /* INCLUDE_XO_H */ Property changes on: vendor/Juniper/libxo/dist/libxo/xo.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_attr.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_attr.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_attr.3 (revision 296962) @@ -0,0 +1,60 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd July, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_attr , xo_attr_h , xo_attr_hv +.Nd Add attribute name/value pairs to formatted output +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft int +.Fn xo_attr "const char *name" "const char *fmt" "..." +.Ft int +.Fn xo_attr_h "xo_handle_t *handle" "const char *name, const char *fmt" "..." +.Ft int +.Fn xo_attr_hv "xo_handle_t *handle" "const char *name" "const char *fmt" "va_list vap" +.Sh DESCRIPTION +The +.Fn xo_attr +function emits attributes for the XML output style. The attribute +value is recorded in the +.Fa handle +and is attached to the next field that is emitted via a +.Xr xo_emit 3 +call. +.Pp +The +.Fa name +parameter give the name of the attribute to be encoded. The +.Fa fmt +parameter gives a printf-style format string used to format the +value of the attribute using any remaining arguments, or the +.Fa vap +parameter as passed to +.Fn xo_attr_hv . +.Bd -literal -offset indent + EXAMPLE: + xo_attr("seconds", "%ld", (unsigned long) login_time); + struct tm *tmp = localtime(login_time); + strftime(buf, sizeof(buf), "%R", tmp); + xo_emit("Logged in at {:login-time}\\n", buf); + XML: + 00:14 +.Ed +.Pp +Since attributes are only emitted in XML, their use should be limited +to meta-data and additional or redundant representations of data +already emitted in other form. +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_attr.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_buf.h =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_buf.h (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_buf.h (revision 296962) @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2015, Juniper Networks, Inc. + * All rights reserved. + * This SOFTWARE is licensed under the LICENSE provided in the + * ../Copyright file. By downloading, installing, copying, or otherwise + * using the SOFTWARE, you agree to be bound by the terms of that + * LICENSE. + * Phil Shafer, August 2015 + */ + +/* + * This file is an _internal_ part of the libxo plumbing, not suitable + * for external use. It is not considered part of the libxo API and + * will not be a stable part of that API. Mine, not your's, dude... + * The real hope is that something like this will become a standard part + * of libc and I can kill this off. + */ + +#ifndef XO_BUF_H +#define XO_BUF_H + +#define XO_BUFSIZ (8*1024) /* Initial buffer size */ + +/* + * xo_buffer_t: a memory buffer that can be grown as needed. We + * use them for building format strings and output data. + */ +typedef struct xo_buffer_s { + char *xb_bufp; /* Buffer memory */ + char *xb_curp; /* Current insertion point */ + unsigned xb_size; /* Size of buffer */ +} xo_buffer_t; + +/* + * Initialize the contents of an xo_buffer_t. + */ +static inline void +xo_buf_init (xo_buffer_t *xbp) +{ + xbp->xb_size = XO_BUFSIZ; + xbp->xb_bufp = xo_realloc(NULL, xbp->xb_size); + xbp->xb_curp = xbp->xb_bufp; +} + +/* + * Reset the buffer to empty + */ +static inline void +xo_buf_reset (xo_buffer_t *xbp) +{ + xbp->xb_curp = xbp->xb_bufp; +} + +/* + * Return the number of bytes left in the buffer + */ +static inline int +xo_buf_left (xo_buffer_t *xbp) +{ + return xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); +} + +/* + * See if the buffer to empty + */ +static inline int +xo_buf_is_empty (xo_buffer_t *xbp) +{ + return (xbp->xb_curp == xbp->xb_bufp); +} + +/* + * Return the current offset + */ +static inline unsigned +xo_buf_offset (xo_buffer_t *xbp) +{ + return xbp ? (xbp->xb_curp - xbp->xb_bufp) : 0; +} + +static inline char * +xo_buf_data (xo_buffer_t *xbp, unsigned offset) +{ + if (xbp == NULL) + return NULL; + return xbp->xb_bufp + offset; +} + +static inline char * +xo_buf_cur (xo_buffer_t *xbp) +{ + if (xbp == NULL) + return NULL; + return xbp->xb_curp; +} + +/* + * Initialize the contents of an xo_buffer_t. + */ +static inline void +xo_buf_cleanup (xo_buffer_t *xbp) +{ + if (xbp->xb_bufp) + xo_free(xbp->xb_bufp); + bzero(xbp, sizeof(*xbp)); +} + +/* + * Does the buffer have room for the given number of bytes of data? + * If not, realloc the buffer to make room. If that fails, we + * return 0 to tell the caller they are in trouble. + */ +static inline int +xo_buf_has_room (xo_buffer_t *xbp, int len) +{ + if (xbp->xb_curp + len >= xbp->xb_bufp + xbp->xb_size) { + int sz = xbp->xb_size + XO_BUFSIZ; + char *bp = xo_realloc(xbp->xb_bufp, sz); + if (bp == NULL) + return 0; + + xbp->xb_curp = bp + (xbp->xb_curp - xbp->xb_bufp); + xbp->xb_bufp = bp; + xbp->xb_size = sz; + } + + return 1; +} + +/* + * Append the given string to the given buffer + */ +static inline void +xo_buf_append (xo_buffer_t *xbp, const char *str, int len) +{ + if (!xo_buf_has_room(xbp, len)) + return; + + memcpy(xbp->xb_curp, str, len); + xbp->xb_curp += len; +} + +/* + * Append the given NUL-terminated string to the given buffer + */ +static inline void +xo_buf_append_str (xo_buffer_t *xbp, const char *str) +{ + int len = strlen(str); + + if (!xo_buf_has_room(xbp, len)) + return; + + memcpy(xbp->xb_curp, str, len); + xbp->xb_curp += len; +} + +#endif /* XO_BUF_H */ Property changes on: vendor/Juniper/libxo/dist/libxo/xo_buf.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_config.h =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_config.h (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_config.h (revision 296962) @@ -0,0 +1,247 @@ +/* libxo/xo_config.h. Generated from xo_config.h.in by configure. */ +/* libxo/xo_config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP + systems. This function is required for `alloca.c' support on those systems. + */ +/* #undef CRAY_STACKSEG_END */ + +/* Define to 1 if using `alloca.c'. */ +/* #undef C_ALLOCA */ + +/* Define to 1 if you have `alloca', as a function or macro. */ +#define HAVE_ALLOCA 1 + +/* Define to 1 if you have and it should be used (not on Ultrix). + */ +/* #undef HAVE_ALLOCA_H */ + +/* Define to 1 if you have the `asprintf' function. */ +#define HAVE_ASPRINTF 1 + +/* Define to 1 if you have the `bzero' function. */ +#define HAVE_BZERO 1 + +/* Define to 1 if you have the `ctime' function. */ +#define HAVE_CTIME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_CTYPE_H 1 + +/* Define to 1 if you have the declaration of `__isthreaded', and to 0 if you + don't. */ +#define HAVE_DECL___ISTHREADED 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `dlfunc' function. */ +#define HAVE_DLFUNC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the `fdopen' function. */ +#define HAVE_FDOPEN 1 + +/* Define to 1 if you have the `flock' function. */ +#define HAVE_FLOCK 1 + +/* Define to 1 if you have the `getpass' function. */ +#define HAVE_GETPASS 1 + +/* Define to 1 if you have the `getprogname' function. */ +#define HAVE_GETPROGNAME 1 + +/* Define to 1 if you have the `getrusage' function. */ +#define HAVE_GETRUSAGE 1 + +/* gettext(3) */ +/* #undef HAVE_GETTEXT */ + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* humanize_number(3) */ +#define HAVE_HUMANIZE_NUMBER 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `crypto' library (-lcrypto). */ +#define HAVE_LIBCRYPTO 1 + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIBUTIL_H 1 + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Support printflike */ +/* #undef HAVE_PRINTFLIKE */ + +/* Define to 1 if your system has a GNU libc compatible `realloc' function, + and to 0 otherwise. */ +#define HAVE_REALLOC 1 + +/* Define to 1 if you have the `srand' function. */ +#define HAVE_SRAND 1 + +/* Define to 1 if you have the `sranddev' function. */ +#define HAVE_SRANDDEV 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STDIO_EXT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_STDTIME_TZFILE_H */ + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strcspn' function. */ +#define HAVE_STRCSPN 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcpy' function. */ +#define HAVE_STRLCPY 1 + +/* Define to 1 if you have the `strspn' function. */ +#define HAVE_STRSPN 1 + +/* Have struct sockaddr_un.sun_len */ +#define HAVE_SUN_LEN 1 + +/* Define to 1 if you have the `sysctlbyname' function. */ +#define HAVE_SYSCTLBYNAME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SYSCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_THREADS_H 1 + +/* thread-local setting */ +#define HAVE_THREAD_LOCAL THREAD_LOCAL_before + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_TZFILE_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `__flbf' function. */ +/* #undef HAVE___FLBF */ + +/* Enable debugging */ +/* #undef LIBXO_DEBUG */ + +/* Enable text-only rendering */ +/* #undef LIBXO_TEXT_ONLY */ + +/* Version number as dotted value */ +#define LIBXO_VERSION "0.4.3" + +/* Version number extra information */ +#define LIBXO_VERSION_EXTRA "" + +/* Version number as a number */ +#define LIBXO_VERSION_NUMBER 4003 + +/* Version number as string */ +#define LIBXO_VERSION_STRING "4003" + +/* Enable local wcwidth implementation */ +#define LIBXO_WCWIDTH 1 + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libxo" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "phil@juniper.net" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libxo" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libxo 0.4.3" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libxo" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.4.3" + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +/* #undef STACK_DIRECTION */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "0.4.3" + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ + +/* Define to rpl_realloc if the replacement function should be used. */ +/* #undef realloc */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ Property changes on: vendor/Juniper/libxo/dist/libxo/xo_config.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_create.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_create.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_create.3 (revision 296962) @@ -0,0 +1,67 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_create , xo_create_to_file , xo_destroy +.Nd create and destroy libxo output handles +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft xo_handle_t * +.Fn xo_create "unsigned style" "unsigned flags" +.Ft xo_handle_t * +.Fn xo_create_to_file "FILE *fp" "unsigned style" "unsigned flags" +.Ft void +.Fn xo_destroy "xo_handle_t *handle" +.Sh DESCRIPTION +A +.Nm libxo +handle can be allocated using the +.Fn xo_create +function. +.Bd -literal -offset indent + Example: + xo_handle_t *xop = xo_create(XO_STYLE_JSON, XOF_WARN); + .... + xo_emit_h(xop, "testing\n"); +.Ed +.Pp +By default, +.Nm libxo +writes output to standard output. +A convenience +function is provided for situations when output should be written to a +different file. +.Pp +Use the +.Dv XOF_CLOSE_FP +flag to trigger a call to +.Xr fclose 3 +for the +.Dv FILE +pointer when the handle is destroyed. +.Pp +The +.Fn xo_destroy +function releases a handle and any resources it is +using. +Calling +.Fn xo_destroy +with a +.Dv NULL +handle will release any +resources associated with the default handle. +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr xo_set_options 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_create.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_emit.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_emit.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_emit.3 (revision 296962) @@ -0,0 +1,104 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_emit , xo_emit_h , xo_emit_hv +.Nd emit formatted output based on format string and arguments +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft int +.Fn xo_emit "const char *fmt" "..." +.Ft int +.Fn xo_emit_h "xo_handle_t *xop" "const char *fmt" "..." +.Ft int +.Fn xo_emit_hv "xo_handle_t *xop" "const char *fmt" "va_list vap" +.Sh DESCRIPTION +The +.Fn xo_emit +function emits formatted output using the description in a format +string along with a set of zero or more arguments, in a style similar +to +.Xr printf 3 +but using a more complex format description string, as described in +.Xr xo_format 5 . +.Pp +.Fn xo_emit +uses the default output handle, as described in +.Xr libxo 3 , +where +.Fn xo_emit_h +uses an explicit handle. +.Fn xo_emit_hv +accepts a +.Fa va_list +for additional flexibility. +.Sh EXAMPLES +In this example, a set of four values is emitted using the following +source code: +.Bd -literal -offset indent + xo_emit(" {:lines/%7ju} {:words/%7ju} " + "{:characters/%7ju} {d:filename/%s}\n", + linect, wordct, charct, file); +.Ed +Output can then be generated in various style, using +the "--libxo" option: +.Bd -literal -offset indent + % wc /etc/motd + 25 165 1140 /etc/motd + % wc --libxo xml,pretty,warn /etc/motd + + + 25 + 165 + 1140 + /etc/motd + + + % wc --libxo json,pretty,warn /etc/motd + { + "wc": { + "file": [ + { + "lines": 25, + "words": 165, + "characters": 1140, + "filename": "/etc/motd" + } + ] + } + } + % wc --libxo html,pretty,warn /etc/motd +
+
+
25
+
+
165
+
+
1140
+
+
/etc/motd
+
+.Ed +.Sh RETURN CODE +.Nm +returns a negative value on error. If the +.Nm XOF_COLUMNS +flag has been turned on for the specific handle using +.Xr xo_set_flags 3 , +then the number of display columns consumed by the output will be returned. +.Sh SEE ALSO +.Xr xo_open_container 3 , +.Xr xo_open_list 3 , +.Xr xo_format 5 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_emit.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_emit_err.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_emit_err.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_emit_err.3 (revision 296962) @@ -0,0 +1,72 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_emit_err , xo_emit_errc , xo_emit_errx +.Nm xo_emit_warn , xo_emit_warnx , xo_emit_warn_c , xo_emit_warn_hc +.Nd emit errors and warnings in multiple output styles +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_emit_warn "const char *fmt" "..." +.Ft void +.Fn xo_emit_warnx "const char *fmt" "..." +.Ft void +.Fn xo_emit_warn_c "int code" "const char *fmt" "..." +.Ft void +.Fn xo_emit_warn_hc "xo_handle_t *xop" "int code, const char *fmt" "..." +.Ft void +.Fn xo_emit_err "int eval" "const char *fmt" "..." +.Ft void +.Fn xo_emit_errc "int eval" "int code" "const char *fmt" "..." +.Ft void +.Fn xo_emit_errx "int eval" "const char *fmt" "..." +.Sh DESCRIPTION +Many programs make use of the standard library functions +.Xr err 3 +and +.Xr warn 3 +to generate errors and warnings for the user. +.Nm libxo +wants to +pass that information via the current output style, and provides +compatible functions to allow this. +.Pp +The +.Fa fmt +argument is one compatible with +.Xr xo_emit 3 +which allows these functions make structured data. +To generate unstructured data, +use the +.Xr xo_err 3 +functions. +.Pp +These functions display the program name, a colon, a formatted message +based on the arguments, and then optionally a colon and an error +message associated with either +.Fa errno +or the +.Fa code +parameter. +.Bd -literal -offset indent + EXAMPLE: + if (open(filename, O_RDONLY) < 0) + xo_err(1, "cannot open file '%s'", filename); +.Ed +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr xo_format 5 , +.Xr xo_err 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_emit_err.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_encoder.c =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_encoder.c (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_encoder.c (revision 296962) @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2015, Juniper Networks, Inc. + * All rights reserved. + * This SOFTWARE is licensed under the LICENSE provided in the + * ../Copyright file. By downloading, installing, copying, or otherwise + * using the SOFTWARE, you agree to be bound by the terms of that + * LICENSE. + * Phil Shafer, August 2015 + */ + +/** + * libxo includes a number of fixed encoding styles. But other + * external encoders are need to deal with new encoders. Rather + * than expose a swarm of libxo internals, we create a distinct + * API, with a simpler API than we use internally. + */ + +#include +#include +#include +#include +#include +#include + +#include "xo_config.h" +#include "xo.h" +#include "xo_encoder.h" + +#ifdef HAVE_DLFCN_H +#include +#if !defined(HAVE_DLFUNC) +#define dlfunc(_p, _n) dlsym(_p, _n) +#endif +#else /* HAVE_DLFCN_H */ +#define dlopen(_n, _f) NULL /* Fail */ +#define dlsym(_p, _n) NULL /* Fail */ +#define dlfunc(_p, _n) NULL /* Fail */ +#endif /* HAVE_DLFCN_H */ + +static void xo_encoder_setup (void); /* Forward decl */ + +/* + * Need a simple string collection + */ +typedef struct xo_string_node_s { + TAILQ_ENTRY(xo_string_node_s) xs_link; /* Next string */ + char xs_data[0]; /* String data */ +} xo_string_node_t; + +typedef TAILQ_HEAD(xo_string_list_s, xo_string_node_s) xo_string_list_t; + +static inline void +xo_string_list_init (xo_string_list_t *listp) +{ + if (listp->tqh_last == NULL) + TAILQ_INIT(listp); +} + +static inline xo_string_node_t * +xo_string_add (xo_string_list_t *listp, const char *str) +{ + if (listp == NULL || str == NULL) + return NULL; + + xo_string_list_init(listp); + size_t len = strlen(str); + xo_string_node_t *xsp; + + xsp = xo_realloc(NULL, sizeof(*xsp) + len + 1); + if (xsp) { + memcpy(xsp->xs_data, str, len); + xsp->xs_data[len] = '\0'; + TAILQ_INSERT_TAIL(listp, xsp, xs_link); + } + + return xsp; +} + +#define XO_STRING_LIST_FOREACH(_xsp, _listp) \ + xo_string_list_init(_listp); \ + TAILQ_FOREACH(_xsp, _listp, xs_link) + +static inline void +xo_string_list_clean (xo_string_list_t *listp) +{ + xo_string_node_t *xsp; + + xo_string_list_init(listp); + + for (;;) { + xsp = TAILQ_FIRST(listp); + if (xsp == NULL) + break; + TAILQ_REMOVE(listp, xsp, xs_link); + xo_free(xsp); + } +} + +static xo_string_list_t xo_encoder_path; + +void +xo_encoder_path_add (const char *path) +{ + xo_encoder_setup(); + + if (path) + xo_string_add(&xo_encoder_path, path); +} + +/* ---------------------------------------------------------------------- */ + +typedef struct xo_encoder_node_s { + TAILQ_ENTRY(xo_encoder_node_s) xe_link; /* Next session */ + char *xe_name; /* Name for this encoder */ + xo_encoder_func_t xe_handler; /* Callback function */ + void *xe_dlhandle; /* dlopen handle */ +} xo_encoder_node_t; + +typedef TAILQ_HEAD(xo_encoder_list_s, xo_encoder_node_s) xo_encoder_list_t; + +#define XO_ENCODER_LIST_FOREACH(_xep, _listp) \ + xo_encoder_list_init(_listp); \ + TAILQ_FOREACH(_xep, _listp, xe_link) + +static xo_encoder_list_t xo_encoders; + +static void +xo_encoder_list_init (xo_encoder_list_t *listp) +{ + if (listp->tqh_last == NULL) + TAILQ_INIT(listp); +} + +static xo_encoder_node_t * +xo_encoder_list_add (const char *name) +{ + if (name == NULL) + return NULL; + + xo_encoder_node_t *xep = xo_realloc(NULL, sizeof(*xep)); + if (xep) { + int len = strlen(name) + 1; + xep->xe_name = xo_realloc(NULL, len); + if (xep->xe_name == NULL) { + xo_free(xep); + return NULL; + } + + memcpy(xep->xe_name, name, len); + + TAILQ_INSERT_TAIL(&xo_encoders, xep, xe_link); + } + + return xep; +} + +void +xo_encoders_clean (void) +{ + xo_encoder_node_t *xep; + + xo_encoder_setup(); + + for (;;) { + xep = TAILQ_FIRST(&xo_encoders); + if (xep == NULL) + break; + + TAILQ_REMOVE(&xo_encoders, xep, xe_link); + + if (xep->xe_dlhandle) + dlclose(xep->xe_dlhandle); + + xo_free(xep); + } + + xo_string_list_clean(&xo_encoder_path); +} + +static void +xo_encoder_setup (void) +{ + static int initted; + if (!initted) { + initted = 1; + + xo_string_list_init(&xo_encoder_path); + xo_encoder_list_init(&xo_encoders); + + xo_encoder_path_add(XO_ENCODERDIR); + } +} + +static xo_encoder_node_t * +xo_encoder_find (const char *name) +{ + xo_encoder_node_t *xep; + + xo_encoder_list_init(&xo_encoders); + + XO_ENCODER_LIST_FOREACH(xep, &xo_encoders) { + if (strcmp(xep->xe_name, name) == 0) + return xep; + } + + return NULL; +} + +static xo_encoder_node_t * +xo_encoder_discover (const char *name) +{ + void *dlp = NULL; + char buf[MAXPATHLEN]; + xo_string_node_t *xsp; + xo_encoder_node_t *xep = NULL; + + XO_STRING_LIST_FOREACH(xsp, &xo_encoder_path) { + static const char fmt[] = "%s/%s.enc"; + char *dir = xsp->xs_data; + size_t len = snprintf(buf, sizeof(buf), fmt, dir, name); + + if (len > sizeof(buf)) /* Should not occur */ + continue; + + dlp = dlopen((const char *) buf, RTLD_NOW); + if (dlp) + break; + } + + if (dlp) { + /* + * If the library exists, find the initializer function and + * call it. + */ + xo_encoder_init_func_t func; + + func = (xo_encoder_init_func_t) dlfunc(dlp, XO_ENCODER_INIT_NAME); + if (func) { + xo_encoder_init_args_t xei; + + bzero(&xei, sizeof(xei)); + + xei.xei_version = XO_ENCODER_VERSION; + int rc = func(&xei); + if (rc == 0 && xei.xei_handler) { + xep = xo_encoder_list_add(name); + if (xep) { + xep->xe_handler = xei.xei_handler; + xep->xe_dlhandle = dlp; + } + } + } + + if (xep == NULL) + dlclose(dlp); + } + + return xep; +} + +void +xo_encoder_register (const char *name, xo_encoder_func_t func) +{ + xo_encoder_setup(); + + xo_encoder_node_t *xep = xo_encoder_find(name); + + if (xep) /* "We alla-ready got one" */ + return; + + xep = xo_encoder_list_add(name); + if (xep) + xep->xe_handler = func; +} + +void +xo_encoder_unregister (const char *name) +{ + xo_encoder_setup(); + + xo_encoder_node_t *xep = xo_encoder_find(name); + if (xep) { + TAILQ_REMOVE(&xo_encoders, xep, xe_link); + xo_free(xep); + } +} + +int +xo_encoder_init (xo_handle_t *xop, const char *name) +{ + xo_encoder_setup(); + + /* Can't have names containing '/' or ':' */ + if (strchr(name, '/') != NULL || strchr(name, ':') != NULL) + return -1; + + /* + * First we look on the list of known (registered) encoders. + * If we don't find it, we follow the set of paths to find + * the encoding library. + */ + xo_encoder_node_t *xep = xo_encoder_find(name); + if (xep == NULL) { + xep = xo_encoder_discover(name); + if (xep == NULL) + return -1; + } + + xo_set_encoder(xop, xep->xe_handler); + + return xo_encoder_handle(xop, XO_OP_CREATE, NULL, NULL); +} + +/* + * A couple of function varieties here, to allow for multiple + * use cases. This varient is for when the main program knows + * its own encoder needs. + */ +xo_handle_t * +xo_encoder_create (const char *name, xo_xof_flags_t flags) +{ + xo_handle_t *xop; + + xop = xo_create(XO_STYLE_ENCODER, flags); + if (xop) { + if (xo_encoder_init(xop, name)) { + xo_destroy(xop); + xop = NULL; + } + } + + return xop; +} + +int +xo_encoder_handle (xo_handle_t *xop, xo_encoder_op_t op, + const char *name, const char *value) +{ + void *private = xo_get_private(xop); + xo_encoder_func_t func = xo_get_encoder(xop); + + if (func == NULL) + return -1; + + return func(xop, op, name, value, private); +} + +const char * +xo_encoder_op_name (xo_encoder_op_t op) +{ + static const char *names[] = { + /* 0 */ "unknown", + /* 1 */ "create", + /* 2 */ "open_container", + /* 3 */ "close_container", + /* 4 */ "open_list", + /* 5 */ "close_list", + /* 6 */ "open_leaf_list", + /* 7 */ "close_leaf_list", + /* 8 */ "open_instance", + /* 9 */ "close_instance", + /* 10 */ "string", + /* 11 */ "content", + /* 12 */ "finish", + /* 13 */ "flush", + /* 14 */ "destroy", + /* 15 */ "attr", + /* 16 */ "version", + }; + + if (op > sizeof(names) / sizeof(names[0])) + return "unknown"; + + return names[op]; +} Property changes on: vendor/Juniper/libxo/dist/libxo/xo_encoder.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_encoder.h =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_encoder.h (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_encoder.h (revision 296962) @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2015, Juniper Networks, Inc. + * All rights reserved. + * This SOFTWARE is licensed under the LICENSE provided in the + * ../Copyright file. By downloading, installing, copying, or otherwise + * using the SOFTWARE, you agree to be bound by the terms of that + * LICENSE. + * Phil Shafer, August 2015 + */ + +/* + * NOTE WELL: This file is needed to software that implements an + * external encoder for libxo that allows libxo data to be encoded in + * new and bizarre formats. General libxo code should _never_ + * include this header file. + */ + +#ifndef XO_ENCODER_H +#define XO_ENCODER_H + +/* + * Expose libxo's memory allocation functions + */ +extern xo_realloc_func_t xo_realloc; +extern xo_free_func_t xo_free; + +typedef unsigned xo_encoder_op_t; + +/* Encoder operations; names are in xo_encoder.c:xo_encoder_op_name() */ +#define XO_OP_UNKNOWN 0 +#define XO_OP_CREATE 1 /* Called when the handle is init'd */ +#define XO_OP_OPEN_CONTAINER 2 +#define XO_OP_CLOSE_CONTAINER 3 +#define XO_OP_OPEN_LIST 4 +#define XO_OP_CLOSE_LIST 5 +#define XO_OP_OPEN_LEAF_LIST 6 +#define XO_OP_CLOSE_LEAF_LIST 7 +#define XO_OP_OPEN_INSTANCE 8 +#define XO_OP_CLOSE_INSTANCE 9 +#define XO_OP_STRING 10 /* Quoted UTF-8 string */ +#define XO_OP_CONTENT 11 /* Other content */ +#define XO_OP_FINISH 12 /* Finish any pending output */ +#define XO_OP_FLUSH 13 /* Flush any buffered output */ +#define XO_OP_DESTROY 14 /* Clean up function */ +#define XO_OP_ATTRIBUTE 15 /* Attribute name/value */ +#define XO_OP_VERSION 16 /* Version string */ + +#define XO_ENCODER_HANDLER_ARGS \ + xo_handle_t *xop __attribute__ ((__unused__)), \ + xo_encoder_op_t op __attribute__ ((__unused__)), \ + const char *name __attribute__ ((__unused__)), \ + const char *value __attribute__ ((__unused__)), \ + void *private __attribute__ ((__unused__)) + +typedef int (*xo_encoder_func_t)(XO_ENCODER_HANDLER_ARGS); + +typedef struct xo_encoder_init_args_s { + unsigned xei_version; /* Current version */ + xo_encoder_func_t xei_handler; /* Encoding handler */ +} xo_encoder_init_args_t; + +#define XO_ENCODER_VERSION 1 /* Current version */ + +#define XO_ENCODER_INIT_ARGS \ + xo_encoder_init_args_t *arg __attribute__ ((__unused__)) + +typedef int (*xo_encoder_init_func_t)(XO_ENCODER_INIT_ARGS); +/* + * Each encoder library must define a function named xo_encoder_init + * that takes the arguments defined in XO_ENCODER_INIT_ARGS. It + * should return zero for success. + */ +#define XO_ENCODER_INIT_NAME_TOKEN xo_encoder_library_init +#define XO_STRINGIFY(_x) #_x +#define XO_STRINGIFY2(_x) XO_STRINGIFY(_x) +#define XO_ENCODER_INIT_NAME XO_STRINGIFY2(XO_ENCODER_INIT_NAME_TOKEN) +extern int XO_ENCODER_INIT_NAME_TOKEN (XO_ENCODER_INIT_ARGS); + +void +xo_encoder_register (const char *name, xo_encoder_func_t func); + +void +xo_encoder_unregister (const char *name); + +void * +xo_get_private (xo_handle_t *xop); + +void +xo_encoder_path_add (const char *path); + +void +xo_set_private (xo_handle_t *xop, void *opaque); + +xo_encoder_func_t +xo_get_encoder (xo_handle_t *xop); + +void +xo_set_encoder (xo_handle_t *xop, xo_encoder_func_t encoder); + +int +xo_encoder_init (xo_handle_t *xop, const char *name); + +xo_handle_t * +xo_encoder_create (const char *name, xo_xof_flags_t flags); + +int +xo_encoder_handle (xo_handle_t *xop, xo_encoder_op_t op, + const char *name, const char *value); + +void +xo_encoders_clean (void); + +const char * +xo_encoder_op_name (xo_encoder_op_t op); + +#endif /* XO_ENCODER_H */ Property changes on: vendor/Juniper/libxo/dist/libxo/xo_encoder.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_err.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_err.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_err.3 (revision 296962) @@ -0,0 +1,74 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_err , xo_errc , xo_errx +.Nm xo_warn , xo_warnx , xo_warn_c , xo_warn_hc +.Nd emit errors and warnings in multiple output styles +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_warn "const char *fmt" "..." +.Ft void +.Fn xo_warnx "const char *fmt" "..." +.Ft void +.Fn xo_warn_c "int code" "const char *fmt" "..." +.Ft void +.Fn xo_warn_hc "xo_handle_t *xop" "int code, const char *fmt" "..." +.Ft void +.Fn xo_err "int eval" "const char *fmt" "..." +.Ft void +.Fn xo_errc "int eval" "int code" "const char *fmt" "..." +.Ft void +.Fn xo_errx "int eval" "const char *fmt" "..." +.Sh DESCRIPTION +Many programs make use of the standard library functions +.Xr err 3 +and +.Xr warn 3 +to generate errors and warnings for the user. +.Nm libxo +wants to +pass that information via the current output style, and provides +compatible functions to allow this. +.Pp +The +.Fa fmt +argument is one compatible with +.Xr printf 3 +rather than +.Xr xo_emit 3 +to aid in simple conversion. This means +these functions make unstructured data. +To generate structured data, +use the +.Xr xo_emit_err 3 +functions. +.Pp +These functions display the program name, a colon, a formatted message +based on the arguments, and then optionally a colon and an error +message associated with either +.Fa errno +or the +.Fa code +parameter. +.Bd -literal -offset indent + EXAMPLE: + if (open(filename, O_RDONLY) < 0) + xo_err(1, "cannot open file '%s'", filename); +.Ed +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr xo_emit_err 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_err.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_error.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_error.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_error.3 (revision 296962) @@ -0,0 +1,41 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_error +.Nd generate simple error messages in multiple output styles +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_error "const char *fmt" "..." +.Sh DESCRIPTION +Use the +.Fn xo_error +function to generate error messages to standard error. +The +.Fa fmt +argument is a string containing printf-style formatting +instructions that describe the remaining arguments. +.Pp +When converting an application to +.Nm libxo , +one can replace +.Em "fprintf(stderr,...)" +calls with +.Fn xo_error +calls. +.Sh SEE ALSO +.Xr printf 3 , +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_error.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_finish.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_finish.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_finish.3 (revision 296962) @@ -0,0 +1,39 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_finish , xo_finish_h +.Nd finish formatting output +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_finish "void" +.Ft void +.Fn xo_finish_h "xo_handle_t *xop" +.Sh DESCRIPTION +When the program is ready to exit or close a handle, a call to +.Fn xo_finish +is required. +This flushes any buffered data, closes +open +.Nm libxo +constructs, and completes any pending operations. +.Pp +Calling this function is +.Em vital +to the proper operation of libxo, +especially for the non-TEXT output styles. +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_finish.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_flush.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_flush.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_flush.3 (revision 296962) @@ -0,0 +1,35 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_flush , xo_flush_h +.Nd flush formatted output from libxo handle +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_flush "void" +.Ft void +.Fn xo_flush_h "xo_handle_t *handle" +.Sh DESCRIPTION +.Nm libxo +buffers data, both for performance and consistency, but also to +allow some advanced features to work properly. +At various times, the +caller may wish to flush any data buffered within the library. +The +.Fn xo_flush +function is used for this. +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_flush.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_humanize.h =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_humanize.h (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_humanize.h (revision 296962) @@ -0,0 +1,169 @@ +/* $NetBSD: humanize_number.c,v 1.8 2004/07/27 01:56:24 enami Exp $ */ + +/* + * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* humanize_number(3) */ +#define HN_DECIMAL 0x01 +#define HN_NOSPACE 0x02 +#define HN_B 0x04 +#define HN_DIVISOR_1000 0x08 + +#define HN_GETSCALE 0x10 +#define HN_AUTOSCALE 0x20 + +static int +xo_humanize_number (char *buf, size_t len, int64_t bytes, + const char *suffix, int scale, int flags) +{ + const char *prefixes, *sep; + int b, i, r, maxscale, s1, s2, sign; + int64_t divisor, max; + // We multiply bytes by 100 to deal with rounding, so we need something + // big enough to hold LLONG_MAX * 100. On 64-bit we can use 128-bit wide + // integers with __int128_t, but on 32-bit we have to use long double. +#ifdef __LP64__ + __int128_t scalable = (__int128_t)bytes; +#else + long double scalable = (long double)bytes; +#endif + size_t baselen; + + assert(buf != NULL); + assert(suffix != NULL); + assert(scale >= 0); + + if (flags & HN_DIVISOR_1000) { + /* SI for decimal multiplies */ + divisor = 1000; + if (flags & HN_B) + prefixes = "B\0k\0M\0G\0T\0P\0E"; + else + prefixes = "\0\0k\0M\0G\0T\0P\0E"; + } else { + /* + * binary multiplies + * XXX IEC 60027-2 recommends Ki, Mi, Gi... + */ + divisor = 1024; + if (flags & HN_B) + prefixes = "B\0K\0M\0G\0T\0P\0E"; + else + prefixes = "\0\0K\0M\0G\0T\0P\0E"; + } + +#define SCALE2PREFIX(scale) (&prefixes[(scale) << 1]) + maxscale = 7; + + if (scale >= maxscale && + (scale & (HN_AUTOSCALE | HN_GETSCALE)) == 0) + return (-1); + + if (buf == NULL || suffix == NULL) + return (-1); + + if (len > 0) + buf[0] = '\0'; + if (bytes < 0) { + sign = -1; + scalable *= -100; + baselen = 3; /* sign, digit, prefix */ + } else { + sign = 1; + scalable *= 100; + baselen = 2; /* digit, prefix */ + } + if (flags & HN_NOSPACE) + sep = ""; + else { + sep = " "; + baselen++; + } + baselen += strlen(suffix); + + /* Check if enough room for `x y' + suffix + `\0' */ + if (len < baselen + 1) + return (-1); + + if (scale & (HN_AUTOSCALE | HN_GETSCALE)) { + /* See if there is additional columns can be used. */ + for (max = 100, i = len - baselen; i-- > 0;) + max *= 10; + + for (i = 0; scalable >= max && i < maxscale; i++) + scalable /= divisor; + + if (scale & HN_GETSCALE) + return (i); + } else + for (i = 0; i < scale && i < maxscale; i++) + scalable /= divisor; + + /* If a value <= 9.9 after rounding and ... */ + if (scalable < 995 && i > 0 && flags & HN_DECIMAL) { + /* baselen + \0 + .N */ + if (len < baselen + 1 + 2) + return (-1); + b = ((int)scalable + 5) / 10; + s1 = b / 10; + s2 = b % 10; + r = snprintf(buf, len, "%s%d%s%d%s%s%s", + ((sign == -1) ? "-" : ""), + s1, localeconv()->decimal_point, s2, + sep, SCALE2PREFIX(i), suffix); + } else + r = snprintf(buf, len, "%s%lld%s%s%s", + /* LONGLONG */ + ((sign == -1) ? "-" : ""), + (long long)((scalable + 50) / 100), + sep, SCALE2PREFIX(i), suffix); + + return (r); +} Property changes on: vendor/Juniper/libxo/dist/libxo/xo_humanize.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_message.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_message.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_message.3 (revision 296962) @@ -0,0 +1,68 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_message , xo_message_c , xo_message_hc , xo_message_hcv +.Nd emit messages in multiple output styles +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_message "const char *fmt" "..." +.Ft void +.Fn xo_message_e "const char *fmt" "..." +.Ft void +.Fn xo_message_c "int code" "const char *fmt" "..." +.Ft void +.Fn xo_message_hc "xo_handle_t *xop" "int code, const char *fmt" "..." +.Ft void +.Fn xo_message_hcv "xo_handle_t *xop" "int code" "const char *fmt" "va_list vap" +.Sh DESCRIPTION +.Nm xo_message +generates text message which lack any sort of structure. +These functions should not be used under normal conditions, since +they completely defeat the value of using libxo. They are provided +for scenarios when the output's content is genuinely unknown and +unusable. +It is used in converting programs where err/warn where not used, +and error messages went to +.Nm stdout , +not +.Nm stderr . +Use of +.Nm xo_message +allows backwards compatibility with that output, but does not put +the error in a useful form. +.Pp +The +.Nm xo_message +function generates output strings using the printf-style format string +and arguments provided. +If the format string does not end in a newline, +.Nm xo_message_e +will appear a colon, a space, and the error associated with the current +.Nm errno +value. +.Nm xo_message_c behaves similarly for the value passed in the +.Fa code +parameter. +.Nm xo_message_hc +accepts a +.Fa handle +as opened by +.Xr xo_create 3 +and +.Nm xo_message_hcv accepts a va_list parameter of arguments. +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_message.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_no_setlocale.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_no_setlocale.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_no_setlocale.3 (revision 296962) @@ -0,0 +1,43 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_no_setlocale +.Nd prevent implicit call to setlocale +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_no_setlocale "void" +.Sh DESCRIPTION +.Nm libxo +automatically initializes the locale based on the setting of the +environment variables +.Ev LC_CTYPE , +.Ev LANG , +and +.Ev LC_ALL . +The first of this +list of variables is used and if none of the variables are set, the locale +defaults to +.Em UTF-8 . +The caller may wish to avoid this behavior, and +can do so by calling the +.Fn xo_no_setlocale +function. +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr xo_open_container 3 , +.Xr xo_open_list 3 , +.Xr xo_format 5 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_no_setlocale.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_open_container.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_open_container.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_open_container.3 (revision 296962) @@ -0,0 +1,188 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_open_container , xo_open_container_h , xo_open_container_hd , xo_open_container_d +.Nm xo_close_container , xo_close_container_h , xo_close_container_hd , xo_close_container_d +.Nd open (and close) container constructs +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft int +.Fn xo_open_container "const char *name" +.Ft int +.Fn xo_open_container_h "xo_handle_t *handle" "const char *name" +.Ft int +.Fn xo_open_container_hd "xo_handle_t *handle" "const char *name" +.Ft int +.Fn xo_open_container_d "const char *name" +.Ft int +.Fn xo_close_container "const char *name" +.Ft int +.Fn xo_close_container_h "xo_handle_t *handle" "const char *name" +.Ft int +.Fn xo_close_container_hd "xo_handle_t *handle" +.Ft int +.Fn xo_close_container_d "void" +.Sh DESCRIPTION +.Nm libxo +represents two types of hierarchy: +.Dq containers +and +.Dq lists . +A container appears once under a given parent where a list contains +instances that can appear multiple times. +A container is used to hold +related fields and to give the data organization and scope. +The container has no value, but serves to +contain other nodes. +.Pp +To open a container, call +.Fn xo_open_container +or +.Fn xo_open_container_h . +The former uses the default handle and +the latter accepts a specific handle. +.Pp +To close a level, use the +.Fn xo_close_container +or +.Fn xo_close_container_h +functions. +.Pp +Each open call should have a matching close call. +If the +.Dv XOF_WARN +flag is set and the name given does not match the name of +the currently open +container, a warning will be generated. +.Bd -literal -offset indent -compact + Example: + + xo_open_container("top"); + xo_open_container("system"); + xo_emit("{:host-name/%s%s%s", hostname, + domainname ? "." : "", domainname ?: ""); + xo_close_container("system"); + xo_close_container("top"); + + Sample Output: + Text: + my-host.example.org + XML: + + + my-host.example.org + + + JSON: + "top" : { + "system" : { + "host-name": "my-host.example.org" + } + } + HTML: +
my-host.example.org
+.Ed +.Sh EMITTING HIERARCHY +To create a container, use the +.Fn xo_open_container +and +.Fn xo_close_container +set of functions. +The +.Fa handle +parameter contains a handle such as returned by +.Xr xo_create 3 +or +.Dv NULL +to use the default handle. +The +.Fa name +parameter gives the name of the container, encoded in +.Em UTF-8 . +Since +.Em ASCII +is a proper subset of +.Em UTF-8 , +traditional C strings can be used directly. +.Pp +The close functions with the +.Dq _d +suffix are used in +.Dq \&Do The Right Thing +mode, where the name of the open containers, lists, and +instances are maintained internally by +.Nm libxo +to allow the caller to +avoid keeping track of the open container name. +.Pp +Use the +.Dv XOF_WARN +flag to generate a warning if the name given on the +close does not match the current open container. +.Pp +For TEXT and HTML output, containers are not rendered into output +text, though for HTML they are used when the +.Dv XOF_XPATH +flag is set. +.Pp +.Bd -literal -offset indent -compact + EXAMPLE: + xo_open_container("system"); + xo_emit("The host name is {:host-name}\n", hn); + xo_close_container("system"); + XML: + foo +.Ed +.Sh DTRT MODE +Some users may find tracking the names of open containers, lists, and +instances inconvenient. +.Nm libxo +offers a +.Dq \&Do The Right Thing +mode, where +.Nm libxo +will track the names of open containers, lists, and instances so +the close function can be called without a name. +To enable +.Em DTRT +mode, +turn on the +.Dv XOF_DTRT +flag prior to making any other +.Nm libxo +output. +.Bd -literal -offset indent -compact + xo_set_flags(NULL, XOF_DTRT); +.Ed +Each open and close function has a version with the suffix +.Dq _d , +which will close the open container, list, or instance: +.Bd -literal -offset indent -compact + xo_open_container("top"); + ... + xo_close_container_d(); +.Ed +Note that the +.Dv XOF_WARN +flag will also cause +.Nm libxo +to track open +containers, lists, and instances. +A warning is generated when the name given to the close function +and the name recorded do not match. +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_open_container.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_open_list.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_open_list.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_open_list.3 (revision 296962) @@ -0,0 +1,158 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_open_list , xo_open_list_h , xo_open_list_hd , xo_open_list_d +.Nm xo_open_instance , xo_open_instance_h , xo_open_instance_hd , xo_open_instance_d +.Nm xo_close_instance , xo_close_instance_h , xo_close_instance_hd , xo_close_instnace_d +.Nm xo_close_list , xo_close_list_h , xo_close_list_hd , xo_close_list_d +.Nd open and close lists and instances +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft int +.Fn xo_open_list_h "xo_handle_t *xop" "const char *name" +.Ft int +.Fn xo_open_list "const char *name" +.Ft int +.Fn xo_open_list_hd "xo_handle_t *xop" "const char *name" +.Ft int +.Fn xo_open_list_d "const char *name" +.Ft int +.Fn xo_open_instance_h "xo_handle_t *xop" "const char *name" +.Ft int +.Fn xo_open_instance "const char *name" +.Ft int +.Fn xo_open_instance_hd "xo_handle_t *xop" "const char *name" +.Ft int +.Fn xo_open_instance_d "const char *name" +.Ft int +.Fn xo_close_instance_h "xo_handle_t *xop" "const char *name" +.Ft int +.Fn xo_close_instance "const char *name" +.Ft int +.Fn xo_close_instance_hd "xo_handle_t *xop" +.Ft int +.Fn xo_close_instance_d "void" +.Ft int +.Fn xo_close_list_h "xo_handle_t *xop" "const char *name" +.Ft int +.Fn xo_close_list "const char *name" +.Ft int +.Fn xo_close_list_hd "xo_handle_t *xop" +.Ft int +.Fn xo_close_list_d "void" +.Sh DESCRIPTION +Lists are sequences of instances of homogeneous data objects. +Two +distinct levels of calls are needed to represent them in our output +styles. +Calls must be made to open and close a list, and for each +instance of data in that list, calls must be make to open and close +that instance. +.Pp +The name given to all calls must be identical, and it is strongly +suggested that the name be singular, not plural, as a matter of +style and usage expectations. +.Pp +A list is a set of one or more instances that appear under the same +parent. +The instances contain details about a specific object. +One can think of instances as objects or records. +A call is needed to +open and close the list, while a distinct call is needed to open and +close each instance of the list: +.Bd -literal -offset indent -compact + xo_open_list("item"); + + for (ip = list; ip->i_title; ip++) { + xo_open_instance("item"); + xo_emit("{L:Item} '{:name/%s}':\n", ip->i_title); + xo_close_instance("item"); + } + + xo_close_list("item"); +.Ed +Getting the list and instance calls correct is critical to the proper +generation of XML and JSON data. +.Pp +.Bd -literal -offset indent -compact + EXAMPLE: + xo_open_list("user"); + for (i = 0; i < num_users; i++) { + xo_open_instance("user"); + xo_emit("{k:name}:{:uid/%u}:{:gid/%u}:{:home}\n", + pw[i].pw_name, pw[i].pw_uid, + pw[i].pw_gid, pw[i].pw_dir); + xo_close_instance("user"); + } + xo_close_list("user"); + TEXT: + phil:1001:1001:/home/phil + pallavi:1002:1002:/home/pallavi + XML: + + phil + 1001 + 1001 + /home/phil + + + pallavi + 1002 + 1002 + /home/pallavi + + JSON: + user: [ + { + "name": "phil", + "uid": 1001, + "gid": 1001, + "home": "/home/phil", + }, + { + "name": "pallavi", + "uid": 1002, + "gid": 1002, + "home": "/home/pallavi", + } + ] +.Ed +.Pp +.Sh LEAF LISTS +In contrast to a list of instances, a "leaf list" is list of simple +values. +To emit a leaf list, call the +.Fn xo_emit +function using the ""l"" modifier: +.Bd -literal -offset indent -compact + for (ip = list; ip->i_title; ip++) { + xo_emit("{Lwc:Item}{l:item}\n", ip->i_title); + } +.Ed +.Pp +The name of the field must match the name of the leaf list. +.Pp +In JSON, leaf lists are rendered as arrays of values. In XML, they +are rendered as multiple leaf elements. +.Bd -literal -offset indent -compact + JSON: + "item": "hammer", "nail" + XML: + hammer + nail +.Ed +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_open_list.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_open_marker.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_open_marker.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_open_marker.3 (revision 296962) @@ -0,0 +1,105 @@ +.\" # +.\" # Copyright (c) 2015, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, January 2015 +.\" +.Dd January 22, 2015 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_open_marker , xo_open_marker_h , xo_close_marker , xo_close_marker_h +.Nd prevent and allow closing of open constructs +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft int +.Fn xo_open_marker "const char *name" +.Ft int +.Fn xo_open_marker_h "xo_handle_t *handle" "const char *name" +.Ft int +.Fn xo_close_marker "const char *name" +.Ft int +.Fn xo_close_marker_h "xo_handle_t *handle" "const char *name" +.Sh DESCRIPTION +.Nm libxo +represents hierarchy using two constructs: +.Dq containers +and +.Dq lists . +A marker can be used to affect how open constructs are closed, either +by preventing their (implicit or explicit) closure or by forcing their +closure. +While a marker is open, no other open constructs can be closed. +When a marker is closed, all constructs open since the marker was opened +will be closed. +A marker is used to "freeze" any open constructs. +Calls to +.Fn xo_close_* +functions that would normally close them will be ignored, effectively +blocking their closure. +However when +.Fn xo_close_marker +is called, any containers, lists, or leaf-lists open since the +matching +.Fn xo_open_marker +call will be close and the marker discarded. +Markers use names which are not user-visible, allowing the caller to +choose appropriate internal names. +The marker has no value and is not emitted in any form. +.Pp +To open a marker, call +.Fn xo_open_marker +or +.Fn xo_open_marker_h . +The former uses the default handle and +the latter accepts a specific handle. +.Pp +To close a marker, use the +.Fn xo_close_marker +or +.Fn xo_close_marker_h +functions. +.Pp +Each open call must have a matching close call. +.Pp +In this example, the +.Fn xo_close_container +call on line [1] will be ignored, since the open marker "outer" +will prevent close of any open constructs that precede it. +The +.Fn xo_close_marker +call on line [2] will close the "system" container, since it was +opened after the "outer" marker. +.Bd -literal -offset indent -compact + Example: + + xo_open_container("top"); + xo_open_marker("outer"); + xo_open_container("system"); + xo_emit("{:host-name/%s%s%s", hostname, + domainname ? "." : "", domainname ?: ""); + xo_close_container("top"); /* [1] */ + xo_close_marker("outer"); /* [2] */ + xo_close_container("top"); +.Ed +.Pp +In this example, the code whiffles through a list of fish, calling a +function to emit details about each fish. The marker "fish-guts" is +used to ensure that any constructs opened by the function are closed +properly. +.Bd -literal -offset indent + for (i = 0; fish[i]; i++) { + xo_open_instance("fish"); + xo_open_marker("fish-guts"); + dump_fish_details(i); + xo_close_marker("fish-guts"); + } +.Ed +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_open_marker.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_parse_args.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_parse_args.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_parse_args.3 (revision 296962) @@ -0,0 +1,148 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_parse_args , xo_set_program +.Nd detect, parse, and remove arguments for libxo +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft int +.Fn xo_parse_args "int argc" "char **argv" +.Ft int +.Fn xo_set_program "const char *name" +.Sh DESCRIPTION +The +.Fn xo_parse_args +function is used to process command-line arguments. +.Nm libxo +specific +options are processed and removed +from the argument list so the calling application does not +need to process them. +If successful, a new value for argc is returned. +On failure, a message it emitted and -1 is returned. +.Bd -literal -offset indent + argc = xo_parse_args(argc, argv); + if (argc < 0) + exit(EXIT_FAILURE); +.Ed +.Pp +Following the call to +.Fn xo_parse_args , +the application can process the remaining arguments in a normal manner. +.Pp +.Nm libxo +uses command line options to trigger rendering behavior. +The following options are recognised: +.Pp +.Bl -tag -width "--libxo" +.It +\-\^\-libxo +.It +\-\^\-libxo= +.It +\-\^\-libxo: +.El +.Pp +Options is a comma-separated list of tokens that correspond to output +styles, flags, or features: +.Pp +.Bl -tag -width "12345678" +.It Sy "Token Action" +.It Dv dtrt +Enable "Do The Right Thing" mode +.It Dv html +Emit HTML output +.It Dv indent=xx +Set the indentation level +.It Dv info +Add info attributes (HTML) +.It Dv json +Emit JSON output +.It Dv keys +Emit the key attribute for keys (XML) +.It Dv log-gettext +Log (via stderr) each +.Xr gettext 3 +string lookup +.It Dv log-syslog +Log (via stderr) each syslog message (via +.Xr xo_syslog 3 ) +.If Dv no-humanize +Ignore the {h:} modifier (TEXT, HTML) +.It Dv no-locale +Do not initialize the locale setting +.It Dv no-top +Do not emit a top set of braces (JSON) +.It Dv not-first +Pretend the 1st output item was not 1st (JSON) +.It Dv pretty +Emit pretty-printed output +.It Dv text +Emit TEXT output +.If Dv underscores +Replace XML-friendly "-"s with JSON friendly "_"s e +.It Dv units +Add the 'units' (XML) or 'data-units (HTML) attribute +.It Dv warn +Emit warnings when libxo detects bad calls +.It Dv warn-xml +Emit warnings in XML +.It Dv xml +Emit XML output +.It Dv xpath +Add XPath expressions (HTML) +.El +.Pp +The +.Dq brief-options +are single letter commands, designed for those with +too little patience to use real tokens. +No comma separator is used. +.Bl -column "i" +.It Sy "Token Action" +.It "H " "Enable HTML output (XO_STYLE_HTML)" +.It "I " "Enable info output (XOF_INFO)" +.It "i " "Indent by " +.It "J " "Enable JSON output (XO_STYLE_JSON)" +.It "P " "Enable pretty-printed output (XOF_PRETTY)" +.It "T " "Enable text output (XO_STYLE_TEXT)" +.It "W " "Enable warnings (XOF_WARN)" +.It "X " "Enable XML output (XO_STYLE_XML)" +.It "x " "Enable XPath data (XOF_XPATH)" +.El +.Pp +The +.Fn xo_set_program +function sets name of the program as reported by +functions like +.Fn xo_failure , +.Fn xo_warn , +.Fn xo_err , +etc. +The program name is initialized by +.Fn xo_parse_args , +but subsequent calls to +.Fn xo_set_program +can override this value. +.Pp +Note that the value is not copied, so the memory passed to +.Fn xo_set_program +(and +.Fn xo_parse_args ) +must be maintained by the caller. +.Pp +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_parse_args.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_set_allocator.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_set_allocator.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_set_allocator.3 (revision 296962) @@ -0,0 +1,54 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_set_allocator +.Nd set allocation functions for libxo +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Sy typedef void *(*xo_realloc_func_t)(void *, size_t); +.Pp +.Sy typedef void (*xo_free_func_t)(void *); +.Ft void +.Fn xo_set_allocator "xo_realloc_func_t realloc_func" "xo_free_func_t free_func" +.Sh DESCRIPTION +The +.Fn xo_set_allocator +function allows +.Nm libxo +to be used in environments +where the standard +.Xr realloc 3 +and +.Xr free 3 +functions are not available. +.Pp +.Fa realloc_func +should expect the same arguments as +.Xr realloc 3 +and return +a pointer to memory following the same convention. +.Fa free_func +will receive the same argument as +.Xr free 3 +and should release it, as appropriate for the environment. +.Pp +By default, the standard +.Xr realloc 3 +and +.Xr free 3 +functions are used. +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_set_allocator.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_set_flags.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_set_flags.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_set_flags.3 (revision 296962) @@ -0,0 +1,139 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_set_flags , xo_clear_flags +.Nd set operational flags for a libxo handle +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_set_flags "xo_handle_t *handle" "unsigned flags" +.Ft void +.Fn xo_clear_flags "xo_handle_t *handle" "xo_xof_flags_t flags" +.Sh DESCRIPTION +Use the +.Fn xo_set_flags +function to set the flags for a +.Nm libxo +handle. +To use the default handle, pass a +.Dv NULL +handle. +.Pp +The set of valid flags include: +.Bl -tag -width "XOF_UNDERSCORES" +.It Sy "Flag Description" +.It Dv XOF_CLOSE_FP +Close file pointer on +.Xr xo_destroy 3 . +This flag will trigger the call of the +.Fn close_func +(provided via +.Xr xo_set_writer 3 ) +when the handle is destroyed. +.It Dv XOF_COLOR +Enable color and effects in output regardless of output device. +.It Dv XOF_COLOR_ALLOWED +Allow color and effects if the output device is a terminal. +.It Dv XOF_INFO +Display info data attributes (HTML) +.It Dv XOF_KEYS +Emit the key attribute (XML) +.It Dv XOF_LOG_GETTEXT +Log (via stderr) each +.Xr gettext 3 +string lookup +.It Dv XOF_LOG_SYSLOG +Log (via stderr) each syslog message (via +.Xr xo_syslog 3 ) +.It Dv XOF_NO_ENV +Do not use the +.Ev LIBXO_OPTIONS +environment variable. +.It Dv XOF_PRETTY +Make 'pretty printed' output, with the +addition of indentation and newlines to enhance the readability of +XML, JSON, and HTML output. +Text output is not affected. +.It Dv XOF_UNDERSCORES +Replaces hyphens with underscores +.It Dv XOF_UNITS +Display units (XML and HMTL) +.It Dv XOF_WARN +Generate warnings for broken calls, triggering diagnostic +output (on standard error) when the library notices errors during +operations, or with arguments to functions. +Without warnings enabled, such conditions are ignored. +Warnings allow developers to debug their interaction with +.Nm libxo . +The function +.Fn xo_failure +can be used as a breakpoint for a debugger, +regardless of whether warnings are enabled. +.It Dv XOF_WARN_XML +Generate warnings in XML on stdout +.It Dv XOF_XPATH +Emit XPath expressions (HTML) +.It Dv XOF_COLUMNS +Force +.Xr xo_emit 3 +to return columns used +.It Dv XOF_FLUSH +Flush output after each +.Xr xo_emit 3 +call +.El +.Pp +If the style is +.Dv XO_STYLE_HTML , +the following additional flags can be +used: +.Bl -tag -width "XOF_UNDERSCORES" +.It Sy "Flag Description" +.It Dv XOF_XPATH +Emit "data-xpath" attributes +.It Dv XOF_INFO +Emit additional informational fields for HTML +output. +See +.Xr xo_set_info 3 +for details. +.El +.Pp +The +.Dv XOF_XPATH +flag enables the emission of XPath expressions detailing +the hierarchy of XML elements used to encode the data field, if the +XPATH style of output were requested. +.Pp +If the style is +.Dv XO_STYLE_XML , +the following additional flags can be +used: +.Bl -tag -width "XOF_UNDERSCORES" +.It Sy "Flag Description" +.It XOF_KEYS +Add 'key' attribute to the XML encoding for +field definitions that use the 'k' modifier. +The key attribute has +the value "key". +.El +.Pp +The +.Fn xo_clear_flags +function turns off the given flags in a specific +handle. +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_set_flags.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_set_info.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_set_info.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_set_info.3 (revision 296962) @@ -0,0 +1,102 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_set_info +.Nd set the field information data for libxo +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_set_info "xo_handle_t *handle" "xo_info_t *info" "int count" +.Sh DESCRIPTION +HTML data can include additional information in attributes that +begin with "data-". +To enable this, three things must occur: +.Pp +First the application must build an array of +.Dv xo_info_t +structures, +one per tag. +The array must be sorted by name, since +.Nm libxo +uses a +binary search to find the entry that matches names from format +instructions. +.Pp +The +.Dv xo_info_t +structure is defined in +.In libxo/xo.h : +.Bd -literal -offset indent + typedef struct xo_info_s { + const char *xi_name; /* Name of the element */ + const char *xi_type; /* Type of field */ + const char *xi_help; /* Description of field */ + } xo_info_t; +.Ed +.Pp +Second, the application must inform +.Nm libxo +about this information using the +.Fn xo_set_info +call. +Like other +.Nm libxo +calls, passing +.Dv NULL +for the handle tells +.Nm libxo +to use the default handle. +.Pp +If the +.Fa count +is -1, +.Nm libxo +will count the elements of +.Fa info , +but there +must be an empty element at the end. +More typically, the number is +known to the application: +.Bd -literal -offset indent + xo_info_t info[] = { + { "in-stock", "number", "Number of items in stock" }, + { "name", "string", "Name of the item" }, + { "on-order", "number", "Number of items on order" }, + { "sku", "string", "Stock Keeping Unit" }, + { "sold", "number", "Number of items sold" }, + }; + int info_count = (sizeof(info) / sizeof(info[0])); + ... + xo_set_info(NULL, info, info_count); +.Ed +.Pp +Third, the emission of info must be triggered with the +.Dv XOF_INFO +flag +using either the +.Fn xo_set_flags +function or the +.Dq --libxo=info +command line argument. +.Pp +The type and help values, if present, are emitted as the "data-type" +and "data-help" attributes: +.Bd -literal -offset indent +
GRO-000-533
+.Ed +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_set_info.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_set_options.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_set_options.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_set_options.3 (revision 296962) @@ -0,0 +1,31 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_set_options +.Nd change options used by a libxo handle +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft int +.Fn xo_set_options "xo_handle_t *xop" "const char *input" +.Sh DESCRIPTION +The +.Fn xo_set_options +function accepts a comma-separated list of styles +and flags and enables them for a specific handle. +The options are identical to those listed in +.Xr xo_parse_args 3 . +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_set_options.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_set_style.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_set_style.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_set_style.3 (revision 296962) @@ -0,0 +1,53 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_set_style , xo_set_style_name +.Nd set the output style for a libxo handle +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_set_style "xo_handle_t *handle" "unsigned style" +.Ft int +.Fn xo_set_style_name "xo_handle_t *handle" "const char *style" +.Sh DESCRIPTION +Use the +.Fn xo_set_style +function to set the output style for a handle. +To use the default handle, pass a +.Dv NULL +handle. +The set of output styles used by +.Nm libxo +is: +.Bl -column "XO_STYLE_TEXT12" +.It Sy "Flag Description" +.It "XO_STYLE_TEXT Traditional text output" +.It "XO_STYLE_XML XML encoded data" +.It "XO_STYLE_JSON JSON encoded data" +.It "XO_STYLE_HTML HTML encoded data" +.El +.Pp +The +.Fn xo_set_style_name +function can be used to set the style based on a name +encoded as a string. +The name can be any of the styles: "text", "xml", "json", or "html". +.Bd -literal -offset indent + EXAMPLE: + xo_set_style_name(NULL, "html"); +.Ed +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_set_style.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_set_syslog_enterprise_id.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_set_syslog_enterprise_id.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_set_syslog_enterprise_id.3 (revision 296962) @@ -0,0 +1,36 @@ +.\" # +.\" # Copyright (c) 2015, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2015 +.\" +.Dd July 20, 2015 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_set_syslog_enterprise_id +.Nd Set the enterprise identifier for syslog content +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_set_syslog_enterprise_id "unsigned short eid" +.Ft void +.Sh DESCRIPTION +The +.Fn xo_set_syslog_enterprise_id +function records an enterprise identifier used for subsequent +.Xr xo_syslog 3 +calls. +Enterprise IDs are +defined by IANA, the Internet Assigned Numbers Authority: +.Bd -literal -offset indent +https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers +.Ed +.Sh SEE ALSO +.Xr xo_syslog 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_set_syslog_enterprise_id.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_set_version.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_set_version.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_set_version.3 (revision 296962) @@ -0,0 +1,34 @@ +.\" # +.\" # Copyright (c) 2015, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_set_version , xo_set_version_h +.Nd record content-version information in encoded output +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_set_version "const char *version" +.Ft void +.Fn xo_set_version_h "xo_handle_t *xop" "const char *version" +.Sh DESCRIPTION +The +.Nm xo_set_version +function records a version number to be emitted as +part of the data for encoding styles (XML and JSON). +This version number is suitable for tracking changes in the content, +allowing a user of the data to discern which version of the data model +is in use. +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_set_version.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_set_writer.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_set_writer.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_set_writer.3 (revision 296962) @@ -0,0 +1,56 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_set_writer +.Nd set custom writer functions for a libxo handle +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Sy typedef int (*xo_write_func_t)(void *, const char *); +.Pp +.Sy typedef void (*xo_close_func_t)(void *); +.Pp +.Sy typedef int (*xo_flush_func_t)(void *); +.Fn xo_set_writer "xo_handle_t *handle" "void *opaque" + "xo_write_func_t write_func" + "xo_close_func_t close_func" + "xo_flush_func_t flush_func" +.Sh DESCRIPTION +The +.Fn xo_set_writer +function allows custom +.Dq write +functions +which can tailor how +.Nm libxo +writes data. +An +.Fa opaque +argument is +recorded and passed back to the +.Fa write_func +function, allowing the function +to acquire context information. +The +.Fa close_func +function can +release this opaque data and any other resources as needed. +The +.Fa flush_func +function should +flush any pending data associated with the opaque pointer. +.Sh SEE ALSO +.Xr xo_emit 3 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_set_writer.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_syslog.3 =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_syslog.3 (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_syslog.3 (revision 296962) @@ -0,0 +1,79 @@ +.\" # +.\" # Copyright (c) 2015, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2015 +.\" +.Dd July 20, 2015 +.Dt LIBXO 3 +.Os +.Sh NAME +.Nm xo_syslog , xo_vsyslog , xo_open_log , xo_close_log , xo_set_logmask +.Nd create SYSLOG (RFC5424) log records using libxo formatting +.Sh LIBRARY +.Lb libxo +.Sh SYNOPSIS +.In libxo/xo.h +.Ft void +.Fn xo_syslog "int pri" "const char *name" "const char *fmt" "..." +.Ft void +.Fn xo_vsyslog "int pri" "const char *name" "const char *fmt" "va_list vap" +.Ft void +.Fn xo_close_log "void" +.Ft void +.Fn xo_open_log "const char *ident" "int logstat" "int logfac" +.Ft int +.Fn xo_set_logmask "int pmask" +.Sh DESCRIPTION +The +.Fn xo_syslog +function creates log entries following the standard defined in +RFC5424. +These messages are sent to the log +.Xr syslogd 8 +daemon, where they can be filtered, forwarded, and archived. +.Nm libxo +format strings are used to create both the message text and the +.Nm SD-PARAMS +content, containing name/value pairs that can be parsed by suitable +automation software. +.Pp +Refer to +.Xr xo_format 5 +for basic information about formatting strings. +.Nm xo_syslog +encodes all value fields at SD-PARAMS within the syslog message. +An exception is made for fields with the "{d:}" modifier; such fields +appear in the message text only, with fields with the "{e:}" modifier +appear as SD-PARAMS, but not in the message text. +.Pp +.Fn xo_vsyslog +accepts a +.Fa va_list +for additional flexibility. +.Pp +.Fn xo_open_log , +.Fn xo_close_log , and +.Fn xo_set_logmask +are all analogous to their libs counterparts, +.Xr openlog 3 , +.Xr closelog 3 , and +.Xr setlogmask 3 . +The extra underscores in the names are unfortunate, but keep +consistency in +.Nm libxo +function names. +.Sh EXAMPLES +.Bd -literal -offset indent + xo_syslog(LOG_LOCAL4 | LOG_NOTICE, "ID47", + "{e:iut/%u}An {:event-source} {:event-id/%u} log entry", + iut, source, id); +.Ed +.Sh SEE ALSO +.Xr xo_syslog 3 , +.Xr xo_set_syslog_enterprise_id 3 , +.Xr xo_format 5 , +.Xr libxo 3 Property changes on: vendor/Juniper/libxo/dist/libxo/xo_syslog.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/xo_syslog.c =================================================================== --- vendor/Juniper/libxo/dist/libxo/xo_syslog.c (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/xo_syslog.c (revision 296962) @@ -0,0 +1,706 @@ +/* + * Copyright (c) 2015, Juniper Networks, Inc. + * All rights reserved. + * This SOFTWARE is licensed under the LICENSE provided in the + * ../Copyright file. By downloading, installing, copying, or otherwise + * using the SOFTWARE, you agree to be bound by the terms of that + * LICENSE. + * Phil Shafer, June 2015 + */ + +/* + * Portions of this file are: + * Copyright (c) 1983, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xo_config.h" +#include "xo.h" +#include "xo_encoder.h" /* For xo_realloc */ +#include "xo_buf.h" + +/* + * SYSLOG (RFC 5424) requires an enterprise identifier. This turns + * out to be a fickle little issue. For a single-vendor box, the + * system should have a single EID that all software can use. When + * VendorX turns FreeBSD into a product, all software (kernel and + * utilities) should report VendorX's EID. But when software is + * installed on top of an external operating system, the application + * should report it's own EID, distinct from the base OS. + * + * To make this happen, the kernel should support a sysctl to assign a + * custom enterprise-id ("kern.syslog.enterprise_id"). libxo then + * allows an application to set a custom EID to override that system + * wide value, if needed. + * + * We try to set the stock IANA assigned Enterprise ID value for the + * vendors we know about (FreeBSD, macosx), but fallback to the + * "example" EID defined by IANA. See: + * https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers + */ + +#define XO_SYSLOG_ENTERPRISE_ID "kern.syslog.enterprise_id" + +#if defined(__FreeBSD__) +#define XO_DEFAULT_EID 2238 +#elif defined(__macosx__) +#define XO_DEFAULT_EID 63 +#else +#define XO_DEFAULT_EID 32473 /* Bail; use "example" number */ +#endif + +#ifdef _SC_HOST_NAME_MAX +#define HOST_NAME_MAX _SC_HOST_NAME_MAX +#else +#define HOST_NAME_MAX 255 +#endif /* _SC_HOST_NAME_MAX */ + +#ifndef UNUSED +#define UNUSED __attribute__ ((__unused__)) +#endif /* UNUSED */ + +static int xo_logfile = -1; /* fd for log */ +static int xo_status; /* connection xo_status */ +static int xo_opened; /* have done openlog() */ +static int xo_logstat = 0; /* xo_status bits, set by openlog() */ +static const char *xo_logtag = NULL; /* string to tag the entry with */ +static int xo_logfacility = LOG_USER; /* default facility code */ +static int xo_logmask = 0xff; /* mask of priorities to be logged */ +static pthread_mutex_t xo_syslog_mutex UNUSED = PTHREAD_MUTEX_INITIALIZER; +static int xo_unit_test; /* Fake data for unit test */ + +#define REAL_VOID(_x) \ + do { int really_ignored = _x; if (really_ignored) { }} while (0) + +#if !defined(HAVE_DECL___ISTHREADED) || !HAVE_DECL___ISTHREADED +#define __isthreaded 1 +#endif + +#define THREAD_LOCK() \ + do { \ + if (__isthreaded) pthread_mutex_lock(&xo_syslog_mutex); \ + } while(0) +#define THREAD_UNLOCK() \ + do { \ + if (__isthreaded) pthread_mutex_unlock(&xo_syslog_mutex); \ + } while(0) + +static void xo_disconnect_log(void); /* disconnect from syslogd */ +static void xo_connect_log(void); /* (re)connect to syslogd */ +static void xo_open_log_unlocked(const char *, int, int); + +enum { + NOCONN = 0, + CONNDEF, + CONNPRIV, +}; + +static xo_syslog_open_t xo_syslog_open; +static xo_syslog_send_t xo_syslog_send; +static xo_syslog_close_t xo_syslog_close; + +static char xo_syslog_enterprise_id[12]; + +/* + * Record an enterprise ID, which functions as a namespace for syslog + * messages. The value is pre-formatted into a string. This allows + * applications to customize their syslog message set, when needed. + */ +void +xo_set_syslog_enterprise_id (unsigned short eid) +{ + snprintf(xo_syslog_enterprise_id, sizeof(xo_syslog_enterprise_id), + "%u", eid); +} + +/* + * Handle the work of transmitting the syslog message + */ +static void +xo_send_syslog (char *full_msg, char *v0_hdr, + char *text_only) +{ + if (xo_syslog_send) { + xo_syslog_send(full_msg, v0_hdr, text_only); + return; + } + + int fd; + int full_len = strlen(full_msg); + + /* Output to stderr if requested. */ + if (xo_logstat & LOG_PERROR) { + struct iovec iov[3]; + struct iovec *v = iov; + char newline[] = "\n"; + + v->iov_base = v0_hdr; + v->iov_len = strlen(v0_hdr); + v += 1; + v->iov_base = text_only; + v->iov_len = strlen(text_only); + v += 1; + v->iov_base = newline; + v->iov_len = 1; + v += 1; + REAL_VOID(writev(STDERR_FILENO, iov, 3)); + } + + /* Get connected, output the message to the local logger. */ + if (!xo_opened) + xo_open_log_unlocked(xo_logtag, xo_logstat | LOG_NDELAY, 0); + xo_connect_log(); + + /* + * If the send() fails, there are two likely scenarios: + * 1) syslogd was restarted + * 2) /var/run/log is out of socket buffer space, which + * in most cases means local DoS. + * If the error does not indicate a full buffer, we address + * case #1 by attempting to reconnect to /var/run/log[priv] + * and resending the message once. + * + * If we are working with a privileged socket, the retry + * attempts end there, because we don't want to freeze a + * critical application like su(1) or sshd(8). + * + * Otherwise, we address case #2 by repeatedly retrying the + * send() to give syslogd a chance to empty its socket buffer. + */ + + if (send(xo_logfile, full_msg, full_len, 0) < 0) { + if (errno != ENOBUFS) { + /* + * Scenario 1: syslogd was restarted + * reconnect and resend once + */ + xo_disconnect_log(); + xo_connect_log(); + if (send(xo_logfile, full_msg, full_len, 0) >= 0) { + return; + } + /* + * if the resend failed, fall through to + * possible scenario 2 + */ + } + while (errno == ENOBUFS) { + /* + * Scenario 2: out of socket buffer space + * possible DoS, fail fast on a privileged + * socket + */ + if (xo_status == CONNPRIV) + break; + usleep(1); + if (send(xo_logfile, full_msg, full_len, 0) >= 0) { + return; + } + } + } else { + return; + } + + /* + * Output the message to the console; try not to block + * as a blocking console should not stop other processes. + * Make sure the error reported is the one from the syslogd failure. + */ + int flags = O_WRONLY | O_NONBLOCK; +#ifdef O_CLOEXEC + flags |= O_CLOEXEC; +#endif /* O_CLOEXEC */ + + if (xo_logstat & LOG_CONS + && (fd = open(_PATH_CONSOLE, flags, 0)) >= 0) { + struct iovec iov[2]; + struct iovec *v = iov; + char crnl[] = "\r\n"; + char *p; + + p = strchr(full_msg, '>') + 1; + v->iov_base = p; + v->iov_len = full_len - (p - full_msg); + ++v; + v->iov_base = crnl; + v->iov_len = 2; + REAL_VOID(writev(fd, iov, 2)); + (void) close(fd); + } +} + +/* Should be called with mutex acquired */ +static void +xo_disconnect_log (void) +{ + if (xo_syslog_close) { + xo_syslog_close(); + return; + } + + /* + * If the user closed the FD and opened another in the same slot, + * that's their problem. They should close it before calling on + * system services. + */ + if (xo_logfile != -1) { + close(xo_logfile); + xo_logfile = -1; + } + xo_status = NOCONN; /* retry connect */ +} + +/* Should be called with mutex acquired */ +static void +xo_connect_log (void) +{ + if (xo_syslog_open) { + xo_syslog_open(); + return; + } + + struct sockaddr_un saddr; /* AF_UNIX address of local logger */ + + if (xo_logfile == -1) { + int flags = SOCK_DGRAM; +#ifdef SOCK_CLOEXEC + flags |= SOCK_CLOEXEC; +#endif /* SOCK_CLOEXEC */ + if ((xo_logfile = socket(AF_UNIX, flags, 0)) == -1) + return; + } + if (xo_logfile != -1 && xo_status == NOCONN) { +#ifdef HAVE_SUN_LEN + saddr.sun_len = sizeof(saddr); +#endif /* HAVE_SUN_LEN */ + saddr.sun_family = AF_UNIX; + + /* + * First try privileged socket. If no success, + * then try default socket. + */ + +#ifdef _PATH_LOG_PRIV + (void) strncpy(saddr.sun_path, _PATH_LOG_PRIV, + sizeof saddr.sun_path); + if (connect(xo_logfile, (struct sockaddr *) &saddr, + sizeof(saddr)) != -1) + xo_status = CONNPRIV; +#endif /* _PATH_LOG_PRIV */ + +#ifdef _PATH_LOG + if (xo_status == NOCONN) { + (void) strncpy(saddr.sun_path, _PATH_LOG, + sizeof saddr.sun_path); + if (connect(xo_logfile, (struct sockaddr *)&saddr, + sizeof(saddr)) != -1) + xo_status = CONNDEF; + } +#endif /* _PATH_LOG */ + +#ifdef _PATH_OLDLOG + if (xo_status == NOCONN) { + /* + * Try the old "/dev/log" path, for backward + * compatibility. + */ + (void) strncpy(saddr.sun_path, _PATH_OLDLOG, + sizeof saddr.sun_path); + if (connect(xo_logfile, (struct sockaddr *)&saddr, + sizeof(saddr)) != -1) + xo_status = CONNDEF; + } +#endif /* _PATH_OLDLOG */ + + if (xo_status == NOCONN) { + (void) close(xo_logfile); + xo_logfile = -1; + } + } +} + +static void +xo_open_log_unlocked (const char *ident, int logstat, int logfac) +{ + if (ident != NULL) + xo_logtag = ident; + xo_logstat = logstat; + if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0) + xo_logfacility = logfac; + + if (xo_logstat & LOG_NDELAY) /* open immediately */ + xo_connect_log(); + + xo_opened = 1; /* ident and facility has been set */ +} + +void +xo_open_log (const char *ident, int logstat, int logfac) +{ + THREAD_LOCK(); + xo_open_log_unlocked(ident, logstat, logfac); + THREAD_UNLOCK(); +} + + +void +xo_close_log (void) +{ + THREAD_LOCK(); + if (xo_logfile != -1) { + (void) close(xo_logfile); + xo_logfile = -1; + } + xo_logtag = NULL; + xo_status = NOCONN; + THREAD_UNLOCK(); +} + +/* xo_set_logmask -- set the log mask level */ +int +xo_set_logmask (int pmask) +{ + int omask; + + THREAD_LOCK(); + omask = xo_logmask; + if (pmask != 0) + xo_logmask = pmask; + THREAD_UNLOCK(); + return (omask); +} + +void +xo_set_syslog_handler (xo_syslog_open_t open_func, + xo_syslog_send_t send_func, + xo_syslog_close_t close_func) +{ + xo_syslog_open = open_func; + xo_syslog_send = send_func; + xo_syslog_close = close_func; +} + +static size_t +xo_snprintf (char *out, size_t outsize, const char *fmt, ...) +{ + int status; + size_t retval = 0; + va_list ap; + if (out && outsize) { + va_start(ap, fmt); + status = vsnprintf(out, outsize, fmt, ap); + if (status < 0) { /* this should never happen, */ + *out = 0; /* handle it in the safest way possible if it does */ + retval = 0; + } else { + retval = status; + retval = retval > outsize ? outsize : retval; + } + va_end(ap); + } + return retval; +} + +static int +xo_syslog_handle_write (void *opaque, const char *data) +{ + xo_buffer_t *xbp = opaque; + int len = strlen(data); + int left = xo_buf_left(xbp); + + if (len > left - 1) + len = left - 1; + + memcpy(xbp->xb_curp, data, len); + xbp->xb_curp += len; + *xbp->xb_curp = '\0'; + + return len; +} + +static void +xo_syslog_handle_close (void *opaque UNUSED) +{ +} + +static int +xo_syslog_handle_flush (void *opaque UNUSED) +{ + return 0; +} + +void +xo_set_unit_test_mode (int value) +{ + xo_unit_test = value; +} + +void +xo_vsyslog (int pri, const char *name, const char *fmt, va_list vap) +{ + int saved_errno = errno; + char tbuf[2048]; + char *tp = NULL, *ep = NULL; + unsigned start_of_msg = 0; + char *v0_hdr = NULL; + xo_buffer_t xb; + static pid_t my_pid; + unsigned log_offset; + + if (my_pid == 0) + my_pid = xo_unit_test ? 222 : getpid(); + + /* Check for invalid bits */ + if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { + xo_syslog(LOG_ERR | LOG_CONS | LOG_PERROR | LOG_PID, + "syslog-unknown-priority", + "syslog: unknown facility/priority: %#x", pri); + pri &= LOG_PRIMASK|LOG_FACMASK; + } + + THREAD_LOCK(); + + /* Check priority against setlogmask values. */ + if (!(LOG_MASK(LOG_PRI(pri)) & xo_logmask)) { + THREAD_UNLOCK(); + return; + } + + /* Set default facility if none specified. */ + if ((pri & LOG_FACMASK) == 0) + pri |= xo_logfacility; + + /* Create the primary stdio hook */ + xb.xb_bufp = tbuf; + xb.xb_curp = tbuf; + xb.xb_size = sizeof(tbuf); + + xo_handle_t *xop = xo_create(XO_STYLE_SDPARAMS, 0); + if (xop == NULL) { + THREAD_UNLOCK(); + return; + } + +#ifdef HAVE_GETPROGNAME + if (xo_logtag == NULL) + xo_logtag = getprogname(); +#endif /* HAVE_GETPROGNAME */ + + xo_set_writer(xop, &xb, xo_syslog_handle_write, xo_syslog_handle_close, + xo_syslog_handle_flush); + + /* Build the message; start by getting the time */ + struct tm tm; + struct timeval tv; + + /* Unit test hack: fake a fixed time */ + if (xo_unit_test) { + tv.tv_sec = 1435085229; + tv.tv_usec = 123456; + } else + gettimeofday(&tv, NULL); + + (void) localtime_r(&tv.tv_sec, &tm); + + if (xo_logstat & LOG_PERROR) { + /* + * For backwards compatibility, we need to make the old-style + * message. This message can be emitted to the console/tty. + */ + v0_hdr = alloca(2048); + tp = v0_hdr; + ep = v0_hdr + 2048; + + if (xo_logtag != NULL) + tp += xo_snprintf(tp, ep - tp, "%s", xo_logtag); + if (xo_logstat & LOG_PID) + tp += xo_snprintf(tp, ep - tp, "[%d]", my_pid); + if (xo_logtag) + tp += xo_snprintf(tp, ep - tp, ": "); + } + + log_offset = xb.xb_curp - xb.xb_bufp; + + /* Add PRI, PRIVAL, and VERSION */ + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), "<%d>1 ", pri); + + /* Add TIMESTAMP with milliseconds and TZOFFSET */ + xb.xb_curp += strftime(xb.xb_curp, xo_buf_left(&xb), "%FT%T", &tm); + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), + ".%03.3u", tv.tv_usec / 1000); + xb.xb_curp += strftime(xb.xb_curp, xo_buf_left(&xb), "%z ", &tm); + + /* + * Add HOSTNAME; we rely on gethostname and don't fluff with + * ip addresses. Might need to revisit..... + */ + char hostname[HOST_NAME_MAX]; + hostname[0] = '\0'; + if (xo_unit_test) + strcpy(hostname, "worker-host"); + else + (void) gethostname(hostname, sizeof(hostname)); + + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), "%s ", + hostname[0] ? hostname : "-"); + + /* Add APP-NAME */ + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), "%s ", + xo_logtag ?: "-"); + + /* Add PROCID */ + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), "%d ", my_pid); + + /* + * Add MSGID. The user should provide us with a name, which we + * prefix with the current enterprise ID, as learned from the kernel. + * If the kernel won't tell us, we use the stock/builtin number. + */ + char *buf UNUSED = NULL; + const char *eid = xo_syslog_enterprise_id; + const char *at_sign = "@"; + + if (name == NULL) { + name = "-"; + eid = at_sign = ""; + + } else if (*name == '@') { + /* Our convention is to prefix IANA-defined names with an "@" */ + name += 1; + eid = at_sign = ""; + + } else if (eid[0] == '\0') { +#ifdef HAVE_SYSCTLBYNAME + /* + * See if the kernel knows the sysctl for the enterprise ID + */ + size_t size = 0; + if (sysctlbyname(XO_SYSLOG_ENTERPRISE_ID, NULL, &size, NULL, 0) == 0 + && size > 0) { + buf = alloca(size); + if (sysctlbyname(XO_SYSLOG_ENTERPRISE_ID, buf, &size, NULL, 0) == 0 + && size > 0) + eid = buf; + } +#endif /* HAVE_SYSCTLBYNAME */ + + if (eid[0] == '\0') { + /* Fallback to our base default */ + xo_set_syslog_enterprise_id(XO_DEFAULT_EID); + eid = xo_syslog_enterprise_id; + } + } + + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), "%s [%s%s%s ", + name, name, at_sign, eid); + + /* + * Now for the real content. We make two distinct passes thru the + * xo_emit engine, first for the SD-PARAMS and then for the text + * message. + */ + va_list ap; + va_copy(ap, vap); + + errno = saved_errno; /* Restore saved error value */ + xo_emit_hv(xop, fmt, ap); + xo_flush_h(xop); + + va_end(ap); + + /* Trim trailing space */ + if (xb.xb_curp[-1] == ' ') + xb.xb_curp -= 1; + + /* Close the structured data (SD-ELEMENT) */ + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), "] "); + + /* + * Since our MSG is known to be UTF-8, we MUST prefix it with + * that most-annoying-of-all-UTF-8 features, the BOM (0xEF.BB.BF). + */ + xb.xb_curp += xo_snprintf(xb.xb_curp, xo_buf_left(&xb), + "%c%c%c", 0xEF, 0xBB, 0xBF); + + /* Save the start of the message */ + if (xo_logstat & LOG_PERROR) + start_of_msg = xb.xb_curp - xb.xb_bufp; + + xo_set_style(xop, XO_STYLE_TEXT); + xo_set_flags(xop, XOF_UTF8); + + errno = saved_errno; /* Restore saved error value */ + xo_emit_hv(xop, fmt, ap); + xo_flush_h(xop); + + /* Remove a trailing newline */ + if (xb.xb_curp[-1] == '\n') + *--xb.xb_curp = '\0'; + + if (xo_get_flags(xop) & XOF_LOG_SYSLOG) + fprintf(stderr, "xo: syslog: %s\n", xb.xb_bufp + log_offset); + + xo_send_syslog(xb.xb_bufp, v0_hdr, xb.xb_bufp + start_of_msg); + + xo_destroy(xop); + + THREAD_UNLOCK(); +} + +/* + * syslog - print message on log file; output is intended for syslogd(8). + */ +void +xo_syslog (int pri, const char *name, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xo_vsyslog(pri, name, fmt, ap); + va_end(ap); +} Property changes on: vendor/Juniper/libxo/dist/libxo/xo_syslog.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/libxo/gen-wide.sh =================================================================== --- vendor/Juniper/libxo/dist/libxo/gen-wide.sh (nonexistent) +++ vendor/Juniper/libxo/dist/libxo/gen-wide.sh (revision 296962) @@ -0,0 +1,76 @@ +#!/bin/sh + +FILE=$1 + +SYMBOLS=" +xo_buffer_s +xo_buffer_t +xo_stack_s +xo_stack_t +xo_handle_s +xo_handle_t +xo_default_handle +xo_default_inited +xo_realloc +xo_free +xo_write_to_file +xo_close_file +xo_buf_init +xo_init_handle +xo_default_init +xo_buf_has_room +xo_printf +xo_escape_xml +xo_escape_json +xo_buf_append +xo_buf_escape +xo_data_append +xo_data_escape +xo_default +xo_indent +xo_warn +xo_create +xo_create_to_file +xo_destroy +xo_set_style +xo_set_flags +xo_set_info +xo_set_formatter +xo_clear_flags +xo_buf_indent +xo_line_ensure_open +xo_line_close +xo_info_compare +xo_info_find +xo_format_data +xo_buf_append_div +xo_format_text +xo_format_label +xo_format_title +xo_format_prep +xo_format_value +xo_format_decoration +xo_format_padding +xo_do_emit +xo_emit_hv +xo_emit_h +xo_emit +xo_attr_hv +xo_attr_h +xo_attr +xo_depth_change +xo_open_container_h +xo_open_container +xo_close_container_h +xo_close_container +xo_open_list_h +xo_open_list +xo_close_list_h +xo_close_list +xo_open_instance_h +xo_open_instance +xo_close_instance_h +xo_close_instance +xo_set_writer +xo_set_allocator +" Property changes on: vendor/Juniper/libxo/dist/libxo/gen-wide.sh ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/.travis.yml =================================================================== --- vendor/Juniper/libxo/dist/.travis.yml (nonexistent) +++ vendor/Juniper/libxo/dist/.travis.yml (revision 296962) @@ -0,0 +1,12 @@ +language: c + +script: printenv && uname -a && ls -l && /bin/sh -x ./bin/setup.sh && cd build && ../configure --enable-warnings && make && sudo make install && make test + +notifications: + recipients: + - libslax-noise@googlegroups.com + +branches: + only: + - master + - develop Index: vendor/Juniper/libxo/dist/INSTALL.md =================================================================== --- vendor/Juniper/libxo/dist/INSTALL.md (nonexistent) +++ vendor/Juniper/libxo/dist/INSTALL.md (revision 296962) @@ -0,0 +1,15 @@ + + +## Instructions for building libxo + +Instructions for building libxo are now available in the +[wiki](http://juniper.github.io/libxo/libxo-manual.html#getting-libxo). Index: vendor/Juniper/libxo/dist/Makefile.am =================================================================== --- vendor/Juniper/libxo/dist/Makefile.am (nonexistent) +++ vendor/Juniper/libxo/dist/Makefile.am (revision 296962) @@ -0,0 +1,102 @@ +# +# $Id$ +# +# Copyright 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = libxo xo xopo xolint xohtml tests doc encoder +bin_SCRIPTS=libxo-config +dist_doc_DATA = Copyright + +EXTRA_DIST = \ + libxo-config.in \ + warnings.mk \ + README.md \ + INSTALL.md \ + packaging/libxo.spec + +.PHONY: test tests + +test tests: + @(cd tests ; ${MAKE} test) + +errors: + @(cd tests/errors ; ${MAKE} test) + +docs: + @(cd doc ; ${MAKE} docs) + + +DIST_FILES_DIR = ~/Dropbox/dist-files/ +GH_PAGES_DIR = gh-pages/ +GH_PAGES_DIR_VER = gh-pages/${PACKAGE_VERSION} +PACKAGE_FILE = ${PACKAGE_TARNAME}-${PACKAGE_VERSION}.tar.gz + +upload: dist upload-docs + @echo "Remember to run:" + @echo " gt tag ${PACKAGE_VERSION}" + +upload-docs: docs + @echo "Uploading libxo-manual.html ... " + @-[ -d ${GH_PAGES_DIR} ] \ + && echo "Updating manual on gh-pages ..." \ + && mkdir -p ${GH_PAGES_DIR_VER} \ + && cp doc/libxo-manual.html ${GH_PAGES_DIR} \ + && cp doc/libxo-manual.html ${GH_PAGES_DIR_VER} \ + && (cd ${GH_PAGES_DIR} \ + && git add ${PACKAGE_VERSION} \ + && git add libxo-manual.html \ + && git commit -m 'new docs' \ + libxo-manual.html ${PACKAGE_VERSION} \ + && git push origin gh-pages ) ; true + +pkgconfigdir=$(libdir)/pkgconfig +pkgconfig_DATA = packaging/${PACKAGE_NAME}.pc + +get-wiki: + git clone https://github.com/Juniper/${PACKAGE_NAME}.wiki.git wiki + +get-gh-pages: + git clone https://github.com/Juniper/${PACKAGE_NAME}.git \ + gh-pages -b gh-pages + +UPDATE_PACKAGE_FILE = \ + -e "s;__SHA1__;$$SHA1;" \ + -e "s;__SHA256__;SHA256 (textproc/${PACKAGE_FILE}) = $$SHA256;" \ + -e "s;__SIZE__;SIZE (textproc/${PACKAGE_FILE}) = $$SIZE;" + +GH_PACKAGING_DIR = ${PACKAGE_VERSION}/packaging +GH_PAGES_PACKAGE_DIR = ${GH_PAGES_DIR}/${GH_PACKAGING_DIR} + +packages: + @-[ -d ${GH_PAGES_DIR} ] && set -x \ + && echo "Updating packages on gh-pages ..." \ + && SHA1="`openssl sha1 ${PACKAGE_FILE} | awk '{print $$2}'`" \ + && SHA256="`openssl sha256 ${PACKAGE_FILE} | awk '{print $$2}'`" \ + && SIZE="`ls -l ${PACKAGE_FILE} | awk '{print $$5}'`" \ + && echo "... ${GH_PAGES_PACKAGE_DIR}/${PACKAGE_NAME}.rb ..." \ + && sed ${UPDATE_PACKAGE_FILE} \ + packaging/${PACKAGE_NAME}.rb.base \ + > ${GH_PAGES_PACKAGE_DIR}/${PACKAGE_NAME}.rb \ + && echo "... ${GH_PAGES_PACKAGE_DIR}/${PACKAGE_NAME}.spec ..." \ + && cp packaging/${PACKAGE_NAME}.spec \ + ${GH_PAGES_PACKAGE_DIR}/${PACKAGE_NAME}.spec \ + && (cd ${GH_PAGES_DIR} \ + && git add ${GH_PACKAGING_DIR} \ + && git add ${GH_PACKAGING_DIR}/libxo.rb \ + ${GH_PACKAGING_DIR}/libxo.spec \ + && git commit -m 'new packaging data' \ + ${GH_PACKAGING_DIR} \ + && git push origin gh-pages ) ; true + +ANALYZE_DIR = ~/trash/libxo +ANALYZE_CMD = scan-build-mp-3.6 + +analyze: + ${ANALYZE_CMD} -o ${ANALYZE_DIR} ${MAKE} Property changes on: vendor/Juniper/libxo/dist/Makefile.am ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/bin/Zaliases =================================================================== --- vendor/Juniper/libxo/dist/bin/Zaliases (nonexistent) +++ vendor/Juniper/libxo/dist/bin/Zaliases (revision 296962) @@ -0,0 +1,29 @@ +set top_src=`pwd` +alias Zautoreconf "(cd $top_src ; autoreconf)" + +set opts=' \ +--with-libslax-prefix=/Users/phil/work/root \ +--enable-debug \ +--enable-warnings \ +--enable-printflike \ +--with-gettext=/opt/local \ +--prefix ${HOME}/work/root \ +' +set opts=`echo $opts` + +setenv CONFIGURE_OPTS "$opts" +setenv ADB_PATH $top_src/build/libxo/.libs + +alias Zconfigure "(cd $top_src/build; ../configure $opts)" +alias Zbuild "(cd $top_src/build; make \!* )" +alias mi "(cd $top_src/build; make && make install); ." + +mkdir -p build +cd build + + +alias xx 'cc -I.. -W -Wall -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Werror -Waggregate-return -Wcast-align -Wcast-qual -Wchar-subscripts -Wcomment -Wformat -Wimplicit -Wmissing-declarations -Wnested-externs -Wparentheses -Wreturn-type -Wshadow -Wswitch -Wtrigraphs -Wuninitialized -Wunused -Wwrite-strings -fno-inline-functions-called-once -g -O2 -o xtest -DUNIT_TEST libxo.c' + +alias mm "make CFLAGS='-O0 -g'" + +alias mmi 'mm && mi' Index: vendor/Juniper/libxo/dist/bin/setup.sh =================================================================== --- vendor/Juniper/libxo/dist/bin/setup.sh (nonexistent) +++ vendor/Juniper/libxo/dist/bin/setup.sh (revision 296962) @@ -0,0 +1,33 @@ +# +# Copyright 2013, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + + +if [ ! -f configure ]; then + vers=`autoreconf --version | head -1` + echo "Using" $vers + + mkdir -p m4 + + autoreconf --install + + if [ ! -f configure ]; then + echo "Failed to create configure script" + exit 1 + fi +fi + +echo "Creating build directory ..." +mkdir build + +echo "Setup is complete. To build libslax:" + +echo " 1) Type 'cd build ; ../configure' to configure libslax" +echo " 2) Type 'make' to build libslax" +echo " 3) Type 'make install' to install libslax" + +exit 0 Property changes on: vendor/Juniper/libxo/dist/bin/setup.sh ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/bin/Makefile.am =================================================================== --- vendor/Juniper/libxo/dist/bin/Makefile.am (nonexistent) +++ vendor/Juniper/libxo/dist/bin/Makefile.am (revision 296962) @@ -0,0 +1,29 @@ +# +# Copyright 2013, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +ACLOCAL_AMFLAGS = -I m4 + +EXTRA_DIST = gt setup.sh + +GT_INSTALL_DIR = ${prefix}/bin +GT_INSTALL_FILES = gt + +install-data-hook: + @echo "Installing gt ... " + @-mkdir -p ${GT_INSTALL_DIR} + @for file in ${GT_INSTALL_FILES} ; do \ + if [ -f $$file ]; then \ + rfile=$$file ; \ + else \ + rfile=${srcdir}/$$file ; \ + fi ; \ + mdir=${GT_INSTALL_DIR}/ ; \ + mkdir -p $$mdir ; \ + cp $$rfile $$mdir/ ; \ + done + @${CHMOD} a+x ${GT_INSTALL_DIR}/gt Property changes on: vendor/Juniper/libxo/dist/bin/Makefile.am ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/configure.ac =================================================================== --- vendor/Juniper/libxo/dist/configure.ac (nonexistent) +++ vendor/Juniper/libxo/dist/configure.ac (revision 296962) @@ -0,0 +1,452 @@ +# +# $Id$ +# +# See ./INSTALL for more info +# + +# +# Release numbering: even numbered dot releases are official ones, and +# odd numbers are development ones. The svn version of this file will +# only (ONLY!) ever (EVER!) contain odd numbers, so I'll always know if +# a particular user has the dist or svn release. +# + +AC_PREREQ(2.2) +AC_INIT([libxo], [0.4.5], [phil@juniper.net]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign -Wno-portability]) + +# Support silent build rules. Requires at least automake-1.11. +# Disable with "configure --disable-silent-rules" or "make V=1" +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AC_PROG_CC +AM_PROG_AR +AC_PROG_INSTALL +AC_CONFIG_MACRO_DIR([m4]) +AC_PROG_LN_S + +# Must be after AC_PROG_AR +LT_INIT([dlopen shared]) + +AC_PATH_PROG(BASENAME, basename, /usr/bin/basename) +AC_PATH_PROG(BISON, bison, /usr/bin/bison) +AC_PATH_PROG(CAT, cat, /bin/cat) +AC_PATH_PROG(CHMOD, chmod, /bin/chmod) +AC_PATH_PROG(CP, cp, /bin/cp) +AC_PATH_PROG(DIFF, diff, /usr/bin/diff) +AC_PATH_PROG(MKDIR, mkdir, /bin/mkdir) +AC_PATH_PROG(MV, mv, /bin/mv) +AC_PATH_PROG(RM, rm, /bin/rm) +AC_PATH_PROG(SED, sed, /bin/sed) + +AC_STDC_HEADERS + +# Checks for typedefs, structures, and compiler characteristics. +AC_C_INLINE +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_ALLOCA +AC_FUNC_MALLOC +AC_FUNC_REALLOC +AC_CHECK_FUNCS([bzero memmove strchr strcspn strerror strspn]) +AC_CHECK_FUNCS([sranddev srand strlcpy]) +AC_CHECK_FUNCS([fdopen getrusage]) +AC_CHECK_FUNCS([gettimeofday ctime]) +AC_CHECK_FUNCS([getpass]) +AC_CHECK_FUNCS([getprogname]) +AC_CHECK_FUNCS([sysctlbyname]) +AC_CHECK_FUNCS([flock]) +AC_CHECK_FUNCS([asprintf]) +AC_CHECK_FUNCS([__flbf]) +AC_CHECK_FUNCS([sysctlbyname]) + + +AC_CHECK_HEADERS([dlfcn.h]) +AC_CHECK_HEADERS([dlfcn.h]) +AC_CHECK_HEADERS([stdio_ext.h]) +AC_CHECK_HEADERS([tzfile.h]) +AC_CHECK_HEADERS([stdtime/tzfile.h]) +AC_CHECK_FUNCS([dlfunc]) + +AC_CHECK_HEADERS([sys/time.h]) +AC_CHECK_HEADERS([ctype.h errno.h stdio.h stdlib.h]) +AC_CHECK_HEADERS([string.h sys/param.h unistd.h ]) +AC_CHECK_HEADERS([sys/sysctl.h]) +AC_CHECK_HEADERS([threads.h]) + +dnl humanize_number(3) is a great function, but it's not standard. +dnl Note Macosx has the function in libutil.a but doesn't ship the +dnl header file, so I'll need to carry my own implementation. See: +dnl https://devforums.apple.com/thread/271121 +AC_CHECK_HEADERS([libutil.h]) +AC_CHECK_LIB([util], [humanize_number], + [HAVE_HUMANIZE_NUMBER=$ac_cv_header_libutil_h], + [HAVE_HUMANIZE_NUMBER=no]) + +AC_MSG_RESULT(humanize_number results: :${HAVE_HUMANIZE_NUMBER}:${ac_cv_header_libutil_h}:) + +if test "$HAVE_HUMANIZE_NUMBER" = "yes"; then + AC_DEFINE([HAVE_HUMANIZE_NUMBER], [1], [humanize_number(3)]) +fi + +AM_CONDITIONAL([HAVE_HUMANIZE_NUMBER], [test "$HAVE_HUMANIZE_NUMBER" = "yes"]) + +AC_ARG_ENABLE([gettext], + [ --disable-gettext Turn off support for gettext], + [GETTEXT_ENABLE=$enableval], + [GETTEXT_ENABLE=yes]) + +dnl Looking for gettext(), assumably in libintl +AC_ARG_WITH(gettext, + [ --with-gettext=[PFX] Specify location of gettext installation], + [GETTEXT_PREFIX=$withval], + [GETTEXT_PREFIX=/usr], +) + +HAVE_GETTEXT=no + +if test "$GETTEXT_ENABLE" != "no"; then + + AC_MSG_CHECKING([gettext in ${GETTEXT_PREFIX}]) + + _save_cflags="$CFLAGS" + CFLAGS="$CFLAGS -I${GETTEXT_PREFIX}/include -L${GETTEXT_PREFIX}/lib -Werror -lintl" + AC_LINK_IFELSE([AC_LANG_SOURCE([[#include ] + [int main() {char *cp = dgettext(NULL, "xx"); return 0; }]])], + [HAVE_GETTEXT=yes], + [HAVE_GETTEXT=no]) + CFLAGS="$_save_cflags" + + AC_MSG_RESULT([$HAVE_GETTEXT]) + + if test "$HAVE_GETTEXT" != "yes"; then + GETTEXT_PREFIX=/opt/local + AC_MSG_CHECKING([gettext in ${GETTEXT_PREFIX}]) + + _save_cflags="$CFLAGS" + CFLAGS="$CFLAGS -I${GETTEXT_PREFIX}/include -L${GETTEXT_PREFIX}/lib -Werror -lintl" + AC_LINK_IFELSE([AC_LANG_SOURCE([[#include ] + [int main() {char *cp = dgettext(NULL, "xx"); return 0; }]])], + [HAVE_GETTEXT=yes], + [HAVE_GETTEXT=no]) + CFLAGS="$_save_cflags" + + AC_MSG_RESULT([$HAVE_GETTEXT]) + fi +fi + +if test "$HAVE_GETTEXT" = "yes"; then + AC_DEFINE([HAVE_GETTEXT], [1], [gettext(3)]) + GETTEXT_CFLAGS="-I${GETTEXT_PREFIX}/include" + GETTEXT_LIBS="-L${GETTEXT_PREFIX}/lib -lintl" +else + GETTEXT_PREFIX=none + GETTEXT_CFLAGS= + GETTEXT_LIBS= +fi +AC_SUBST(GETTEXT_CFLAGS) +AC_SUBST(GETTEXT_LIBS) + +GETTEXT_BINDIR=${GETTEXT_PREFIX}/bin +AC_SUBST(GETTEXT_BINDIR) +GETTEXT_LIBDIR=${GETTEXT_PREFIX}/lib +AC_SUBST(GETTEXT_LIBDIR) + +AM_CONDITIONAL([HAVE_GETTEXT], [test "$HAVE_GETTEXT" = "yes"]) + +dnl Looking for how to do thread-local variables +AC_ARG_WITH(threads, + [ --with-threads=[STYLE] Specify style of thread-local support (none)], + [THREAD_LOCAL=$withval], + [THREAD_LOCAL=unknown], +) + +AC_MSG_CHECKING([thread-locals are ${THREAD_LOCAL}]) + +if test "$THREAD_LOCAL" = "unknown"; then + AC_LINK_IFELSE([AC_LANG_SOURCE([[] + [__thread int foo; int main() { foo++; return foo; }]])], + [THREAD_LOCAL=before], + [THREAD_LOCAL=unknown]) + + AC_MSG_RESULT([$THREAD_LOCAL]) +fi + +if test "$THREAD_LOCAL" = "unknown"; then + AC_LINK_IFELSE([AC_LANG_SOURCE([[] + [int __thread foo; int main() { foo++; return foo; }]])], + [THREAD_LOCAL=after], + [THREAD_LOCAL=unknown]) + AC_MSG_RESULT([$THREAD_LOCAL]) +fi + +if test "$THREAD_LOCAL" = "unknown"; then + AC_LINK_IFELSE([AC_LANG_SOURCE([[] + [__declspec(int) foo; int main() { foo++; return foo; }]])], + [THREAD_LOCAL=declspec], + [THREAD_LOCAL=unknown]) + AC_MSG_RESULT([$THREAD_LOCAL]) +fi + +if test "$THREAD_LOCAL" != "unknown"; then + AC_DEFINE_UNQUOTED([HAVE_THREAD_LOCAL], + THREAD_LOCAL_${THREAD_LOCAL}, [thread-local setting]) +fi + +dnl Looking for libcrypto.... +AC_CHECK_LIB([crypto], [MD5_Init]) +AM_CONDITIONAL([HAVE_LIBCRYPTO], [test "$HAVE_LIBCRYPTO" != "no"]) + +AC_CHECK_MEMBER([struct sockaddr_un.sun_len], + [HAVE_SUN_LEN=yes ; + AC_DEFINE([HAVE_SUN_LEN], [1], [Have struct sockaddr_un.sun_len])], + [HAS_SUN_LEN=no], [[#include ]]) + +AC_CHECK_DECLS([__isthreaded], [], [], [#include ]) +HAVE_ISTHREADED=${ac_cv_have_decl___isthreaded} + +dnl +dnl Some packages need to be checked against version numbers so we +dnl define a function here for later use +dnl +AC_DEFUN([VERSION_TO_NUMBER], +[`$1 | sed -e 's/lib.* //' | awk 'BEGIN { FS = "."; } { printf "%d", ([$]1 * 1000 + [$]2) * 1000 + [$]3;}'`]) + +LIBSLAX_CONFIG_PREFIX="" +LIBSLAX_SRC="" + +AC_ARG_WITH(libslax-prefix, + [ --with-libslax-prefix=[PFX] Specify location of libslax config], + LIBSLAX_CONFIG_PREFIX=$withval +) + +AC_MSG_CHECKING(for libslax) +if test "x$LIBSLAX_CONFIG_PREFIX" != "x" +then + SLAX_CONFIG=${LIBSLAX_CONFIG_PREFIX}/bin/slax-config +else + SLAX_CONFIG=slax-config +fi + +dnl +dnl make sure slax-config is executable, +dnl test version and init our variables +dnl + +if ${SLAX_CONFIG} --libs > /dev/null 2>&1 +then + LIBSLAX_VERSION=`$SLAX_CONFIG --version` + SLAX_BINDIR="`$SLAX_CONFIG --bindir | head -1`" + SLAX_OXTRADOCDIR="`$SLAX_CONFIG --oxtradoc | head -1`" + AC_MSG_RESULT($LIBSLAX_VERSION found) + HAVE_OXTRADOC=yes +else + LIBSLAX_VERSION= + SLAX_BINDIR= + SLAX_OXTRADOCDIR= + AC_MSG_RESULT([no]) + HAVE_OXTRADOC=no +fi +AM_CONDITIONAL([HAVE_OXTRADOC], [test "$HAVE_OXTRADOC" != "no"]) + +AC_SUBST(SLAX_BINDIR) +AC_SUBST(SLAX_OXTRADOCDIR) + +AC_MSG_CHECKING([whether to build with warnings]) +AC_ARG_ENABLE([warnings], + [ --enable-warnings Turn on compiler warnings], + [LIBXO_WARNINGS=$enableval], + [LIBXO_WARNINGS=no]) +AC_MSG_RESULT([$LIBXO_WARNINGS]) +AM_CONDITIONAL([LIBXO_WARNINGS_HIGH], [test "$LIBXO_WARNINGS" != "no"]) + +AC_MSG_CHECKING([whether to build with debugging]) +AC_ARG_ENABLE([debug], + [ --enable-debug Turn on debugging], + [LIBXO_DEBUG=yes; AC_DEFINE([LIBXO_DEBUG], [1], [Enable debugging])], + [LIBXO_DEBUG=no]) +AC_MSG_RESULT([$LIBXO_DEBUG]) +AM_CONDITIONAL([LIBXO_DEBUG], [test "$LIBXO_DEBUG" != "no"]) + +AC_MSG_CHECKING([whether to build with text-only rendering]) +AC_ARG_ENABLE([text-only], + [ --enable-text-only Turn on text-only rendering], + [LIBXO_TEXT_ONLY=yes; AC_DEFINE([LIBXO_TEXT_ONLY], [1], [Enable text-only rendering])], + [LIBXO_TEXT_ONLY=no]) +AC_MSG_RESULT([$LIBXO_TEXT_ONLY]) +AM_CONDITIONAL([LIBXO_TEXT_ONLY], [test "$LIBXO_TEXT_ONLY" != "no"]) + +AC_MSG_CHECKING([whether to build with local wcwidth implementation]) +AC_ARG_ENABLE([wcwidth], + [ --disable-wcwidth Disable local wcwidth implementation], + [LIBXO_WCWIDTH=$enableval], + [LIBXO_WCWIDTH=yes]) +AC_MSG_RESULT([$LIBXO_WCWIDTH]) +if test "${LIBXO_WCWIDTH}" != "no"; then + AC_DEFINE([LIBXO_WCWIDTH], [1], [Enable local wcwidth implementation]) +fi + +AC_CHECK_LIB([m], [lrint]) +AM_CONDITIONAL([HAVE_LIBM], [test "$HAVE_LIBM" != "no"]) + +AC_MSG_CHECKING([compiler for gcc]) +HAVE_GCC=no +if test "${CC}" != ""; then + HAVE_GCC=`${CC} --version 2>&1 | grep GCC` + if test "${HAVE_GCC}" != ""; then + HAVE_GCC=yes + else + HAVE_GCC=no + fi +fi +AC_MSG_RESULT([$HAVE_GCC]) +AM_CONDITIONAL([HAVE_GCC], [test "$HAVE_GCC" = "yes"]) + +AC_MSG_CHECKING([whether to build with printflike]) +AC_ARG_ENABLE([printflike], + [ --enable-printflike Enable use of GCC __printflike attribute], + [HAVE_PRINTFLIKE=yes; + AC_DEFINE([HAVE_PRINTFLIKE], [1], [Support printflike])], + [HAVE_PRINTFLIKE=no]) +AC_MSG_RESULT([$HAVE_PRINTFLIKE]) +AM_CONDITIONAL([HAVE_PRINTFLIKE], [test "$HAVE_PRINTFLIKE" != ""]) + +AC_MSG_CHECKING([whether to build with LIBXO_OPTIONS]) +AC_ARG_ENABLE([libxo-options], + [ --disable-libxo-options Turn off support for LIBXO_OPTIONS], + [LIBXO_OPTS=$enableval], + [LIBXO_OPTS=yes]) +AC_MSG_RESULT([$LIBXO_OPTS]) +AM_CONDITIONAL([NO_LIBXO_OPTIONS], [test "$LIBXO_OPTS" != "yes"]) + +case $host_os in + darwin*) + LIBTOOL=glibtool + XO_LIBEXT=dylib + ;; + Linux*|linux*) + CFLAGS="-D_GNU_SOURCE $CFLAGS" + LDFLAGS=-ldl + XO_LIBEXT=so + ;; + cygwin*|CYGWIN*) + LDFLAGS=-no-undefined + XO_LIBEXT=ddl + ;; +esac + +case $prefix in + NONE) + prefix=/usr/local + ;; +esac + +XO_LIBS=-lxo +XO_SRCDIR=${srcdir} +XO_LIBDIR=${libdir} +XO_BINDIR=${bindir} +XO_INCLUDEDIR=${includedir} + +AC_SUBST(XO_SRCDIR) +AC_SUBST(XO_LIBDIR) +AC_SUBST(XO_BINDIR) +AC_SUBST(XO_INCLUDEDIR) +AC_SUBST(XO_LIBEXT) + +AC_ARG_WITH(encoder-dir, + [ --with-encoder-dir=[DIR] Specify location of encoder libraries], + [XO_ENCODERDIR=$withval], + [XO_ENCODERDIR=$libdir/libxo/encoder] +) +AC_SUBST(XO_ENCODERDIR) + +AC_ARG_WITH(share-dir, + [ --with-share-dir=[DIR] Specify location of shared files], + [XO_SHAREDIR=$withval], + [XO_SHAREDIR=$datarootdir/libxo] +) +XO_SHAREDIR=`echo $XO_SHAREDIR | sed "s;\\${prefix};$prefix;"` +AC_SUBST(XO_SHAREDIR) + +dnl for the spec file +RELDATE=`date +'%Y-%m-%d%n'` +AC_SUBST(RELDATE) + +AC_MSG_RESULT(Using configure dir $ac_abs_confdir) + +if test -d $ac_abs_confdir/.git ; then + extra=`git branch | awk '/\*/ { print $2 }'` + if test "$extra" != "" -a "$extra" != "master" + then + LIBXO_VERSION_EXTRA="-git-$extra" + fi +fi + +LIBXO_VERSION=$PACKAGE_VERSION +LIBXO_VERSION_NUMBER=VERSION_TO_NUMBER(echo $PACKAGE_VERSION) +AC_SUBST(LIBXO_VERSION) +AC_SUBST(LIBXO_VERSION_NUMBER) +AC_SUBST(LIBXO_VERSION_EXTRA) + +AC_DEFINE_UNQUOTED(LIBXO_VERSION, ["$LIBXO_VERSION"], + [Version number as dotted value]) +AC_DEFINE_UNQUOTED(LIBXO_VERSION_NUMBER, [$LIBXO_VERSION_NUMBER], + [Version number as a number]) +AC_DEFINE_UNQUOTED(LIBXO_VERSION_STRING, ["$LIBXO_VERSION_NUMBER"], + [Version number as string]) +AC_DEFINE_UNQUOTED(LIBXO_VERSION_EXTRA, ["$LIBXO_VERSION_EXTRA"], + [Version number extra information]) + +AC_CONFIG_HEADERS([libxo/xo_config.h]) +AC_CONFIG_FILES([ + Makefile + libxo-config + xohtml/xohtml.sh + libxo/Makefile + libxo/add.man + encoder/Makefile + encoder/cbor/Makefile + encoder/test/Makefile + xo/Makefile + xolint/Makefile + xohtml/Makefile + xopo/Makefile + packaging/libxo.pc + doc/Makefile + tests/Makefile + tests/core/Makefile + tests/gettext/Makefile + tests/xo/Makefile + packaging/libxo.spec + packaging/libxo.rb.base +]) +AC_OUTPUT + +AC_MSG_NOTICE([summary of build options: + + libxo version: ${VERSION} ${LIBXO_VERSION_EXTRA} + host type: ${host} / ${host_os} + install prefix: ${prefix} + srcdir: ${XO_SRCDIR} + libdir: ${XO_LIBDIR} + bindir: ${XO_BINDIR} + includedir: ${XO_INCLUDEDIR} + share dir: ${XO_SHAREDIR} + extensions dir: ${XO_ENCODERDIR} + oxtradoc dir: ${SLAX_OXTRADOCDIR} + + compiler: ${CC} (${HAVE_GCC:-no}) + compiler flags: ${CFLAGS} + library types: Shared=${enable_shared}, Static=${enable_static} + + warnings: ${LIBXO_WARNINGS:-no} + debug: ${LIBXO_DEBUG:-no} + printf-like: ${HAVE_PRINTFLIKE:-no} + libxo-options: ${LIBXO_OPTS:-no} + text-only: ${LIBXO_TEXT_ONLY:-no} + gettext: ${HAVE_GETTEXT:-no} (${GETTEXT_PREFIX}) + isthreaded: ${HAVE_ISTHREADED:-no} + thread-local: ${THREAD_LOCAL:-no} + local wcwidth: ${LIBXO_WCWIDTH:-no} +]) Index: vendor/Juniper/libxo/dist/doc/Makefile.am =================================================================== --- vendor/Juniper/libxo/dist/doc/Makefile.am (nonexistent) +++ vendor/Juniper/libxo/dist/doc/Makefile.am (revision 296962) @@ -0,0 +1,70 @@ +# +# $Id$ +# +# Copyright 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +if HAVE_OXTRADOC +OXTRADOC_DIR = ${SLAX_OXTRADOCDIR} +OXTRADOC_PREFIX = ${OXTRADOC_DIR} +OXTRADOC = ${OXTRADOC_DIR}/oxtradoc +SLAXPROC_BINDIR = ${SLAX_BINDIR} + +XML2RFC = ${OXTRADOC_DIR}/xml2rfc.tcl +XML2HTMLDIR = ${OXTRADOC_DIR} +XML2HTMLBIN = ${XML2HTMLDIR}/rfc2629-to-html.slax +SLAXPROC = ${SLAX_BINDIR}/slaxproc + +SLAXPROC_ARGS = \ + -a oxtradoc-dir ${OXTRADOC_DIR} \ + -a oxtradoc-install-dir ${OXTRADOC_DIR} \ + -a anchor-prefix docs + +SLAXPROC_ARGS_INLINE = \ + -a oxtradoc-inline yes + +SLAXPROC_ARGS += ${SLAXPROC_ARGS_INLINE} + +XML2HTML = \ + ${SLAXPROC} -g -e -I ${OXTRADOC_DIR} -I . \ + ${SLAXPROC_ARGS} \ + ${XML2HTMLBIN} + +OX_ARGS = -P ${OXTRADOC_PREFIX} -L ${OXTRADOC_PREFIX} +OX_ARGS += -S ${SLAXPROC} -p doc +OX_CMD = ${PERL} ${PERLOPTS} ${OXTRADOC} ${OX_ARGS} +OXTRADOC_CMD = ${OX_CMD} + +OUTPUT = libxo-manual +INPUT = libxo + +EXTRA_DIST = \ + ${INPUT}.txt \ + ${OUTPUT}.html \ + ${OUTPUT}.txt + +doc docs: ${OUTPUT}.txt ${OUTPUT}.html + +${OUTPUT}.txt: ${INPUT}.txt ${OXTRADOC} xolint.txt + ${OXTRADOC_CMD} -m text -o $@ $< + +${OUTPUT}.html: ${INPUT}.txt ${OXTRADOC} ${XML2HTMLBIN} xolint.txt + ${OXTRADOC_CMD} -m html -o $@ $< + +xolint.txt: ${top_srcdir}/xolint/xolint.pl + perl ${top_srcdir}/xolint/xolint.pl -D > xolint.txt + +CLEANFILES = \ +xolint.txt \ +${INPUT}.xml \ +${INPUT}.txt \ +${INPUT}.fxml \ +${INPUT}.html +else +doc docs: + @${ECHO} "The 'oxtradoc' tool is not installed; see libslax.org" +endif Property changes on: vendor/Juniper/libxo/dist/doc/Makefile.am ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/doc/libxo.txt =================================================================== --- vendor/Juniper/libxo/dist/doc/libxo.txt (nonexistent) +++ vendor/Juniper/libxo/dist/doc/libxo.txt (revision 296962) @@ -0,0 +1,3757 @@ +# +# Copyright (c) 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. +# Phil Shafer, July 2014 +# + +* Overview + +libxo - A Library for Generating Text, XML, JSON, and HTML Output + +You want to prepare for the future, but you need to live in the +present. You'd love a flying car, but need to get to work today. You +want to support features like XML, JSON, and HTML rendering to allow +integration with NETCONF, REST, and web browsers, but you need to make +text output for command line users. And you don't want multiple code +paths that can't help but get out of sync. None of this "if (xml) +{... } else {...}" logic. And ifdefs are right out. But you'd +really, really like all the fancy features that modern encoding +formats can provide. libxo can help. + +The libxo library allows an application to generate text, XML, JSON, +and HTML output using a common set of function calls. The application +decides at run time which output style should be produced. The +application calls a function "xo_emit" to product output that is +described in a format string. A "field descriptor" tells libxo what +the field is and what it means. Each field descriptor is placed in +braces with a printf-like format string (^format-strings^): + + xo_emit(" {:lines/%7ju} {:words/%7ju} " + "{:characters/%7ju} {d:filename/%s}\n", + linect, wordct, charct, file); + +Each field can have a role, with the 'value' role being the default, +and the role tells libxo how and when to render that field. Output +can then be generated in various style, using the "--libxo" option: + + % wc /etc/motd + 25 165 1140 /etc/motd + % wc --libxo xml,pretty,warn /etc/motd + + + 25 + 165 + 1140 + /etc/motd + + + % wc --libxo json,pretty,warn /etc/motd + { + "wc": { + "file": [ + { + "lines": 25, + "words": 165, + "characters": 1140, + "filename": "/etc/motd" + } + ] + } + } + % wc --libxo html,pretty,warn /etc/motd +
+
+
25
+
+
165
+
+
1140
+
+
/etc/motd
+
+ +** Getting libxo + +libxo lives on github as: + + https://github.com/Juniper/libxo + +The latest release of libxo is available at: + + https://github.com/Juniper/libxo/releases + +We are following the branching scheme from +^http://nvie.com/posts/a-successful-git-branching-model/^ which means +we will do development under the "develop" branch, and release from +the "master" branch. To clone a developer tree, run the following +command: + + git clone https://github.com/Juniper/libxo.git -b develop + +We're using semantic release numbering, as defined in +^http://semver.org/spec/v2.0.0.html^. + +libxo is open source, distributed under the BSD license. It shipped +as part of the FreeBSD operating system starting with release 11.0. + +Issues, problems, and bugs should be directly to the issues page on +our github site. + +*** Downloading libxo Source Code + +You can retrieve the source for libxo in two ways: + +A) Use a "distfile" for a specific release. We use +github to maintain our releases. Visit +github release page (^https://github.com/Juniper/libxo/releases^) +to see the list of releases. To download the latest, look for the +release with the green "Latest release" button and the green +"libxo-RELEASE.tar.gz" button under that section. + +After downloading that release's distfile, untar it as follows: + + tar -zxf libxo-RELEASE.tar.gz + cd libxo-RELEASE + +[Note: for Solaris users, your "tar" command lacks the "-z" flag, +so you'll need to substitute "gzip -dc "file" | tar xf -" instead of +"tar -zxf "file"".] + +B) Use the current build from github. This gives you the most recent +source code, which might be less stable than a specific release. To +build libxo from the git repo: + + git clone https://github.com/Juniper/libxo.git + cd libxo + +_BE AWARE_: The github repository does _not_ contain the files +generated by "autoreconf", with the notable exception of the "m4" +directory. Since these files (depcomp, configure, missing, +install-sh, etc) are generated files, we keep them out of the source +code repository. + +This means that if you download the a release distfile, these files +will be ready and you'll just need to run "configure", but if you +download the source code from svn, then you'll need to run +"autoreconf" by hand. This step is done for you by the "setup.sh" +script, described in the next section. + +*** Building libxo + +To build libxo, you'll need to set up the build, run the "configure" +script, run the "make" command, and run the regression tests. + +The following is a summary of the commands needed. These commands are +explained in detail in the rest of this section. + + sh bin/setup.sh + cd build + ../configure + make + make test + sudo make install + +The following sections will walk thru each of these steps with +additional details and options, but the above directions should be all +that's needed. + +**** Setting up the build + +[If you downloaded a distfile, you can skip this step.] + +Run the "setup.sh" script to set up the build. This script runs the +"autoreconf" command to generate the "configure" script and other +generated files. + + sh bin/setup.sh + +Note: We're are currently using autoreconf version 2.69. + +**** Running the "configure" Script + +Configure (and autoconf in general) provides a means of building +software in diverse environments. Our configure script supports +a set of options that can be used to adjust to your operating +environment. Use "configure --help" to view these options. + +We use the "build" directory to keep object files and generated files +away from the source tree. + +To run the configure script, change into the "build" directory, and +run the "configure" script. Add any required options to the +"../configure" command line. + + cd build + ../configure + +Expect to see the "configure" script generate the following error: + + /usr/bin/rm: cannot remove `libtoolT': No such file or directory + +This error is harmless and can be safely ignored. + +By default, libxo installs architecture-independent files, including +extension library files, in the /usr/local directories. To specify an +installation prefix other than /usr/local for all installation files, +include the --prefix=prefix option and specify an alternate +location. To install just the extension library files in a different, +user-defined location, include the --with-extensions-dir=dir option +and specify the location where the extension libraries will live. + + cd build + ../configure [OPTION]... [VAR=VALUE]... + +**** Running the "make" command + +Once the "configure" script is run, build the images using the "make" +command: + + make + +**** Running the Regression Tests + +libxo includes a set of regression tests that can be run to ensure +the software is working properly. These test are optional, but will +help determine if there are any issues running libxo on your +machine. To run the regression tests: + + make test + +**** Installing libxo + +Once the software is built, you'll need to install libxo using the +"make install" command. If you are the root user, or the owner of the +installation directory, simply issue the command: + + make install + +If you are not the "root" user and are using the "sudo" package, use: + + sudo make install + +Verify the installation by viewing the output of "xo --version": + + % xo --version + libxo version 0.3.5-git-develop + xo version 0.3.5-git-develop + +* Formatting with libxo + +Most unix commands emit text output aimed at humans. It is designed +to be parsed and understood by a user. Humans are gifted at +extracting details and pattern matching in such output. Often +programmers need to extract information from this human-oriented +output. Programmers use tools like grep, awk, and regular expressions +to ferret out the pieces of information they need. Such solutions are +fragile and require maintenance when output contents change or evolve, +along with testing and validation. + +Modern tool developers favor encoding schemes like XML and JSON, +which allow trivial parsing and extraction of data. Such formats are +simple, well understood, hierarchical, easily parsed, and often +integrate easier with common tools and environments. Changes to +content can be done in ways that do not break existing users of the +data, which can reduce maintenance costs and increase feature velocity. + +In addition, modern reality means that more output ends up in web +browsers than in terminals, making HTML output valuable. + +libxo allows a single set of function calls in source code to generate +traditional text output, as well as XML and JSON formatted data. HTML +can also be generated; "
" elements surround the traditional text +output, with attributes that detail how to render the data. + +A single libxo function call in source code is all that's required: + + xo_emit("Connecting to {:host}.{:domain}...\n", host, domain); + + TEXT: + Connecting to my-box.example.com... + XML: + my-box + example.com + JSON: + "host": "my-box", + "domain": "example.com" + HTML: +
+
Connecting to
+
my-box
+
.
+
example.com
+
...
+
+ +** Encoding Styles + +There are four encoding styles supported by libxo: + +- TEXT output can be display on a terminal session, allowing +compatibility with traditional command line usage. +- XML output is suitable for tools like XPath and protocols like +NETCONF. +- JSON output can be used for RESTful APIs and integration with +languages like Javascript and Python. +- HTML can be matched with a small CSS file to permit rendering in any +HTML5 browser. + +In general, XML and JSON are suitable for encoding data, while TEXT is +suited for terminal output and HTML is suited for display in a web +browser (see ^xohtml^). + +*** Text Output + +Most traditional programs generate text output on standard output, +with contents like: + + 36 ./src + 40 ./bin + 90 . + +In this example (taken from du source code), the code to generate this +data might look like: + + printf("%d\t%s\n", num_blocks, path); + +Simple, direct, obvious. But it's only making text output. Imagine +using a single code path to make TEXT, XML, JSON or HTML, deciding at +run time which to generate. + +libxo expands on the idea of printf format strings to make a single +format containing instructions for creating multiple output styles: + + xo_emit("{:blocks/%d}\t{:path/%s}\n", num_blocks, path); + +This line will generate the same text output as the earlier printf +call, but also has enough information to generate XML, JSON, and HTML. + +The following sections introduce the other formats. + +*** XML Output + +XML output consists of a hierarchical set of elements, each encoded +with a start tag and an end tag. The element should be named for data +value that it is encoding: + + + 36 + ./src + + + 40 + ./bin + + + 90 + . + + +XML is a W3C standard for encoding data. See w3c.org/TR/xml for +additional information. + +*** JSON Output + +JSON output consists of a hierarchical set of objects and lists, each +encoded with a quoted name, a colon, and a value. If the value is a +string, it must be quoted, but numbers are not quoted. Objects are +encoded using braces; lists are encoded using square brackets. +Data inside objects and lists is separated using commas: + + items: [ + { "blocks": 36, "path" : "./src" }, + { "blocks": 40, "path" : "./bin" }, + { "blocks": 90, "path" : "./" } + ] + +*** HTML Output + +HTML output is designed to allow the output to be rendered in a web +browser with minimal effort. Each piece of output data is rendered +inside a
element, with a class name related to the role of the +data. By using a small set of class attribute values, a CSS +stylesheet can render the HTML into rich text that mirrors the +traditional text content. + +Additional attributes can be enabled to provide more details about the +data, including data type, description, and an XPath location. + +
+
36
+
+
./src
+
+
+
40
+
+
./bin
+
+
+
90
+
+
./
+
+ +** Format Strings @format-strings@ + +libxo uses format strings to control the rendering of data into the +various output styles. Each format string contains a set of zero or +more field descriptions, which describe independent data fields. Each +field description contains a set of modifiers, a content string, and +zero, one, or two format descriptors. The modifiers tell libxo what +the field is and how to treat it, while the format descriptors are +formatting instructions using printf-style format strings, telling +libxo how to format the field. The field description is placed inside +a set of braces, with a colon (":") after the modifiers and a slash +("/") before each format descriptors. Text may be intermixed with +field descriptions within the format string. + +The field description is given as follows: + + '{' [ role | modifier ]* [',' long-names ]* ':' [ content ] + [ '/' field-format [ '/' encoding-format ]] '}' + +The role describes the function of the field, while the modifiers +enable optional behaviors. The contents, field-format, and +encoding-format are used in varying ways, based on the role. These +are described in the following sections. + +In the following example, three field descriptors appear. The first +is a padding field containing three spaces of padding, the second is a +label ("In stock"), and the third is a value field ("in-stock"). The +in-stock field has a "%u" format that will parse the next argument +passed to the xo_emit function as an unsigned integer. + + xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\n", 65); + +This single line of code can generate text (" In stock: 65\n"), XML +("65"), JSON ('"in-stock": 6'), or HTML (too +lengthy to be listed here). + +While roles and modifiers typically use single character for brevity, +there are alternative names for each which allow more verbose +formatting strings. These names must be preceded by a comma, and may +follow any single-character values: + + xo_emit("{L,white,colon:In stock}{,key:in-stock/%u}\n", 65); + +*** Field Roles + +Field roles are optional, and indicate the role and formatting of the +content. The roles are listed below; only one role is permitted: + +|---+--------------+-------------------------------------------------| +| R | Name | Description | +|---+--------------+-------------------------------------------------| +| C | color | Field has color and effect controls | +| D | decoration | Field is non-text (e.g., colon, comma) | +| E | error | Field is an error message | +| G | gettext | Call gettext(3) on the format string | +| L | label | Field is text that prefixes a value | +| N | note | Field is text that follows a value | +| P | padding | Field is spaces needed for vertical alignment | +| T | title | Field is a title value for headings | +| U | units | Field is the units for the previous value field | +| V | value | Field is the name of field (the default) | +| W | warning | Field is a warning message | +| [ | start-anchor | Begin a section of anchored variable-width text | +| ] | stop-anchor | End a section of anchored variable-width text | +|---+--------------+-------------------------------------------------| + + EXAMPLE: + xo_emit("{L:Free}{D::}{P: }{:free/%u} {U:Blocks}\n", + free_blocks); + +When a role is not provided, the "value" role is used as the default. + +Roles and modifiers can also use more verbose names, when preceeded by +a comma: + + EXAMPLE: + xo_emit("{,label:Free}{,decoration::}{,padding: }" + "{,value:free/%u} {,units:Blocks}\n", + free_blocks); + +**** The Color Role ({C:}) @color-role@ + +Colors and effects control how text values are displayed; they are +used for display styles (TEXT and HTML). + + xo_emit("{C:bold}{:value}{C:no-bold}\n", value); + +Colors and effects remain in effect until modified by other "C"-role +fields. + + xo_emit("{C:bold}{C:inverse}both{C:no-bold}only inverse\n"); + +If the content is empty, the "reset" action is performed. + + xo_emit("{C:both,underline}{:value}{C:}\n", value); + +The content should be a comma-separated list of zero or more colors or +display effects. + + xo_emit("{C:bold,inverse}Ugly{C:no-bold,no-inverse}\n"); + +The color content can be either static, when placed directly within +the field descriptor, or a printf-style format descriptor can be used, +if preceded by a slash ("/"): + + xo_emit("{C:/%s%s}{:value}{C:}", need_bold ? "bold" : "", + need_underline ? "underline" : "", value); + +Color names are prefixed with either "fg-" or "bg-" to change the +foreground and background colors, respectively. + + xo_emit("{C:/fg-%s,bg-%s}{Lwc:Cost}{:cost/%u}{C:reset}\n", + fg_color, bg_color, cost); + +The following table lists the supported effects: + +|---------------+-------------------------------------------------| +| Name | Description | +|---------------+-------------------------------------------------| +| bg-XXXXX | Change background color | +| bold | Start bold text effect | +| fg-XXXXX | Change foreground color | +| inverse | Start inverse (aka reverse) text effect | +| no-bold | Stop bold text effect | +| no-inverse | Stop inverse (aka reverse) text effect | +| no-underline | Stop underline text effect | +| normal | Reset effects (only) | +| reset | Reset colors and effects (restore defaults) | +| underline | Start underline text effect | +|---------------+-------------------------------------------------| + +The following color names are supported: + +|---------+--------------------------------------------| +| Name | Description | +|---------+--------------------------------------------| +| black | | +| blue | | +| cyan | | +| default | Default color for foreground or background | +| green | | +| magenta | | +| red | | +| white | | +| yellow | | +|---------+--------------------------------------------| + +**** The Decoration Role ({D:}) + +Decorations are typically punctuation marks such as colons, +semi-colons, and commas used to decorate the text and make it simpler +for human readers. By marking these distinctly, HTML usage scenarios +can use CSS to direct their display parameters. + + xo_emit("{D:((}{:name}{D:))}\n", name); + +**** The Gettext Role ({G:}) @gettext-role@ + +libxo supports internationalization (i18n) through its use of +gettext(3). Use the "{G:}" role to request that the remaining part of +the format string, following the "{G:}" field, be handled using +gettext(). + +Since gettext() uses the string as the key into the message catalog, +libxo uses a simplified version of the format string that removes +unimportant field formatting and modifiers, stopping minor formatting +changes from impacting the expensive translation process. A developer +change such as changing "/%06d" to "/%08d" should not force hand +inspection of all .po files. + +The simplified version can be generated for a single message using the +"xopo -s " command, or an entire .pot can be translated using +the "xopo -f -o " command. + + xo_emit("{G:}Invalid token\n"); + +The {G:} role allows a domain name to be set. gettext calls will +continue to use that domain name until the current format string +processing is complete, enabling a library function to emit strings +using it's own catalog. The domain name can be either static as the +content of the field, or a format can be used to get the domain name +from the arguments. + + xo_emit("{G:libc}Service unavailable in restricted mode\n"); + +See ^howto-i18n^ for additional details. + +**** The Label Role ({L:}) + +Labels are text that appears before a value. + + xo_emit("{Lwc:Cost}{:cost/%u}\n", cost); + +**** The Note Role ({N:}) + +Notes are text that appears after a value. + + xo_emit("{:cost/%u} {N:per year}\n", cost); + +**** The Padding Role ({P:}) @padding-role@ + +Padding represents whitespace used before and between fields. + +The padding content can be either static, when placed directly within +the field descriptor, or a printf-style format descriptor can be used, +if preceded by a slash ("/"): + + xo_emit("{P: }{Lwc:Cost}{:cost/%u}\n", cost); + xo_emit("{P:/%30s}{Lwc:Cost}{:cost/%u}\n", "", cost); + +**** The Title Role ({T:}) + +Title are heading or column headers that are meant to be displayed to +the user. The title can be either static, when placed directly within +the field descriptor, or a printf-style format descriptor can be used, +if preceded by a slash ("/"): + + xo_emit("{T:Interface Statistics}\n"); + xo_emit("{T:/%20.20s}{T:/%6.6s}\n", "Item Name", "Cost"); + +Title fields have an extra convenience feature; if both content and +format are specified, instead of looking to the argument list for a +value, the content is used, allowing a mixture of format and content +within the field descriptor: + + xo_emit("{T:Name/%20s}{T:Count/%6s}\n"); + +Since the incoming argument is a string, the format must be "%s" or +something suitable. + +**** The Units Role ({U:}) + +Units are the dimension by which values are measured, such as degrees, +miles, bytes, and decibels. The units field carries this information +for the previous value field. + + xo_emit("{Lwc:Distance}{:distance/%u}{Uw:miles}\n", miles); + +Note that the sense of the 'w' modifier is reversed for units; +a blank is added before the contents, rather than after it. + +When the XOF_UNITS flag is set, units are rendered in XML as the +"units" attribute: + + 50 + +Units can also be rendered in HTML as the "data-units" attribute: + +
50
+ +**** The Value Role ({V:} and {:}) + +The value role is used to represent the a data value that is +interesting for the non-display output styles (XML and JSON). Value +is the default role; if no other role designation is given, the field +is a value. The field name must appear within the field descriptor, +followed by one or two format descriptors. The first format +descriptor is used for display styles (TEXT and HTML), while the +second one is used for encoding styles (XML and JSON). If no second +format is given, the encoding format defaults to the first format, +with any minimum width removed. If no first format is given, both +format descriptors default to "%s". + + xo_emit("{:length/%02u}x{:width/%02u}x{:height/%02u}\n", + length, width, height); + xo_emit("{:author} wrote \"{:poem}\" in {:year/%4d}\n, + author, poem, year); + +**** The Anchor Roles ({[:} and {]:}) @anchor-role@ + +The anchor roles allow a set of strings by be padded as a group, +but still be visible to xo_emit as distinct fields. Either the start +or stop anchor can give a field width and it can be either directly in +the descriptor or passed as an argument. Any fields between the start +and stop anchor are padded to meet the minimum width given. + +To give a width directly, encode it as the content of the anchor tag: + + xo_emit("({[:10}{:min/%d}/{:max/%d}{]:})\n", min, max); + +To pass a width as an argument, use "%d" as the format, which must +appear after the "/". Note that only "%d" is supported for widths. +Using any other value could ruin your day. + + xo_emit("({[:/%d}{:min/%d}/{:max/%d}{]:})\n", width, min, max); + +If the width is negative, padding will be added on the right, suitable +for left justification. Otherwise the padding will be added to the +left of the fields between the start and stop anchors, suitable for +right justification. If the width is zero, nothing happens. If the +number of columns of output between the start and stop anchors is less +than the absolute value of the given width, nothing happens. + +Widths over 8k are considered probable errors and not supported. If +XOF_WARN is set, a warning will be generated. + +*** Field Modifiers + +Field modifiers are flags which modify the way content emitted for +particular output styles: + +|---+---------------+-------------------------------------------------| +| M | Name | Description | +|---+---------------+-------------------------------------------------| +| c | colon | A colon (":") is appended after the label | +| d | display | Only emit field for display styles (text/HTML) | +| e | encoding | Only emit for encoding styles (XML/JSON) | +| g | gettext | Call gettext on field's render content | +| h | humanize (hn) | Format large numbers in human-readable style | +| | hn-space | Humanize: Place space between numeric and unit | +| | hn-decimal | Humanize: Add a decimal digit, if number < 10 | +| | hn-1000 | Humanize: Use 1000 as divisor instead of 1024 | +| k | key | Field is a key, suitable for XPath predicates | +| l | leaf-list | Field is a leaf-list | +| n | no-quotes | Do not quote the field when using JSON style | +| p | plural | Gettext: Use comma-separated plural form | +| q | quotes | Quote the field when using JSON style | +| t | trim | Trim leading and trailing whitespace | +| w | white | A blank (" ") is appended after the label | +|---+---------------+-------------------------------------------------| + +Roles and modifiers can also use more verbose names, when preceeded by +a comma. For example, the modifier string "Lwc" (or "L,white,colon") +means the field has a label role (text that describes the next field) +and should be followed by a colon ('c') and a space ('w'). The +modifier string "Vkq" (or ":key,quote") means the field has a value +role (the default role), that it is a key for the current instance, +and that the value should be quoted when encoded for JSON. + +**** The Colon Modifier ({c:}) + +The colon modifier appends a single colon to the data value: + + EXAMPLE: + xo_emit("{Lc:Name}{:name}\n", "phil"); + TEXT: + Name:phil + +The colon modifier is only used for the TEXT and HTML output +styles. It is commonly combined with the space modifier ('{w:}'). +It is purely a convenience feature. + +**** The Display Modifier ({d:}) + +The display modifier indicated the field should only be generated for +the display output styles, TEXT and HTML. + + EXAMPLE: + xo_emit("{Lcw:Name}{d:name} {:id/%d}\n", "phil", 1); + TEXT: + Name: phil 1 + XML: + 1 + +The display modifier is the opposite of the encoding modifier, and +they are often used to give to distinct views of the underlying data. + +**** The Encoding Modifier ({e:}) @e-modifier@ + +The display modifier indicated the field should only be generated for +the display output styles, TEXT and HTML. + + EXAMPLE: + xo_emit("{Lcw:Name}{:name} {e:id/%d}\n", "phil", 1); + TEXT: + Name: phil + XML: + phil1 + +The encoding modifier is the opposite of the display modifier, and +they are often used to give to distinct views of the underlying data. + +**** The Gettext Modifier ({g:}) @gettext-modifier@ + +The gettext modifier is used to translate individual fields using the +gettext domain (typically set using the "{G:}" role) and current +language settings. Once libxo renders the field value, it is passed +to gettext(3), where it is used as a key to find the native language +translation. + +In the following example, the strings "State" and "full" are passed +to gettext() to find locale-based translated strings. + + xo_emit("{Lgwc:State}{g:state}\n", "full"); + +See ^gettext-role^, ^plural-modifier^, and ^howto-i18n^ for additional +details. + +**** The Humanize Modifier ({h:}) + +The humanize modifier is used to render large numbers as in a +human-readable format. While numbers like "44470272" are completely +readable to computers and savants, humans will generally find "44M" +more meaningful. + +"hn" can be used as an alias for "humanize". + +The humanize modifier only affects display styles (TEXT and HMTL). +The "no-humanize" option (See ^LIBXO_OPTIONS^) will block the function of +the humanize modifier. + +There are a number of modifiers that affect details of humanization. +These are only available in as full names, not single characters. The +"hn-space" modifier places a space between the number and any +multiplier symbol, such as "M" or "K" (ex: "44 K"). The "hn-decimal" +modifier will add a decimal point and a single tenths digit when the number is +less than 10 (ex: "4.4K"). The "hn-1000" modifier will use 1000 as divisor +instead of 1024, following the JEDEC-standard instead of the more +natural binary powers-of-two tradition. + + EXAMPLE: + xo_emit("{h:input/%u}, {h,hn-space:output/%u}, " + "{h,hn-decimal:errors/%u}, {h,hn-1000:capacity/%u}, " + "{h,hn-decimal:remaining/%u}\n", + input, output, errors, capacity, remaining); + TEXT: + 21, 57 K, 96M, 44M, 1.2G + +In the HTML style, the original numeric value is rendered in the +"data-number" attribute on the
element: + +
96M
+ +**** The Key Modifier ({k:}) + +The key modifier is used to indicate that a particular field helps +uniquely identify an instance of list data. + + EXAMPLE: + xo_open_list("user"); + for (i = 0; i < num_users; i++) { + xo_open_instance("user"); + xo_emit("User {k:name} has {:count} tickets\n", + user[i].u_name, user[i].u_tickets); + xo_close_instance("user"); + } + xo_close_list("user"); + +Currently the key modifier is only used when generating XPath value +for the HTML output style when XOF_XPATH is set, but other uses are +likely in the near future. + +**** The Leaf-List Modifier ({l:}) + +The leaf-list modifier is used to distinguish lists where each +instance consists of only a single value. In XML, these are +rendered as single elements, where JSON renders them as arrays. + + EXAMPLE: + for (i = 0; i < num_users; i++) { + xo_emit("Member {l:user}\n", user[i].u_name); + } + XML: + phil + pallavi + JSON: + "user": [ "phil", "pallavi" ] + +The name of the field must match the name of the leaf list. + +**** The No-Quotes Modifier ({n:}) + +The no-quotes modifier (and its twin, the 'quotes' modifier) affect +the quoting of values in the JSON output style. JSON uses quotes for +string value, but no quotes for numeric, boolean, and null data. +xo_emit applies a simple heuristic to determine whether quotes are +needed, but often this needs to be controlled by the caller. + + EXAMPLE: + const char *bool = is_true ? "true" : "false"; + xo_emit("{n:fancy/%s}", bool); + JSON: + "fancy": true + +**** The Plural Modifier ({p:}) @plural-modifier@ + +The plural modifier selects the appropriate plural form of an +expression based on the most recent number emitted and the current +language settings. The contents of the field should be the singular +and plural English values, separated by a comma: + + xo_emit("{:bytes} {Ngp:byte,bytes}\n", bytes); + +The plural modifier is meant to work with the gettext modifier ({g:}) +but can work independently. See ^gettext-modifier^. + +When used without the gettext modifier or when the message does not +appear in the message catalog, the first token is chosen when the last +numeric value is equal to 1; otherwise the second value is used, +mimicking the simple pluralization rules of English. + +When used with the gettext modifier, the ngettext(3) function is +called to handle the heavy lifting, using the message catalog to +convert the singular and plural forms into the native language. + +**** The Quotes Modifier ({q:}) + +The quotes modifier (and its twin, the 'no-quotes' modifier) affect +the quoting of values in the JSON output style. JSON uses quotes for +string value, but no quotes for numeric, boolean, and null data. +xo_emit applies a simple heuristic to determine whether quotes are +needed, but often this needs to be controlled by the caller. + + EXAMPLE: + xo_emit("{q:time/%d}", 2014); + JSON: + "year": "2014" + +**** The White Space Modifier ({w:}) + +The white space modifier appends a single space to the data value: + + EXAMPLE: + xo_emit("{Lw:Name}{:name}\n", "phil"); + TEXT: + Name phil + +The white space modifier is only used for the TEXT and HTML output +styles. It is commonly combined with the colon modifier ('{c:}'). +It is purely a convenience feature. + +Note that the sense of the 'w' modifier is reversed for the units role +({Uw:}); a blank is added before the contents, rather than after it. + +*** Field Formatting + +The field format is similar to the format string for printf(3). Its +use varies based on the role of the field, but generally is used to +format the field's contents. + +If the format string is not provided for a value field, it defaults to +"%s". + +Note a field definition can contain zero or more printf-style +'directives', which are sequences that start with a '%' and end with +one of following characters: "diouxXDOUeEfFgGaAcCsSp". Each directive +is matched by one of more arguments to the xo_emit function. + +The format string has the form: + + '%' format-modifier * format-character + +The format- modifier can be: +- a '#' character, indicating the output value should be prefixed with +'0x', typically to indicate a base 16 (hex) value. +- a minus sign ('-'), indicating the output value should be padded on +the right instead of the left. +- a leading zero ('0') indicating the output value should be padded on the +left with zeroes instead of spaces (' '). +- one or more digits ('0' - '9') indicating the minimum width of the +argument. If the width in columns of the output value is less that +the minumum width, the value will be padded to reach the minimum. +- a period followed by one or more digits indicating the maximum +number of bytes which will be examined for a string argument, or the maximum +width for a non-string argument. When handling ASCII strings this +functions as the field width but for multi-byte characters, a single +character may be composed of multiple bytes. +xo_emit will never dereference memory beyond the given number of bytes. +- a second period followed by one or more digits indicating the maximum +width for a string argument. This modifier cannot be given for non-string +arguments. +- one or more 'h' characters, indicating shorter input data. +- one or more 'l' characters, indicating longer input data. +- a 'z' character, indicating a 'size_t' argument. +- a 't' character, indicating a 'ptrdiff_t' argument. +- a ' ' character, indicating a space should be emitted before +positive numbers. +- a '+' character, indicating sign should emitted before any number. + +Note that 'q', 'D', 'O', and 'U' are considered deprecated and will be +removed eventually. + +The format character is described in the following table: + +|-----+-----------------+----------------------| +| Ltr | Argument Type | Format | +|-----+-----------------+----------------------| +| d | int | base 10 (decimal) | +| i | int | base 10 (decimal) | +| o | int | base 8 (octal) | +| u | unsigned | base 10 (decimal) | +| x | unsigned | base 16 (hex) | +| X | unsigned long | base 16 (hex) | +| D | long | base 10 (decimal) | +| O | unsigned long | base 8 (octal) | +| U | unsigned long | base 10 (decimal) | +| e | double | [-]d.ddde+-dd | +| E | double | [-]d.dddE+-dd | +| f | double | [-]ddd.ddd | +| F | double | [-]ddd.ddd | +| g | double | as 'e' or 'f' | +| G | double | as 'E' or 'F' | +| a | double | [-]0xh.hhhp[+-]d | +| A | double | [-]0Xh.hhhp[+-]d | +| c | unsigned char | a character | +| C | wint_t | a character | +| s | char * | a UTF-8 string | +| S | wchar_t * | a unicode/WCS string | +| p | void * | '%#lx' | +|-----+-----------------+----------------------| + +The 'h' and 'l' modifiers affect the size and treatment of the +argument: + +|-----+-------------+--------------------| +| Mod | d, i | o, u, x, X | +|-----+-------------+--------------------| +| hh | signed char | unsigned char | +| h | short | unsigned short | +| l | long | unsigned long | +| ll | long long | unsigned long long | +| j | intmax_t | uintmax_t | +| t | ptrdiff_t | ptrdiff_t | +| z | size_t | size_t | +| q | quad_t | u_quad_t | +|-----+-------------+--------------------| + +*** UTF-8 and Locale Strings + +For strings, the 'h' and 'l' modifiers affect the interpretation of +the bytes pointed to argument. The default '%s' string is a 'char *' +pointer to a string encoded as UTF-8. Since UTF-8 is compatible with +ASCII data, a normal 7-bit ASCII string can be used. '%ls' expects a +'wchar_t *' pointer to a wide-character string, encoded as a 32-bit +Unicode values. '%hs' expects a 'char *' pointer to a multi-byte +string encoded with the current locale, as given by the LC_CTYPE, +LANG, or LC_ALL environment varibles. The first of this list of +variables is used and if none of the variables are set, the locale +defaults to "UTF-8". + +For example, a function is passed a locale-base name, a hat size, +and a time value. The hat size is formatted in a UTF-8 (ASCII) +string, and the time value is formatted into a wchar_t string. + + void print_order (const char *name, int size, + struct tm *timep) { + char buf[32]; + const char *size_val = "unknown"; + + if (size > 0) + snprintf(buf, sizeof(buf), "%d", size); + size_val = buf; + } + + wchar_t when[32]; + wcsftime(when, sizeof(when), L"%d%b%y", timep); + + xo_emit("The hat for {:name/%hs} is {:size/%s}.\n", + name, size_val); + xo_emit("It was ordered on {:order-time/%ls}.\n", + when); + } + +It is important to note that xo_emit will perform the conversion +required to make appropriate output. Text style output uses the +current locale (as described above), while XML, JSON, and HTML use +UTF-8. + +UTF-8 and locale-encoded strings can use multiple bytes to encode one +column of data. The traditional "precision'" (aka "max-width") value +for "%s" printf formatting becomes overloaded since it specifies both +the number of bytes that can be safely referenced and the maximum +number of columns to emit. xo_emit uses the precision as the former, +and adds a third value for specifying the maximum number of columns. + +In this example, the name field is printed with a minimum of 3 columns +and a maximum of 6. Up to ten bytes of data at the location given by +'name' are in used in filling those columns. + + xo_emit("{:name/%3.10.6s}", name); + +*** Characters Outside of Field Definitions + +Characters in the format string that are not part of a field +definition are copied to the output for the TEXT style, and are +ignored for the JSON and XML styles. For HTML, these characters are +placed in a
with class "text". + + EXAMPLE: + xo_emit("The hat is {:size/%s}.\n", size_val); + TEXT: + The hat is extra small. + XML: + extra small + JSON: + "size": "extra small" + HTML: +
The hat is
+
extra small
+
.
+ +*** "%m" Is Supported + +libxo supports the '%m' directive, which formats the error message +associated with the current value of "errno". It is the equivalent +of "%s" with the argument strerror(errno). + + xo_emit("{:filename} cannot be opened: {:error/%m}", filename); + xo_emit("{:filename} cannot be opened: {:error/%s}", + filename, strerror(errno)); + +*** "%n" Is Not Supported + +libxo does not support the '%n' directive. It's a bad idea and we +just don't do it. + +*** The Encoding Format (eformat) + +The "eformat" string is the format string used when encoding the field +for JSON and XML. If not provided, it defaults to the primary format +with any minimum width removed. If the primary is not given, both +default to "%s". + +*** Content Strings + +For padding and labels, the content string is considered the content, +unless a format is given. + +*** Argument Validation @printf-like@ + +Many compilers and tool chains support validation of printf-like +arguments. When the format string fails to match the argument list, +a warning is generated. This is a valuable feature and while the +formatting strings for libxo differ considerably from printf, many of +these checks can still provide build-time protection against bugs. + +libxo provide variants of functions that provide this ability, if the +"--enable-printflike" option is passed to the "configure" script. +These functions use the "_p" suffix, like "xo_emit_p()", +xo_emit_hp()", etc. + +The following are features of libxo formatting strings that are +incompatible with printf-like testing: + +- implicit formats, where "{:tag}" has an implicit "%s"; +- the "max" parameter for strings, where "{:tag/%4.10.6s}" means up to +ten bytes of data can be inspected to fill a minimum of 4 columns and +a maximum of 6; +- percent signs in strings, where "{:filled}%" makes a single, +trailing percent sign; +- the "l" and "h" modifiers for strings, where "{:tag/%hs}" means +locale-based string and "{:tag/%ls}" means a wide character string; +- distinct encoding formats, where "{:tag/#%s/%s}" means the display +styles (text and HTML) will use "#%s" where other styles use "%s"; + +If none of these features are in use by your code, then using the "_p" +variants might be wise. + +|------------------+------------------------| +| Function | printf-like Equivalent | +|------------------+------------------------| +| xo_emit_hv | xo_emit_hvp | +| xo_emit_h | xo_emit_hp | +| xo_emit | xo_emit_p | +| xo_emit_warn_hcv | xo_emit_warn_hcvp | +| xo_emit_warn_hc | xo_emit_warn_hcp | +| xo_emit_warn_c | xo_emit_warn_cp | +| xo_emit_warn | xo_emit_warn_p | +| xo_emit_warnx_ | xo_emit_warnx_p | +| xo_emit_err | xo_emit_err_p | +| xo_emit_errx | xo_emit_errx_p | +| xo_emit_errc | xo_emit_errc_p | +|------------------+------------------------| + +*** Example + +In this example, the value for the number of items in stock is emitted: + + xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\n", + instock); + +This call will generate the following output: + + TEXT: + In stock: 144 + XML: + 144 + JSON: + "in-stock": 144, + HTML: +
+
+
In stock
+
:
+
+
144
+
+ +Clearly HTML wins the verbosity award, and this output does +not include XOF_XPATH or XOF_INFO data, which would expand the +penultimate line to: + +
144
+ +** Command-line Arguments + +libxo uses command line options to trigger rendering behavior. The +following options are recognised: + +- --libxo +- --libxo= +- --libxo: + +Options is a comma-separated list of tokens that correspond to output +styles, flags, or features: + +|-------------+-------------------------------------------------------| +| Token | Action | +|-------------+-------------------------------------------------------| +| color | Enable colors/effects for display styles (TEXT, HTML) | +| dtrt | Enable "Do The Right Thing" mode | +| html | Emit HTML output | +| indent=xx | Set the indentation level | +| info | Add info attributes (HTML) | +| json | Emit JSON output | +| keys | Emit the key attribute for keys (XML) | +| log-gettext | Log (via stderr) each gettext(3) string lookup | +| log-syslog | Log (via stderr) each syslog message (via xo_syslog) | +| no-humanize | Ignore the {h:} modifier (TEXT, HTML) | +| no-locale | Do not initialize the locale setting | +| no-top | Do not emit a top set of braces (JSON) | +| not-first | Pretend the 1st output item was not 1st (JSON) | +| pretty | Emit pretty-printed output | +| text | Emit TEXT output | +| underscores | Replace XML-friendly "-"s with JSON friendly "_"s e | +| units | Add the 'units' (XML) or 'data-units (HTML) attribute | +| warn | Emit warnings when libxo detects bad calls | +| warn-xml | Emit warnings in XML | +| xml | Emit XML output | +| xpath | Add XPath expressions (HTML) | +|-------------+-------------------------------------------------------| + +The brief options are detailed in ^LIBXO_OPTIONS^. + +** Representing Hierarchy + +For XML and JSON, individual fields appear inside hierarchies which +provide context and meaning to the fields. Unfortunately, these +encoding have a basic disconnect between how lists is similar objects +are represented. + +XML encodes lists as set of sequential elements: + + phil + pallavi + sjg + +JSON encodes lists using a single name and square brackets: + + "user": [ "phil", "pallavi", "sjg" ] + +This means libxo needs three distinct indications of hierarchy: one +for containers of hierarchy appear only once for any specific parent, +one for lists, and one for each item in a list. + +*** Containers + +A "container" is an element of a hierarchy that appears only once +under any specific parent. The container has no value, but serves to +contain other nodes. + +To open a container, call xo_open_container() or +xo_open_container_h(). The former uses the default handle and +the latter accepts a specific handle. + + int xo_open_container_h (xo_handle_t *xop, const char *name); + int xo_open_container (const char *name); + +To close a level, use the xo_close_container() or +xo_close_container_h() functions: + + int xo_close_container_h (xo_handle_t *xop, const char *name); + int xo_close_container (const char *name); + +Each open call must have a matching close call. If the XOF_WARN flag +is set and the name given does not match the name of the currently open +container, a warning will be generated. + + Example: + + xo_open_container("top"); + xo_open_container("system"); + xo_emit("{:host-name/%s%s%s", hostname, + domainname ? "." : "", domainname ?: ""); + xo_close_container("system"); + xo_close_container("top"); + + Sample Output: + Text: + my-host.example.org + XML: + + + my-host.example.org + + + JSON: + "top" : { + "system" : { + "host-name": "my-host.example.org" + } + } + HTML: +
my-host.example.org
+ +*** Lists and Instances + +A list is set of one or more instances that appear under the same +parent. The instances contain details about a specific object. One +can think of instances as objects or records. A call is needed to +open and close the list, while a distinct call is needed to open and +close each instance of the list: + + xo_open_list("item"); + + for (ip = list; ip->i_title; ip++) { + xo_open_instance("item"); + xo_emit("{L:Item} '{:name/%s}':\n", ip->i_title); + xo_close_instance("item"); + } + + xo_close_list("item"); + +Getting the list and instance calls correct is critical to the proper +generation of XML and JSON data. + +*** DTRT Mode + +Some users may find tracking the names of open containers, lists, and +instances inconvenient. libxo offers a "Do The Right Thing" mode, where +libxo will track the names of open containers, lists, and instances so +the close function can be called without a name. To enable DTRT mode, +turn on the XOF_DTRT flag prior to making any other libxo output. + + xo_set_flags(NULL, XOF_DTRT); + +Each open and close function has a version with the suffix "_d", which +will close the open container, list, or instance: + + xo_open_container("top"); + ... + xo_close_container_d(); + +This also works for lists and instances: + + xo_open_list("item"); + for (...) { + xo_open_instance("item"); + xo_emit(...); + xo_close_instance_d(); + } + xo_close_list_d(); + +Note that the XOF_WARN flag will also cause libxo to track open +containers, lists, and instances. A warning is generated when the +name given to the close function and the name recorded do not match. + +*** Markers + +Markers are used to protect and restore the state of open constructs. +While a marker is open, no other open constructs can be closed. When +a marker is closed, all constructs open since the marker was opened +will be closed. + +Markers use names which are not user-visible, allowing the caller to +choose appropriate internal names. + +In this example, the code whiffles through a list of fish, calling a +function to emit details about each fish. The marker "fish-guts" is +used to ensure that any constructs opened by the function are closed +properly. + + for (i = 0; fish[i]; i++) { + xo_open_instance("fish"); + xo_open_marker("fish-guts"); + dump_fish_details(i); + xo_close_marker("fish-guts"); + } + +** Handles @handles@ + +libxo uses "handles" to control its rendering functionality. The +handle contains state and buffered data, as well as callback functions +to process data. + +A default handle is used when a NULL is passed to functions accepting +a handle. This handle is initialized to write its data to stdout +using the default style of text (XO_STYLE_TEXT). + +For the convenience of callers, the libxo library includes handle-less +functions that implicitly use the default handle. Any function that +takes a handle will use the default handle is a value of NULL is +passed in place of a valid handle. + +For example, the following are equivalent: + + xo_emit("test"); + xo_emit_h(NULL, "test"); + +Handles are created using xo_create() and destroy using xo_destroy(). + +** UTF-8 + +All strings for libxo must be UTF-8. libxo will handle turning them +into locale-based strings for display to the user. + +The only exception is argument formatted using the "%ls" format, which +require a wide character string (wchar_t *) as input. libxo will +convert these arguments as needed to either UTF-8 (for XML, JSON, and +HTML styles) or locale-based strings for display in text style. + + xo_emit("Alll strings are utf-8 content {:tag/%ls}", + L"except for wide strings"); + +"%S" is equivalent to "%ls". + +* The libxo API + +This section gives details about the functions in libxo, how to call +them, and the actions they perform. + +** Handles + +Handles give an abstraction for libxo that encapsulates the state of a +stream of output. Handles have the data type "xo_handle_t" and are +opaque to the caller. + +The library has a default handle that is automatically initialized. +By default, this handle will send text style output to standard output. +The xo_set_style and xo_set_flags functions can be used to change this +behavior. + +Many libxo functions take a handle as their first parameter; most that +do not use the default handle. Any function taking a handle can +be passed NULL to access the default handle. + +For the typical command that is generating output on standard output, +there is no need to create an explicit handle, but they are available +when needed, e.g., for daemons that generate multiple streams of +output. + +*** xo_create + +A handle can be allocated using the xo_create() function: + + xo_handle_t *xo_create (unsigned style, unsigned flags); + + Example: + xo_handle_t *xop = xo_create(XO_STYLE_JSON, XOF_WARN); + .... + xo_emit_h(xop, "testing\n"); + +See also ^styles^ and ^flags^. + +*** xo_create_to_file + +By default, libxo writes output to standard output. A convenience +function is provided for situations when output should be written to +a different file: + + xo_handle_t *xo_create_to_file (FILE *fp, unsigned style, + unsigned flags); + +Use the XOF_CLOSE_FP flag to trigger a call to fclose() for +the FILE pointer when the handle is destroyed. + +*** xo_set_writer + +The xo_set_writer function allows custom 'write' functions +which can tailor how libxo writes data. An opaque argument is +recorded and passed back to the write function, allowing the function +to acquire context information. The 'close' function can +release this opaque data and any other resources as needed. +The flush function can flush buffered data associated with the opaque +object. + + void xo_set_writer (xo_handle_t *xop, void *opaque, + xo_write_func_t write_func, + xo_close_func_t close_func); + xo_flush_func_t flush_func); + +*** xo_set_style + +To set the style, use the xo_set_style() function: + + void xo_set_style(xo_handle_t *xop, unsigned style); + +To use the default handle, pass a NULL handle: + + xo_set_style(NULL, XO_STYLE_XML); + +**** Output Styles (XO_STYLE_*) @styles@ + +The libxo functions accept a set of output styles: + +|---------------+-------------------------| +| Flag | Description | +|---------------+-------------------------| +| XO_STYLE_TEXT | Traditional text output | +| XO_STYLE_XML | XML encoded data | +| XO_STYLE_JSON | JSON encoded data | +| XO_STYLE_HTML | HTML encoded data | +|---------------+-------------------------| + +**** xo_set_style_name + +The xo_set_style_name() can be used to set the style based on a name +encoded as a string: + + int xo_set_style_name (xo_handle_t *xop, const char *style); + +The name can be any of the styles: "text", "xml", "json", or "html". + + EXAMPLE: + xo_set_style_name(NULL, "html"); + +*** xo_set_flags + +To set the flags, use the xo_set_flags() function: + + void xo_set_flags(xo_handle_t *xop, unsigned flags); + +To use the default handle, pass a NULL handle: + + xo_set_style(NULL, XO_STYLE_XML); + +**** Flags (XOF_*) @flags@ + +The set of valid flags include: + +|-------------------+----------------------------------------| +| Flag | Description | +|-------------------+----------------------------------------| +| XOF_CLOSE_FP | Close file pointer on xo_destroy() | +| XOF_COLOR | Enable color and effects in output | +| XOF_COLOR_ALLOWED | Allow color/effect for terminal output | +| XOF_DTRT | Enable "do the right thing" mode | +| XOF_INFO | Display info data attributes (HTML) | +| XOF_KEYS | Emit the key attribute (XML) | +| XOF_NO_ENV | Do not use the LIBXO_OPTIONS env var | +| XOF_NO_HUMANIZE | Display humanization (TEXT, HTML) | +| XOF_PRETTY | Make 'pretty printed' output | +| XOF_UNDERSCORES | Replaces hyphens with underscores | +| XOF_UNITS | Display units (XML, HMTL) | +| XOF_WARN | Generate warnings for broken calls | +| XOF_WARN_XML | Generate warnings in XML on stdout | +| XOF_XPATH | Emit XPath expressions (HTML) | +| XOF_COLUMNS | Force xo_emit to return columns used | +| XOF_FLUSH | Flush output after each xo_emit call | +|-------------------+----------------------------------------| + +The XOF_CLOSE_FP flag will trigger the call of the close_func +(provided via xo_set_writer()) when the handle is destroyed. + +The XOF_COLOR flag enables color and effects in output regardless of +output device, while the XOF_COLOR_ALLOWED flag allows color and +effects only if the output device is a terminal. + +The XOF_PRETTY flag requests 'pretty printing', which will trigger the +addition of indentation and newlines to enhance the readability of +XML, JSON, and HTML output. Text output is not affected. + +The XOF_WARN flag requests that warnings will trigger diagnostic +output (on standard error) when the library notices errors during +operations, or with arguments to functions. Without warnings enabled, +such conditions are ignored. + +Warnings allow developers to debug their interaction with libxo. +The function "xo_failure" can used as a breakpoint for a debugger, +regardless of whether warnings are enabled. + +If the style is XO_STYLE_HTML, the following additional flags can be +used: + +|---------------+-----------------------------------------| +| Flag | Description | +|---------------+-----------------------------------------| +| XOF_XPATH | Emit "data-xpath" attributes | +| XOF_INFO | Emit additional info fields | +|---------------+-----------------------------------------| + +The XOF_XPATH flag enables the emission of XPath expressions detailing +the hierarchy of XML elements used to encode the data field, if the +XPATH style of output were requested. + +The XOF_INFO flag encodes additional informational fields for HTML +output. See ^info^ for details. + +If the style is XO_STYLE_XML, the following additional flags can be +used: + +|---------------+-----------------------------------------| +| Flag | Description | +|---------------+-----------------------------------------| +| XOF_KEYS | Flag 'key' fields for xml | +|---------------+-----------------------------------------| + +The XOF_KEYS flag adds 'key' attribute to the XML encoding for +field definitions that use the 'k' modifier. The key attribute has +the value "key": + + xo_emit("{k:name}", item); + + XML: + truck + +**** xo_clear_flags + +The xo_clear_flags() function turns off the given flags in a specific +handle. + + void xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags); + +**** xo_set_options + +The xo_set_options() function accepts a comma-separated list of styles +and flags and enables them for a specific handle. + + int xo_set_options (xo_handle_t *xop, const char *input); + +The options are identical to those listed in ^command-line-arguments^. + +*** xo_destroy + +The xo_destroy function releases a handle and any resources it is +using. Calling xo_destroy with a NULL handle will release any +resources associated with the default handle. + + void xo_destroy(xo_handle_t *xop); + +** Emitting Content (xo_emit) + +The following functions are used to emit output: + + int xo_emit (const char *fmt, ...); + int xo_emit_h (xo_handle_t *xop, const char *fmt, ...); + int xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap); + +The "fmt" argument is a string containing field descriptors as +specified in ^format-strings^. The use of a handle is optional and +NULL can be passed to access the internal 'default' handle. See +^handles^. + +The remaining arguments to xo_emit() and xo_emit_h() are a set of +arguments corresponding to the fields in the format string. Care must +be taken to ensure the argument types match the fields in the format +string, since an inappropriate cast can ruin your day. The vap +argument to xo_emit_hv() points to a variable argument list that can +be used to retrieve arguments via va_arg(). + +*** Attributes (xo_attr) @xo_attr@ + +The xo_attr() function emits attributes for the XML output style. + + + int xo_attr (const char *name, const char *fmt, ...); + int xo_attr_h (xo_handle_t *xop, const char *name, + const char *fmt, ...); + int xo_attr_hv (xo_handle_t *xop, const char *name, + const char *fmt, va_list vap); + +The name parameter give the name of the attribute to be encoded. The +fmt parameter gives a printf-style format string used to format the +value of the attribute using any remaining arguments, or the vap +parameter passed to xo_attr_hv(). + + EXAMPLE: + xo_attr("seconds", "%ld", (unsigned long) login_time); + struct tm *tmp = localtime(login_time); + strftime(buf, sizeof(buf), "%R", tmp); + xo_emit("Logged in at {:login-time}\n", buf); + XML: + 00:14 + +xo_attr is placed on the next container, instance, leaf, or leaf list +that is emitted. + +Since attributes are only emitted in XML, their use should be limited +to meta-data and additional or redundant representations of data +already emitted in other form. + +*** Flushing Output (xo_flush) + +libxo buffers data, both for performance and consistency, but also to +allow some advanced features to work properly. At various times, the +caller may wish to flush any data buffered within the library. The +xo_flush() call is used for this: + + void xo_flush (void); + void xo_flush_h (xo_handle_t *xop); + +Calling xo_flush also triggers the flush function associated with the +handle. For the default handle, this is equivalent to +"fflush(stdio);". + +*** Finishing Output (xo_finish) + +When the program is ready to exit or close a handle, a call to +xo_finish() is required. This flushes any buffered data, closes +open libxo constructs, and completes any pending operations. + + int xo_finish (void); + int xo_finish_h (xo_handle_t *xop); + void xo_finish_atexit (void); + +Calling this function is vital to the proper operation of libxo, +especially for the non-TEXT output styles. + +xo_finish_atexit is suitable for use with atexit(3). + +** Emitting Hierarchy + +libxo represents to types of hierarchy: containers and lists. A +container appears once under a given parent where a list contains +instances that can appear multiple times. A container is used to hold +related fields and to give the data organization and scope. + +To create a container, use the xo_open_container and +xo_close_container functions: + + int xo_open_container (const char *name); + int xo_open_container_h (xo_handle_t *xop, const char *name); + int xo_open_container_hd (xo_handle_t *xop, const char *name); + int xo_open_container_d (const char *name); + + int xo_close_container (const char *name); + int xo_close_container_h (xo_handle_t *xop, const char *name); + int xo_close_container_hd (xo_handle_t *xop); + int xo_close_container_d (void); + +The name parameter gives the name of the container, encoded in UTF-8. +Since ASCII is a proper subset of UTF-8, traditional C strings can be +used directly. + +The close functions with the "_d" suffix are used in "Do The Right +Thing" mode, where the name of the open containers, lists, and +instances are maintained internally by libxo to allow the caller to +avoid keeping track of the open container name. + +Use the XOF_WARN flag to generate a warning if the name given on the +close does not match the current open container. + +For TEXT and HTML output, containers are not rendered into output +text, though for HTML they are used when the XOF_XPATH flag is set. + + EXAMPLE: + xo_open_container("system"); + xo_emit("The host name is {:host-name}\n", hn); + xo_close_container("system"); + XML: + foo + +*** Lists and Instances + +Lists are sequences of instances of homogeneous data objects. Two +distinct levels of calls are needed to represent them in our output +styles. Calls must be made to open and close a list, and for each +instance of data in that list, calls must be make to open and close +that instance. + +The name given to all calls must be identical, and it is strongly +suggested that the name be singular, not plural, as a matter of +style and usage expectations. + + EXAMPLE: + xo_open_list("user"); + for (i = 0; i < num_users; i++) { + xo_open_instance("user"); + xo_emit("{k:name}:{:uid/%u}:{:gid/%u}:{:home}\n", + pw[i].pw_name, pw[i].pw_uid, + pw[i].pw_gid, pw[i].pw_dir); + xo_close_instance("user"); + } + xo_close_list("user"); + TEXT: + phil:1001:1001:/home/phil + pallavi:1002:1002:/home/pallavi + XML: + + phil + 1001 + 1001 + /home/phil + + + pallavi + 1002 + 1002 + /home/pallavi + + JSON: + user: [ + { + "name": "phil", + "uid": 1001, + "gid": 1001, + "home": "/home/phil", + }, + { + "name": "pallavi", + "uid": 1002, + "gid": 1002, + "home": "/home/pallavi", + } + ] + +** Support Functions + +*** Parsing Command-line Arguments (xo_parse_args) @xo_parse_args@ + +The xo_parse_args() function is used to process a program's +arguments. libxo-specific options are processed and removed +from the argument list so the calling application does not +need to process them. If successful, a new value for argc +is returned. On failure, a message it emitted and -1 is returned. + + argc = xo_parse_args(argc, argv); + if (argc < 0) + exit(EXIT_FAILURE); + +Following the call to xo_parse_args, the application can process the +remaining arguments in a normal manner. See ^command-line-arguments^ +for a description of valid arguments. + +*** xo_set_program + +The xo_set_program function sets name of the program as reported by +functions like xo_failure, xo_warn, xo_err, etc. The program name is +initialized by xo_parse_args, but subsequent calls to xo_set_program +can override this value. + + xo_set_program(argv[0]); + +Note that the value is not copied, so the memory passed to +xo_set_program (and xo_parse_args) must be maintained by the caller. + +*** xo_set_version + +The xo_set_version function records a version number to be emitted as +part of the data for encoding styles (XML and JSON). This version +number is suitable for tracking changes in the content, allowing a +user of the data to discern which version of the data model is in use. + + void xo_set_version (const char *version); + void xo_set_version_h (xo_handle_t *xop, const char *version); + +*** Field Information (xo_info_t) @info@ + +HTML data can include additional information in attributes that +begin with "data-". To enable this, three things must occur: + +First the application must build an array of xo_info_t structures, +one per tag. The array must be sorted by name, since libxo uses a +binary search to find the entry that matches names from format +instructions. + +Second, the application must inform libxo about this information using +the xo_set_info() call: + + typedef struct xo_info_s { + const char *xi_name; /* Name of the element */ + const char *xi_type; /* Type of field */ + const char *xi_help; /* Description of field */ + } xo_info_t; + + void xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count); + +Like other libxo calls, passing NULL for the handle tells libxo to use +the default handle. + +If the count is -1, libxo will count the elements of infop, but there +must be an empty element at the end. More typically, the number is +known to the application: + + xo_info_t info[] = { + { "in-stock", "number", "Number of items in stock" }, + { "name", "string", "Name of the item" }, + { "on-order", "number", "Number of items on order" }, + { "sku", "string", "Stock Keeping Unit" }, + { "sold", "number", "Number of items sold" }, + }; + int info_count = (sizeof(info) / sizeof(info[0])); + ... + xo_set_info(NULL, info, info_count); + +Third, the emission of info must be triggered with the XOF_INFO flag +using either the xo_set_flags() function or the "--libxo=info" command +line argument. + +The type and help values, if present, are emitted as the "data-type" +and "data-help" attributes: + +
GRO-000-533
+ +*** Memory Allocation + +The xo_set_allocator function allows libxo to be used in environments +where the standard realloc() and free() functions are not available. + + void xo_set_allocator (xo_realloc_func_t realloc_func, + xo_free_func_t free_func); + +realloc_func should expect the same arguments as realloc(3) and return +a pointer to memory following the same convention. free_func will +receive the same argument as free(3) and should release it, as +appropriate for the environment. + +By default, the standard realloc() and free() functions are used. + +*** LIBXO_OPTIONS @LIBXO_OPTIONS@ + +The environment variable "LIBXO_OPTIONS" can be set to a string of +options: + +|--------+---------------------------------------------| +| Option | Action | +|--------+---------------------------------------------| +| c | Enable color/effects for TEXT/HTML | +| F | Force line-buffered flushing | +| H | Enable HTML output (XO_STYLE_HTML) | +| I | Enable info output (XOF_INFO) | +| i | Indent by | +| J | Enable JSON output (XO_STYLE_JSON) | +| k | Add keys to XPATH expressions in HTML | +| n | Disable humanization (TEXT, HTML) | +| P | Enable pretty-printed output (XOF_PRETTY) | +| T | Enable text output (XO_STYLE_TEXT) | +| U | Add units to HTML output | +| u | Change "-"s to "_"s in element names (JSON) | +| W | Enable warnings (XOF_WARN) | +| X | Enable XML output (XO_STYLE_XML) | +| x | Enable XPath data (XOF_XPATH) | +|--------+---------------------------------------------| + +For example, warnings can be enabled by: + + % env LIBXO_OPTIONS=W my-app + +Complete HTML output can be generated with: + + % env LIBXO_OPTIONS=HXI my-app + +Since environment variables are inherited, child processes will have +the same options, which may be undesirable, making the use of the +"--libxo" option is preferable in most situations. + +*** Errors, Warnings, and Messages + +Many programs make use of the standard library functions err() and +warn() to generate errors and warnings for the user. libxo wants to +pass that information via the current output style, and provides +compatible functions to allow this: + + void xo_warn (const char *fmt, ...); + void xo_warnx (const char *fmt, ...); + void xo_warn_c (int code, const char *fmt, ...); + void xo_warn_hc (xo_handle_t *xop, int code, + const char *fmt, ...); + void xo_err (int eval, const char *fmt, ...); + void xo_errc (int eval, int code, const char *fmt, ...); + void xo_errx (int eval, const char *fmt, ...); + void xo_message (const char *fmt, ...); + void xo_message_c (int code, const char *fmt, ...); + void xo_message_hc (xo_handle_t *xop, int code, + const char *fmt, ...); + void xo_message_hcv (xo_handle_t *xop, int code, + const char *fmt, va_list vap); + +These functions display the program name, a colon, a formatted message +based on the arguments, and then optionally a colon and an error +message associated with either "errno" or the "code" parameter. + + EXAMPLE: + if (open(filename, O_RDONLY) < 0) + xo_err(1, "cannot open file '%s'", filename); + +*** xo_error + +The xo_error function can be used for generic errors that should be +reported over the handle, rather than to stderr. The xo_error +function behaves like xo_err for TEXT and HTML output styles, but puts +the error into XML or JSON elements: + + EXAMPLE:: + xo_error("Does not %s", "compute"); + XML:: + Does not compute + JSON:: + "error": { "message": "Does not compute" } + +*** xo_no_setlocale + +libxo automatically initializes the locale based on setting of the +environment variables LC_CTYPE, LANG, and LC_ALL. The first of this +list of variables is used and if none of the variables, the locale +defaults to "UTF-8". The caller may wish to avoid this behavior, and +can do so by calling the xo_no_setlocale() function. + + void xo_no_setlocale (void); + +** Emitting syslog Messages + +syslog is the system logging facility used throughout the unix world. +Messages are sent from commands, applications, and daemons to a +hierarchy of servers, where they are filtered, saved, and forwarded +based on configuration behaviors. + +syslog is an older protocol, originally documented only in source +code. By the time RFC 3164 published, variation and mutation left the +leading "" string as only common content. RFC 5424 defines a new +version (version 1) of syslog and introduces structured data into the +messages. Structured data is a set of name/value pairs transmitted +distinctly alongside the traditional text message, allowing filtering +on precise values instead of regular expressions. + +These name/value pairs are scoped by a two-part identifier; an +enterprise identifier names the party responsible for the message +catalog and a name identifying that message. Enterprise IDs are +defined by IANA, the Internet Assigned Numbers Authority: + +https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers + +Use the ^xo_set_syslog_enterprise_id^() function to set the Enterprise +ID, as needed. + +The message name should follow the conventions in ^good-field-names^, +as should the fields within the message. + + /* Both of these calls are optional */ + xo_set_syslog_enterprise_id(32473); + xo_open_log("my-program", 0, LOG_DAEMON); + + /* Generate a syslog message */ + xo_syslog(LOG_ERR, "upload-failed", + "error <%d> uploading file '{:filename}' " + "as '{:target/%s:%s}'", + code, filename, protocol, remote); + + xo_syslog(LOG_INFO, "poofd-invalid-state", + "state {:current/%u} is invalid {:connection/%u}", + state, conn); + +The developer should be aware that the message name may be used in the +future to allow access to further information, including +documentation. Care should be taken to choose quality, descriptive +names. + +*** Priority, Facility, and Flags @priority@ + +The xo_syslog, xo_vsyslog, and xo_open_log functions accept a set of +flags which provide the priority of the message, the source facility, +and some additional features. These values are OR'd together to +create a single integer argument: + + xo_syslog(LOG_ERR | LOG_AUTH, "login-failed", + "Login failed; user '{:user}' from host '{:address}'", + user, addr); + +These values are defined in . + +The priority value indicates the importance and potential impact of +each message. + +|-------------+-------------------------------------------------------| +| Priority | Description | +|-------------+-------------------------------------------------------| +| LOG_EMERG | A panic condition, normally broadcast to all users | +| LOG_ALERT | A condition that should be corrected immediately | +| LOG_CRIT | Critical conditions | +| LOG_ERR | Generic errors | +| LOG_WARNING | Warning messages | +| LOG_NOTICE | Non-error conditions that might need special handling | +| LOG_INFO | Informational messages | +| LOG_DEBUG | Developer-oriented messages | +|-------------+-------------------------------------------------------| + +The facility value indicates the source of message, in fairly generic +terms. + +|---------------+-------------------------------------------------| +| Facility | Description | +|---------------+-------------------------------------------------| +| LOG_AUTH | The authorization system (e.g. login(1)) | +| LOG_AUTHPRIV | As LOG_AUTH, but logged to a privileged file | +| LOG_CRON | The cron daemon: cron(8) | +| LOG_DAEMON | System daemons, not otherwise explicitly listed | +| LOG_FTP | The file transfer protocol daemons | +| LOG_KERN | Messages generated by the kernel | +| LOG_LPR | The line printer spooling system | +| LOG_MAIL | The mail system | +| LOG_NEWS | The network news system | +| LOG_SECURITY | Security subsystems, such as ipfw(4) | +| LOG_SYSLOG | Messages generated internally by syslogd(8) | +| LOG_USER | Messages generated by user processes (default) | +| LOG_UUCP | The uucp system | +| LOG_LOCAL0..7 | Reserved for local use | +|---------------+-------------------------------------------------| + +In addition to the values listed above, xo_open_log accepts a set of +addition flags requesting specific behaviors. + +|------------+----------------------------------------------------| +| Flag | Description | +|------------+----------------------------------------------------| +| LOG_CONS | If syslogd fails, attempt to write to /dev/console | +| LOG_NDELAY | Open the connection to syslogd(8) immediately | +| LOG_PERROR | Write the message also to standard error output | +| LOG_PID | Log the process id with each message | +|------------+----------------------------------------------------| + +*** xo_syslog + +Use the xo_syslog function to generate syslog messages by calling it +with a log priority and facility, a message name, a format string, and +a set of arguments. The priority/facility argument are discussed +above, as is the message name. + +The format string follows the same conventions as xo_emit's format +string, with each field being rendered as an SD-PARAM pair. + + xo_syslog(LOG_ERR, "poofd-missing-file", + "'{:filename}' not found: {:error/%m}", filename); + + ... [poofd-missing-file@32473 filename="/etc/poofd.conf" + error="Permission denied"] '/etc/poofd.conf' not + found: Permission denied + +*** Support functions + +**** xo_vsyslog + +xo_vsyslog is identical in function to xo_syslog, but takes the set of +arguments using a va_list. + + void my_log (const char *name, const char *fmt, ...) + { + va_list vap; + va_start(vap, fmt); + xo_vsyslog(LOG_ERR, name, fmt, vap); + va_end(vap); + } + +**** xo_open_log + +xo_open_log functions similar to openlog(3), allowing customization of +the program name, the log facility number, and the additional option +flags described in ^priority^. + + void + xo_open_log (const char *ident, int logopt, int facility); + +**** xo_close_log + +xo_close_log functions similar to closelog(3), closing the log file +and releasing any associated resources. + + void + xo_close_log (void); + +**** xo_set_logmask + +xo_set_logmask function similar to setlogmask(3), restricting the set +of generated log event to those whose associated bit is set in +maskpri. Use LOG_MASK(pri) to find the appropriate bit, or +LOG_UPTO(toppri) to create a mask for all priorities up to and +including toppri. + + int + xo_set_logmask (int maskpri); + + Example: + setlogmask(LOG_UPTO(LOG_WARN)); + +**** xo_set_syslog_enterprise_id + +Use the xo_set_syslog_enterprise_id to supply a platform- or +application-specific enterprise id. This value is used in any +future syslog messages. + +Ideally, the operating system should supply a default value via the +"kern.syslog.enterprise_id" sysctl value. Lacking that, the +application should provide a suitable value. + + void + xo_set_syslog_enterprise_id (unsigned short eid); + +Enterprise IDs are administered by IANA, the Internet Assigned Number +Authority. The complete list is EIDs on their web site: + + https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers + +New EIDs can be requested from IANA using the following page: + + http://pen.iana.org/pen/PenApplication.page + +Each software development organization that defines a set of syslog +messages should register their own EID and use that value in their +software to ensure that messages can be uniquely identified by the +combination of EID + message name. + +** Creating Custom Encoders + +The number of encoding schemes in current use is staggering, with new +and distinct schemes appearing daily. While libxo provide XML, JSON, +HMTL, and text natively, there are requirements for other encodings. + +Rather than bake support for all possible encoders into libxo, the API +allows them to be defined externally. libxo can then interfaces with +these encoding modules using a simplistic API. libxo processes all +functions calls, handles state transitions, performs all formatting, +and then passes the results as operations to a customized encoding +function, which implements specific encoding logic as required. This +means your encoder doesn't need to detect errors with unbalanced +open/close operations but can rely on libxo to pass correct data. + +By making a simple API, libxo internals are not exposed, insulating the +encoder and the library from future or internal changes. + +The three elements of the API are: +- loading +- initialization +- operations + +The following sections provide details about these topics. + +libxo source contain an encoder for Concise Binary Object +Representation, aka CBOR (RFC 7049) which can be used as used as an +example for the API. + +*** Loading Encoders + +Encoders can be registered statically or discovered dynamically. +Applications can choose to call the xo_encoder_register() +function to explicitly register encoders, but more typically they are +built as shared libraries, placed in the libxo/extensions directory, +and loaded based on name. libxo looks for a file with the name of the encoder +and an extension of ".enc". This can be a file or a symlink to the +shared library file that supports the encoder. + + % ls -1 lib/libxo/extensions/*.enc + lib/libxo/extensions/cbor.enc + lib/libxo/extensions/test.enc + +*** Encoder Initialization + +Each encoder must export a symbol used to access the library, which +must have the following signature: + + int xo_encoder_library_init (XO_ENCODER_INIT_ARGS); + +XO_ENCODER_INIT_ARGS is a macro defined in xo_encoder.h that defines +an argument called "arg", a pointer of the type +xo_encoder_init_args_t. This structure contains two fields: + +- xei_version is the version number of the API as implemented within +libxo. This version is currently as 1 using XO_ENCODER_VERSION. This +number can be checked to ensure compatibility. The working assumption +is that all versions should be backward compatible, but each side may +need to accurately know the version supported by the other side. +xo_encoder_library_init can optionally check this value, and must then +set it to the version number used by the encoder, allowing libxo to +detect version differences and react accordingly. For example, if +version 2 adds new operations, then libxo will know that an encoding +library that set xei_version to 1 cannot be expected to handle those +new operations. + +- xei_handler must be set to a pointer to a function of type +xo_encoder_func_t, as defined in xo_encoder.h. This function +takes a set of parameters: +-- xop is a pointer to the opaque xo_handle_t structure +-- op is an integer representing the current operation +-- name is a string whose meaning differs by operation +-- value is a string whose meaning differs by operation +-- private is an opaque structure provided by the encoder + +Additional arguments may be added in the future, so handler functions +should use the XO_ENCODER_HANDLER_ARGS macro. An appropriate +"extern" declaration is provided to help catch errors. + +Once the encoder initialization function has completed processing, it +should return zero to indicate that no error has occurred. A non-zero +return code will cause the handle initialization to fail. + +*** Operations + +The encoder API defines a set of operations representing the +processing model of libxo. Content is formatted within libxo, and +callbacks are made to the encoder's handler function when data is +ready to be processed. + +|-----------------------+---------------------------------------| +| Operation | Meaning (Base function) | +|-----------------------+---------------------------------------| +| XO_OP_CREATE | Called when the handle is created | +| XO_OP_OPEN_CONTAINER | Container opened (xo_open_container) | +| XO_OP_CLOSE_CONTAINER | Container closed (xo_close_container) | +| XO_OP_OPEN_LIST | List opened (xo_open_list) | +| XO_OP_CLOSE_LIST | List closed (xo_close_list) | +| XO_OP_OPEN_LEAF_LIST | Leaf list opened (xo_open_leaf_list) | +| XO_OP_CLOSE_LEAF_LIST | Leaf list closed (xo_close_leaf_list) | +| XO_OP_OPEN_INSTANCE | Instance opened (xo_open_instance) | +| XO_OP_CLOSE_INSTANCE | Instance closed (xo_close_instance) | +| XO_OP_STRING | Field with Quoted UTF-8 string | +| XO_OP_CONTENT | Field with content | +| XO_OP_FINISH | Finish any pending output | +| XO_OP_FLUSH | Flush any buffered output | +| XO_OP_DESTROY | Clean up resources | +| XO_OP_ATTRIBUTE | An attribute name/value pair | +| XO_OP_VERSION | A version string | +|-----------------------+---------------------------------------| + +For all the open and close operations, the name parameter holds the +name of the construct. For string, content, and attribute operations, +the name parameter is the name of the field and the value parameter is +the value. "string" are differentiated from "content" to allow differing +treatment of true, false, null, and numbers from real strings, though +content values are formatted as strings before the handler is called. +For version operations, the value parameter contains the version. + +All strings are encoded in UTF-8. + +* The "xo" Utility + +The "xo" utility allows command line access to the functionality of +the libxo library. Using "xo", shell scripts can emit XML, JSON, and +HTML using the same commands that emit text output. + +The style of output can be selected using a specific option: "-X" for +XML, "-J" for JSON, "-H" for HTML, or "-T" for TEXT, which is the +default. The "--style ardinalyayammetersgrayErmissionpay eniedday6otuslay-oyay-eltayay \ No newline at end of file Index: vendor/Juniper/libxo/dist/tests/gettext/saved/gt_01.XP.err =================================================================== Index: vendor/Juniper/libxo/dist/tests/gettext/saved/gt_01.XP.out =================================================================== --- vendor/Juniper/libxo/dist/tests/gettext/saved/gt_01.XP.out (nonexistent) +++ vendor/Juniper/libxo/dist/tests/gettext/saved/gt_01.XP.out (revision 296962) @@ -0,0 +1,49 @@ + + amingflay + ordsway + urningbay + ymay + ouchcay + amingflay + ordsway + urningbay + ymay + ouchcay + 0 + 1 + 2 + 3 + 4 + 1234 + 1234 + foop + 4321 + + 1234 + foop + 4321 + + 1234 + foop + 4321 + + 3 + 1.2.3 + Tue Jun 23 18:47:09 UTC 2015 + <__warning> + gt_01.test + Nableuay otay ectulatobjay orwardfay elocipingvay + ectulatobjay + Ermissionpay eniedday + + <__warning> + gt_01.test + automaticyay ynchronizationsay ofyay ardinalyay ammetersgray ailedfay + + ardinalyay + ammetersgray + Ermissionpay eniedday + + 6 + otuslay-oyay-eltayay + Index: vendor/Juniper/libxo/dist/tests/gettext/strerror.pot =================================================================== --- vendor/Juniper/libxo/dist/tests/gettext/strerror.pot (nonexistent) +++ vendor/Juniper/libxo/dist/tests/gettext/strerror.pot (revision 296962) @@ -0,0 +1,468 @@ +# +# Copyright (c) 1982, 1985, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# List of system errors ala strerror() and sys_errlist +# Phil Shafer , 2015. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-07-01 16:15-0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Received {:received} {N:byte,bytes} from {:from}#{:port} in {:time} ms\n" +msgstr "" + +# 0 - ENOERROR +msgid "No error: 0" +msgstr "" + +# 1 - EPERM +msgid "Operation not permitted" +msgstr "" + +# 2 - ENOENT +msgid "No such file or directory" +msgstr "" + +# 3 - ESRCH +msgid "No such process" +msgstr "" + +# 4 - EINTR +msgid "Interrupted system call" +msgstr "" + +# 5 - EIO +msgid "Input/output error" +msgstr "" + +# 6 - ENXIO +msgid "Device not configured" +msgstr "" + +# 7 - E2BIG +msgid "Argument list too long" +msgstr "" + +# 8 - ENOEXEC +msgid "Exec format error" +msgstr "" + +# 9 - EBADF +msgid "Bad file descriptor" +msgstr "" + +# 10 - ECHILD +msgid "No child processes" +msgstr "" + +# 11 - EDEADLK +msgid "Resource deadlock avoided" +msgstr "" + +# 12 - ENOMEM +msgid "Cannot allocate memory" +msgstr "" + +# 13 - EACCES +msgid "Permission denied" +msgstr "" + +# 14 - EFAULT +msgid "Bad address" +msgstr "" + +# 15 - ENOTBLK +msgid "Block device required" +msgstr "" + +# 16 - EBUSY +msgid "Device busy" +msgstr "" + +# 17 - EEXIST +msgid "File exists" +msgstr "" + +# 18 - EXDEV +msgid "Cross-device link" +msgstr "" + +# 19 - ENODEV +msgid "Operation not supported by device" +msgstr "" + +# 20 - ENOTDIR +msgid "Not a directory" +msgstr "" + +# 21 - EISDIR +msgid "Is a directory" +msgstr "" + +# 22 - EINVAL +msgid "Invalid argument" +msgstr "" + +# 23 - ENFILE +msgid "Too many open files in system" +msgstr "" + +# 24 - EMFILE +msgid "Too many open files" +msgstr "" + +# 25 - ENOTTY +msgid "Inappropriate ioctl for device" +msgstr "" + +# 26 - ETXTBSY +msgid "Text file busy" +msgstr "" + +# 27 - EFBIG +msgid "File too large" +msgstr "" + +# 28 - ENOSPC +msgid "No space left on device" +msgstr "" + +# 29 - ESPIPE +msgid "Illegal seek" +msgstr "" + +# 30 - EROFS +msgid "Read-only file system" +msgstr "" + +# 31 - EMLINK +msgid "Too many links" +msgstr "" + +# 32 - EPIPE +msgid "Broken pipe" +msgstr "" + +# +# math software +# + +# 33 - EDOM +msgid "Numerical argument out of domain" +msgstr "" + +# 34 - ERANGE +msgid "Result too large" +msgstr "" + +# +# non-blocking and interrupt i/o +# + +# 35 - EAGAIN +# 35 - EWOULDBLOCK +msgid "Resource temporarily unavailable" +msgstr "" + +# 36 - EINPROGRESS +msgid "Operation now in progress" +msgstr "" + +# 37 - EALREADY +msgid "Operation already in progress" +msgstr "" + + +# +# ipc/network software -- argument errors +# + +# 38 - ENOTSOCK +msgid "Socket operation on non-socket" +msgstr "" + +# 39 - EDESTADDRREQ +msgid "Destination address required" +msgstr "" + +# 40 - EMSGSIZE +msgid "Message too long" +msgstr "" + +# 41 - EPROTOTYPE +msgid "Protocol wrong type for socket" +msgstr "" + +# 42 - ENOPROTOOPT +msgid "Protocol not available" +msgstr "" + +# 43 - EPROTONOSUPPORT +msgid "Protocol not supported" +msgstr "" + +# 44 - ESOCKTNOSUPPORT +msgid "Socket type not supported" +msgstr "" + +# 45 - EOPNOTSUPP +msgid "Operation not supported" +msgstr "" + +# 46 - EPFNOSUPPORT +msgid "Protocol family not supported" +msgstr "" + +# 47 - EAFNOSUPPORT +msgid "Address family not supported by protocol family" +msgstr "" + +# 48 - EADDRINUSE +msgid "Address already in use" +msgstr "" + +# 49 - EADDRNOTAVAIL +msgid "Can't assign requested address" +msgstr "" + +# +# ipc/network software -- operational errors +# + +# 50 - ENETDOWN +msgid "Network is down" +msgstr "" + +# 51 - ENETUNREACH +msgid "Network is unreachable" +msgstr "" + +# 52 - ENETRESET +msgid "Network dropped connection on reset" +msgstr "" + +# 53 - ECONNABORTED +msgid "Software caused connection abort" +msgstr "" + +# 54 - ECONNRESET +msgid "Connection reset by peer" +msgstr "" + +# 55 - ENOBUFS +msgid "No buffer space available" +msgstr "" + +# 56 - EISCONN +msgid "Socket is already connected" +msgstr "" + +# 57 - ENOTCONN +msgid "Socket is not connected" +msgstr "" + +# 58 - ESHUTDOWN +msgid "Can't send after socket shutdown" +msgstr "" + +# 59 - ETOOMANYREFS +msgid "Too many references: can't splice" +msgstr "" + +# 60 - ETIMEDOUT +msgid "Operation timed out" +msgstr "" + +# 61 - ECONNREFUSED +msgid "Connection refused" +msgstr "" + +# 62 - ELOOP +msgid "Too many levels of symbolic links" +msgstr "" + +# 63 - ENAMETOOLONG +msgid "File name too long" +msgstr "" + +# +# should be rearranged +# + +# 64 - EHOSTDOWN +msgid "Host is down" +msgstr "" + +# 65 - EHOSTUNREACH +msgid "No route to host" +msgstr "" + +# 66 - ENOTEMPTY +msgid "Directory not empty" +msgstr "" + +# +# quotas & mush +# + +# 67 - EPROCLIM +msgid "Too many processes" +msgstr "" + +# 68 - EUSERS +msgid "Too many users" +msgstr "" + +# 69 - EDQUOT +msgid "Disc quota exceeded" +msgstr "" + +# +# Network File System +# + +# 70 - ESTALE +msgid "Stale NFS file handle" +msgstr "" + +# 71 - EREMOTE +msgid "Too many levels of remote in path" +msgstr "" + +# 72 - EBADRPC +msgid "RPC struct is bad" +msgstr "" + +# 73 - ERPCMISMATCH +msgid "RPC version wrong" +msgstr "" + +# 74 - EPROGUNAVAIL +msgid "RPC prog. not avail" +msgstr "" + +# 75 - EPROGMISMATCH +msgid "Program version wrong" +msgstr "" + +# 76 - EPROCUNAVAIL +msgid "Bad procedure for program" +msgstr "" + +# 77 - ENOLCK +msgid "No locks available" +msgstr "" + +# 78 - ENOSYS +msgid "Function not implemented" +msgstr "" + +# 79 - EFTYPE +msgid "Inappropriate file type or format" +msgstr "" + +# 80 - EAUTH +msgid "Authentication error" +msgstr "" + +# 81 - ENEEDAUTH +msgid "Need authenticator" +msgstr "" + +# 82 - EIDRM +msgid "Identifier removed" +msgstr "" + +# 83 - ENOMSG +msgid "No message of desired type" +msgstr "" + +# 84 - EOVERFLOW +msgid "Value too large to be stored in data type" +msgstr "" + +# 85 - ECANCELED +msgid "Operation canceled" +msgstr "" + +# 86 - EILSEQ +msgid "Illegal byte sequence" +msgstr "" + +# 87 - ENOATTR +msgid "Attribute not found" +msgstr "" + +# +# General +# + +# 88 - EDOOFUS +msgid "Programming error" +msgstr "" + +# 89 - EBADMSG +msgid "Bad message" +msgstr "" + +# 90 - EMULTIHOP +msgid "Multihop attempted" +msgstr "" + +# 91 - ENOLINK +msgid "Link has been severed" +msgstr "" + +# 92 - EPROTO +msgid "Protocol error" +msgstr "" + +# 93 - ENOTCAPABLE +msgid "Capabilities insufficient" +msgstr "" + +# 94 - ECAPMODE +msgid "Not permitted in capability mode" +msgstr "" + +# 95 - ENOTRECOVERABLE +msgid "State not recoverable" +msgstr "" + +# 96 - EOWNERDEAD +msgid "Previous owner died" +msgstr "" Index: vendor/Juniper/libxo/dist/tests/xo/Makefile.am =================================================================== --- vendor/Juniper/libxo/dist/tests/xo/Makefile.am (nonexistent) +++ vendor/Juniper/libxo/dist/tests/xo/Makefile.am (revision 296962) @@ -0,0 +1,90 @@ +# +# $Id$ +# +# Copyright 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +AM_CFLAGS = -I${top_srcdir} -I${top_srcdir}/libxo + +# Ick: maintained by hand! +TEST_CASES = \ +xo_01.sh + +X=\ +xo_02.sh \ +xo_03.sh \ +xo_04.sh \ +xo_05.sh \ +xo_06.sh + +# TEST_CASES := $(shell cd ${srcdir} ; echo *.c ) + +EXTRA_DIST = \ + ${TEST_CASES} \ + ${addprefix saved/, ${TEST_CASES:.sh=.T.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.T.out}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.XP.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.XP.out}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.JP.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.JP.out}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.HP.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.HP.out}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.X.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.X.out}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.J.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.J.out}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.H.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.H.out}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.HIPx.err}} \ + ${addprefix saved/, ${TEST_CASES:.sh=.HIPx.out}} + +S2O = | ${SED} '1,/@@/d' + +all: + +#TEST_TRACE = set -x ; + +XO=../../xo/xo + +TEST_ONE = \ + LIBXO_OPTIONS=:W$$fmt \ + ${CHECKER} sh ${srcdir}/$$base.sh ${XO} ${TEST_OPTS} \ + > out/$$base.$$fmt.out 2> out/$$base.$$fmt.err ; \ + ${DIFF} -Nu ${srcdir}/saved/$$base.$$fmt.out out/$$base.$$fmt.out ${S2O} ; \ + ${DIFF} -Nu ${srcdir}/saved/$$base.$$fmt.err out/$$base.$$fmt.err ${S2O} + +TEST_FORMATS = T XP JP HP X J H HIPx + +test tests: ${bin_PROGRAMS} + @${MKDIR} -p out + -@ ${TEST_TRACE} (for test in ${TEST_CASES} ; do \ + base=`${BASENAME} $$test .sh` ; \ + (for fmt in ${TEST_FORMATS}; do \ + echo "... $$test ... $$fmt ..."; \ + ${TEST_ONE}; \ + true; \ + done) \ + done) + +one: + -@(test=${TEST_CASE}; data=${TEST_DATA}; ${TEST_ONE} ; true) + +accept: + -@(for test in ${TEST_CASES} ; do \ + base=`${BASENAME} $$test .sh` ; \ + (for fmt in ${TEST_FORMATS}; do \ + echo "... $$test ... $$fmt ..."; \ + ${CP} out/$$base.$$fmt.out ${srcdir}/saved/$$base.$$fmt.out ; \ + ${CP} out/$$base.$$fmt.err ${srcdir}/saved/$$base.$$fmt.err ; \ + done) \ + done) + +CLEANFILES = +CLEANDIRS = out + +clean-local: + rm -rf ${CLEANDIRS} Property changes on: vendor/Juniper/libxo/dist/tests/xo/Makefile.am ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.H.err =================================================================== Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.H.out =================================================================== --- vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.H.out (nonexistent) +++ vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.H.out (revision 296962) @@ -0,0 +1 @@ +
Item
one
is
number
001
,
color
:
red
Item
two
is
number
002
,
color
:
blue
Item
three
is
number
003
,
color
:
green
Item
four
is
number
004
,
color
:
yellow
\ No newline at end of file Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.HIPx.err =================================================================== Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.HIPx.out =================================================================== --- vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.HIPx.out (nonexistent) +++ vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.HIPx.out (revision 296962) @@ -0,0 +1,52 @@ +
+
Item
+
one
+
is
+
number
+
+
001
+
,
+
color
+
:
+
+
red
+
+
+
Item
+
two
+
is
+
number
+
+
002
+
,
+
color
+
:
+
+
blue
+
+
+
Item
+
three
+
is
+
number
+
+
003
+
,
+
color
+
:
+
+
green
+
+
+
Item
+
four
+
is
+
number
+
+
004
+
,
+
color
+
:
+
+
yellow
+
Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.HP.err =================================================================== Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.HP.out =================================================================== --- vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.HP.out (nonexistent) +++ vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.HP.out (revision 296962) @@ -0,0 +1,52 @@ +
+
Item
+
one
+
is
+
number
+
+
001
+
,
+
color
+
:
+
+
red
+
+
+
Item
+
two
+
is
+
number
+
+
002
+
,
+
color
+
:
+
+
blue
+
+
+
Item
+
three
+
is
+
number
+
+
003
+
,
+
color
+
:
+
+
green
+
+
+
Item
+
four
+
is
+
number
+
+
004
+
,
+
color
+
:
+
+
yellow
+
Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.J.err =================================================================== Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.J.out =================================================================== --- vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.J.out (nonexistent) +++ vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.J.out (revision 296962) @@ -0,0 +1 @@ +"top": {"item": {"name":"one","value":1,"color":"red"}, "item": {"name":"two","value":2,"color":"blue"}, "item": {"name":"three","value":3,"color":"green"}, "item": {"name":"four","value":4,"color":"yellow"}} Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.JP.err =================================================================== Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.JP.out =================================================================== --- vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.JP.out (nonexistent) +++ vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.JP.out (revision 296962) @@ -0,0 +1,22 @@ +"top": { + "item": { + "name": "one", + "value": 1, + "color": "red" + }, + "item": { + "name": "two", + "value": 2, + "color": "blue" + }, + "item": { + "name": "three", + "value": 3, + "color": "green" + }, + "item": { + "name": "four", + "value": 4, + "color": "yellow" + } +} Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.T.err =================================================================== Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.T.out =================================================================== --- vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.T.out (nonexistent) +++ vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.T.out (revision 296962) @@ -0,0 +1,4 @@ +Item one is number 001, color: red +Item two is number 002, color: blue +Item three is number 003, color: green +Item four is number 004, color: yellow Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.X.err =================================================================== Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.X.out =================================================================== --- vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.X.out (nonexistent) +++ vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.X.out (revision 296962) @@ -0,0 +1 @@ +one1redtwo2bluethree3greenfour4yellow \ No newline at end of file Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.XP.err =================================================================== Index: vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.XP.out =================================================================== --- vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.XP.out (nonexistent) +++ vendor/Juniper/libxo/dist/tests/xo/saved/xo_01.XP.out (revision 296962) @@ -0,0 +1,22 @@ + + + one + 1 + red + + + two + 2 + blue + + + three + 3 + green + + + four + 4 + yellow + + Index: vendor/Juniper/libxo/dist/tests/xo/xo_01.sh =================================================================== --- vendor/Juniper/libxo/dist/tests/xo/xo_01.sh (nonexistent) +++ vendor/Juniper/libxo/dist/tests/xo/xo_01.sh (revision 296962) @@ -0,0 +1,27 @@ +# +# $Id$ +# +# Copyright 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +XO=$1 +shift + +XOP="${XO} --warn --depth 1 --leading-xpath /top" + +${XO} --open top + +NF= +for i in one:1:red two:2:blue three:3:green four:4:yellow ; do + set `echo $i | sed 's/:/ /g'` + ${XOP} ${NF} --wrap item \ + 'Item {k:name} is {Lw:number}{:value/%03d/%d}, {Lwc:color}{:color}\n' \ + $1 $2 $3 + NF=--not-first +done + +${XO} --close top \ No newline at end of file Property changes on: vendor/Juniper/libxo/dist/tests/xo/xo_01.sh ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/xo/Makefile.am =================================================================== --- vendor/Juniper/libxo/dist/xo/Makefile.am (nonexistent) +++ vendor/Juniper/libxo/dist/xo/Makefile.am (revision 296962) @@ -0,0 +1,43 @@ +# +# Copyright 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +if LIBXO_WARNINGS_HIGH +LIBXO_WARNINGS = HIGH +endif +include ${top_srcdir}/warnings.mk + +AM_CFLAGS = \ + -DLIBXO_XMLSOFT_NEED_PRIVATE \ + -I${top_builddir} \ + -I${top_srcdir} \ + -I${top_srcdir}/libxo \ + ${WARNINGS} + +LIBS = \ + ${LIBXO_LIBS} + +bin_PROGRAMS = xo + +xo_SOURCES = xo.c +#xo_LDADD = ../libxo/libxo.la +#xo_LDFLAGS = -static + +LDADD = \ + ${top_builddir}/libxo/libxo.la + +if HAVE_HUMANIZE_NUMBER +LDADD += -lutil +endif + +man_MANS = xo.1 + +EXTRA_DIST = xo.1 + +install-data-hook: + for file in ${man_MANS}; do \ + cat ../libxo/add.man >> ${DESTDIR}${man1dir}/$$file ; done Property changes on: vendor/Juniper/libxo/dist/xo/Makefile.am ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/Juniper/libxo/dist/xo/xo.1 =================================================================== --- vendor/Juniper/libxo/dist/xo/xo.1 (nonexistent) +++ vendor/Juniper/libxo/dist/xo/xo.1 (revision 296962) @@ -0,0 +1,173 @@ +.\" # +.\" # Copyright (c) 2014, Juniper Networks, Inc. +.\" # All rights reserved. +.\" # This SOFTWARE is licensed under the LICENSE provided in the +.\" # ../Copyright file. By downloading, installing, copying, or +.\" # using the SOFTWARE, you agree to be bound by the terms of that +.\" # LICENSE. +.\" # Phil Shafer, July 2014 +.\" +.Dd December 4, 2014 +.Dt XO 1 +.Os +.Sh NAME +.Nm xo +.Nd emit formatted output based on format string and arguments +.Sh SYNOPSIS +.Nm +.Op Fl options +.Op Ar argument... +.Sh DESCRIPTION +The +.Nm +utility allows command line access to the functionality of +the +.Nm libxo +library. +Using +.Nm , +shell scripts can emit +.Em XML , +.Em JSON , +or +.Em HTML +using the same commands that emit text output. +.Pp +.Bl -tag -width "12345678901234567" +.It Fl "-close " +Close tags for the given path +.It Fl "-depth " +Set the depth for pretty printing +.It Fl "-help" +Display this help text +.It Fl "-html OR -H" +Generate HTML output +.It Fl "-json OR -J" +Generate JSON output +.It Fl "-leading-xpath " +Add a prefix to generated XPaths (HTML) +.It Fl "-open " +Open tags for the given path +.It Fl "-pretty OR -p" +Make 'pretty' output (add indent, newlines) +.It Fl "-style