1251875Speter/* Licensed to the Apache Software Foundation (ASF) under one or more
2251875Speter * contributor license agreements.  See the NOTICE file distributed with
3251875Speter * this work for additional information regarding copyright ownership.
4251875Speter * The ASF licenses this file to You under the Apache License, Version 2.0
5251875Speter * (the "License"); you may not use this file except in compliance with
6251875Speter * the License.  You may obtain a copy of the License at
7251875Speter *
8251875Speter *     http://www.apache.org/licenses/LICENSE-2.0
9251875Speter *
10251875Speter * Unless required by applicable law or agreed to in writing, software
11251875Speter * distributed under the License is distributed on an "AS IS" BASIS,
12251875Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13251875Speter * See the License for the specific language governing permissions and
14251875Speter * limitations under the License.
15251875Speter */
16251875Speter
17251875Speter#include "apr.h"
18251875Speter#include "apr_private.h"
19251875Speter
20251875Speter#include "apr_lib.h"
21251875Speter#include "apr_strings.h"
22251875Speter#include "apr_network_io.h"
23251875Speter#include "apr_portable.h"
24251875Speter#include "apr_errno.h"
25251875Speter#include <math.h>
26251875Speter#if APR_HAVE_CTYPE_H
27251875Speter#include <ctype.h>
28251875Speter#endif
29251875Speter#if APR_HAVE_NETINET_IN_H
30251875Speter#include <netinet/in.h>
31251875Speter#endif
32251875Speter#if APR_HAVE_SYS_SOCKET_H
33251875Speter#include <sys/socket.h>
34251875Speter#endif
35251875Speter#if APR_HAVE_ARPA_INET_H
36251875Speter#include <arpa/inet.h>
37251875Speter#endif
38251875Speter#if APR_HAVE_LIMITS_H
39251875Speter#include <limits.h>
40251875Speter#endif
41251875Speter#if APR_HAVE_STRING_H
42251875Speter#include <string.h>
43251875Speter#endif
44251875Speter
45251875Spetertypedef enum {
46251875Speter    NO = 0, YES = 1
47251875Speter} boolean_e;
48251875Speter
49251875Speter#ifndef FALSE
50251875Speter#define FALSE 0
51251875Speter#endif
52251875Speter#ifndef TRUE
53251875Speter#define TRUE 1
54251875Speter#endif
55251875Speter#define NUL '\0'
56251875Speter
57251875Speterstatic const char null_string[] = "(null)";
58251875Speter#define S_NULL ((char *)null_string)
59251875Speter#define S_NULL_LEN 6
60251875Speter
61251875Speter#define FLOAT_DIGITS 6
62251875Speter#define EXPONENT_LENGTH 10
63251875Speter
64251875Speter/*
65251875Speter * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
66251875Speter *
67251875Speter * NOTICE: this is a magic number; do not decrease it
68251875Speter */
69251875Speter#define NUM_BUF_SIZE 512
70251875Speter
71251875Speter/*
72251875Speter * cvt - IEEE floating point formatting routines.
73251875Speter *       Derived from UNIX V7, Copyright(C) Caldera International Inc.
74251875Speter */
75251875Speter
76251875Speter/*
77251875Speter *    apr_ecvt converts to decimal
78251875Speter *      the number of digits is specified by ndigit
79251875Speter *      decpt is set to the position of the decimal point
80251875Speter *      sign is set to 0 for positive, 1 for negative
81251875Speter */
82251875Speter
83251875Speter#define NDIG 80
84251875Speter
85251875Speter/* buf must have at least NDIG bytes */
86251875Speterstatic char *apr_cvt(double arg, int ndigits, int *decpt, int *sign,
87251875Speter                     int eflag, char *buf)
88251875Speter{
89251875Speter    register int r2;
90251875Speter    double fi, fj;
91251875Speter    register char *p, *p1;
92251875Speter
93251875Speter    if (ndigits >= NDIG - 1)
94251875Speter        ndigits = NDIG - 2;
95251875Speter    r2 = 0;
96251875Speter    *sign = 0;
97251875Speter    p = &buf[0];
98251875Speter    if (arg < 0) {
99251875Speter        *sign = 1;
100251875Speter        arg = -arg;
101251875Speter    }
102251875Speter    arg = modf(arg, &fi);
103251875Speter    p1 = &buf[NDIG];
104251875Speter    /*
105251875Speter     * Do integer part
106251875Speter     */
107251875Speter    if (fi != 0) {
108251875Speter        p1 = &buf[NDIG];
109251875Speter        while (p1 > &buf[0] && fi != 0) {
110251875Speter            fj = modf(fi / 10, &fi);
111251875Speter            *--p1 = (int) ((fj + .03) * 10) + '0';
112251875Speter            r2++;
113251875Speter        }
114251875Speter        while (p1 < &buf[NDIG])
115251875Speter            *p++ = *p1++;
116251875Speter    }
117251875Speter    else if (arg > 0) {
118251875Speter        while ((fj = arg * 10) < 1) {
119251875Speter            arg = fj;
120251875Speter            r2--;
121251875Speter        }
122251875Speter    }
123251875Speter    p1 = &buf[ndigits];
124251875Speter    if (eflag == 0)
125251875Speter        p1 += r2;
126251875Speter    if (p1 < &buf[0]) {
127251875Speter        *decpt = -ndigits;
128251875Speter        buf[0] = '\0';
129251875Speter        return (buf);
130251875Speter    }
131251875Speter    *decpt = r2;
132251875Speter    while (p <= p1 && p < &buf[NDIG]) {
133251875Speter        arg *= 10;
134251875Speter        arg = modf(arg, &fj);
135251875Speter        *p++ = (int) fj + '0';
136251875Speter    }
137251875Speter    if (p1 >= &buf[NDIG]) {
138251875Speter        buf[NDIG - 1] = '\0';
139251875Speter        return (buf);
140251875Speter    }
141251875Speter    p = p1;
142251875Speter    *p1 += 5;
143251875Speter    while (*p1 > '9') {
144251875Speter        *p1 = '0';
145251875Speter        if (p1 > buf)
146251875Speter            ++ * --p1;
147251875Speter        else {
148251875Speter            *p1 = '1';
149251875Speter            (*decpt)++;
150251875Speter            if (eflag == 0) {
151251875Speter                if (p > buf)
152251875Speter                    *p = '0';
153251875Speter                p++;
154251875Speter            }
155251875Speter        }
156251875Speter    }
157251875Speter    *p = '\0';
158251875Speter    return (buf);
159251875Speter}
160251875Speter
161251875Speterstatic char *apr_ecvt(double arg, int ndigits, int *decpt, int *sign, char *buf)
162251875Speter{
163251875Speter    return (apr_cvt(arg, ndigits, decpt, sign, 1, buf));
164251875Speter}
165251875Speter
166251875Speterstatic char *apr_fcvt(double arg, int ndigits, int *decpt, int *sign, char *buf)
167251875Speter{
168251875Speter    return (apr_cvt(arg, ndigits, decpt, sign, 0, buf));
169251875Speter}
170251875Speter
171251875Speter/*
172251875Speter * apr_gcvt  - Floating output conversion to
173251875Speter * minimal length string
174251875Speter */
175251875Speter
176251875Speterstatic char *apr_gcvt(double number, int ndigit, char *buf, boolean_e altform)
177251875Speter{
178251875Speter    int sign, decpt;
179251875Speter    register char *p1, *p2;
180251875Speter    register int i;
181251875Speter    char buf1[NDIG];
182251875Speter
183251875Speter    p1 = apr_ecvt(number, ndigit, &decpt, &sign, buf1);
184251875Speter    p2 = buf;
185251875Speter    if (sign)
186251875Speter        *p2++ = '-';
187251875Speter    for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--)
188251875Speter        ndigit--;
189251875Speter    if ((decpt >= 0 && decpt - ndigit > 4)
190251875Speter        || (decpt < 0 && decpt < -3)) {                /* use E-style */
191251875Speter        decpt--;
192251875Speter        *p2++ = *p1++;
193251875Speter        *p2++ = '.';
194251875Speter        for (i = 1; i < ndigit; i++)
195251875Speter            *p2++ = *p1++;
196251875Speter        *p2++ = 'e';
197251875Speter        if (decpt < 0) {
198251875Speter            decpt = -decpt;
199251875Speter            *p2++ = '-';
200251875Speter        }
201251875Speter        else
202251875Speter            *p2++ = '+';
203251875Speter        if (decpt / 100 > 0)
204251875Speter            *p2++ = decpt / 100 + '0';
205251875Speter        if (decpt / 10 > 0)
206251875Speter            *p2++ = (decpt % 100) / 10 + '0';
207251875Speter        *p2++ = decpt % 10 + '0';
208251875Speter    }
209251875Speter    else {
210251875Speter        if (decpt <= 0) {
211251875Speter            if (*p1 != '0')
212251875Speter                *p2++ = '.';
213251875Speter            while (decpt < 0) {
214251875Speter                decpt++;
215251875Speter                *p2++ = '0';
216251875Speter            }
217251875Speter        }
218251875Speter        for (i = 1; i <= ndigit; i++) {
219251875Speter            *p2++ = *p1++;
220251875Speter            if (i == decpt)
221251875Speter                *p2++ = '.';
222251875Speter        }
223251875Speter        if (ndigit < decpt) {
224251875Speter            while (ndigit++ < decpt)
225251875Speter                *p2++ = '0';
226251875Speter            *p2++ = '.';
227251875Speter        }
228251875Speter    }
229251875Speter    if (p2[-1] == '.' && !altform)
230251875Speter        p2--;
231251875Speter    *p2 = '\0';
232251875Speter    return (buf);
233251875Speter}
234251875Speter
235251875Speter/*
236251875Speter * The INS_CHAR macro inserts a character in the buffer and writes
237251875Speter * the buffer back to disk if necessary
238251875Speter * It uses the char pointers sp and bep:
239251875Speter *      sp points to the next available character in the buffer
240251875Speter *      bep points to the end-of-buffer+1
241251875Speter * While using this macro, note that the nextb pointer is NOT updated.
242251875Speter *
243251875Speter * NOTE: Evaluation of the c argument should not have any side-effects
244251875Speter */
245251875Speter#define INS_CHAR(c, sp, bep, cc)                    \
246251875Speter{                                                   \
247251875Speter    if (sp) {                                       \
248251875Speter        if (sp >= bep) {                            \
249251875Speter            vbuff->curpos = sp;                     \
250251875Speter            if (flush_func(vbuff))                  \
251251875Speter                return -1;                          \
252251875Speter            sp = vbuff->curpos;                     \
253251875Speter            bep = vbuff->endpos;                    \
254251875Speter        }                                           \
255251875Speter        *sp++ = (c);                                \
256251875Speter    }                                               \
257251875Speter    cc++;                                           \
258251875Speter}
259251875Speter
260251875Speter#define NUM(c) (c - '0')
261251875Speter
262251875Speter#define STR_TO_DEC(str, num)                        \
263251875Speter    num = NUM(*str++);                              \
264251875Speter    while (apr_isdigit(*str))                       \
265251875Speter    {                                               \
266251875Speter        num *= 10 ;                                 \
267251875Speter        num += NUM(*str++);                         \
268251875Speter    }
269251875Speter
270251875Speter/*
271251875Speter * This macro does zero padding so that the precision
272251875Speter * requirement is satisfied. The padding is done by
273251875Speter * adding '0's to the left of the string that is going
274251875Speter * to be printed. We don't allow precision to be large
275251875Speter * enough that we continue past the start of s.
276251875Speter *
277251875Speter * NOTE: this makes use of the magic info that s is
278251875Speter * always based on num_buf with a size of NUM_BUF_SIZE.
279251875Speter */
280251875Speter#define FIX_PRECISION(adjust, precision, s, s_len)  \
281251875Speter    if (adjust) {                                   \
282251875Speter        apr_size_t p = (precision + 1 < NUM_BUF_SIZE) \
283251875Speter                     ? precision : NUM_BUF_SIZE - 1;  \
284251875Speter        while (s_len < p)                           \
285251875Speter        {                                           \
286251875Speter            *--s = '0';                             \
287251875Speter            s_len++;                                \
288251875Speter        }                                           \
289251875Speter    }
290251875Speter
291251875Speter/*
292251875Speter * Macro that does padding. The padding is done by printing
293251875Speter * the character ch.
294251875Speter */
295251875Speter#define PAD(width, len, ch)                         \
296251875Speterdo                                                  \
297251875Speter{                                                   \
298251875Speter    INS_CHAR(ch, sp, bep, cc);                      \
299251875Speter    width--;                                        \
300251875Speter}                                                   \
301251875Speterwhile (width > len)
302251875Speter
303251875Speter/*
304251875Speter * Prefix the character ch to the string str
305251875Speter * Increase length
306251875Speter * Set the has_prefix flag
307251875Speter */
308251875Speter#define PREFIX(str, length, ch)                     \
309251875Speter    *--str = ch;                                    \
310251875Speter    length++;                                       \
311251875Speter    has_prefix=YES;
312251875Speter
313251875Speter
314251875Speter/*
315251875Speter * Convert num to its decimal format.
316251875Speter * Return value:
317251875Speter *   - a pointer to a string containing the number (no sign)
318251875Speter *   - len contains the length of the string
319251875Speter *   - is_negative is set to TRUE or FALSE depending on the sign
320251875Speter *     of the number (always set to FALSE if is_unsigned is TRUE)
321251875Speter *
322251875Speter * The caller provides a buffer for the string: that is the buf_end argument
323251875Speter * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
324251875Speter * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
325251875Speter *
326251875Speter * Note: we have 2 versions. One is used when we need to use quads
327251875Speter * (conv_10_quad), the other when we don't (conv_10). We're assuming the
328251875Speter * latter is faster.
329251875Speter */
330251875Speterstatic char *conv_10(register apr_int32_t num, register int is_unsigned,
331251875Speter                     register int *is_negative, char *buf_end,
332251875Speter                     register apr_size_t *len)
333251875Speter{
334251875Speter    register char *p = buf_end;
335251875Speter    register apr_uint32_t magnitude = num;
336251875Speter
337251875Speter    if (is_unsigned) {
338251875Speter        *is_negative = FALSE;
339251875Speter    }
340251875Speter    else {
341251875Speter        *is_negative = (num < 0);
342251875Speter
343251875Speter        /*
344251875Speter         * On a 2's complement machine, negating the most negative integer
345251875Speter         * results in a number that cannot be represented as a signed integer.
346251875Speter         * Here is what we do to obtain the number's magnitude:
347251875Speter         *      a. add 1 to the number
348251875Speter         *      b. negate it (becomes positive)
349251875Speter         *      c. convert it to unsigned
350251875Speter         *      d. add 1
351251875Speter         */
352251875Speter        if (*is_negative) {
353251875Speter            apr_int32_t t = num + 1;
354251875Speter            magnitude = ((apr_uint32_t) -t) + 1;
355251875Speter        }
356251875Speter    }
357251875Speter
358251875Speter    /*
359251875Speter     * We use a do-while loop so that we write at least 1 digit
360251875Speter     */
361251875Speter    do {
362251875Speter        register apr_uint32_t new_magnitude = magnitude / 10;
363251875Speter
364251875Speter        *--p = (char) (magnitude - new_magnitude * 10 + '0');
365251875Speter        magnitude = new_magnitude;
366251875Speter    }
367251875Speter    while (magnitude);
368251875Speter
369251875Speter    *len = buf_end - p;
370251875Speter    return (p);
371251875Speter}
372251875Speter
373251875Speterstatic char *conv_10_quad(apr_int64_t num, register int is_unsigned,
374251875Speter                     register int *is_negative, char *buf_end,
375251875Speter                     register apr_size_t *len)
376251875Speter{
377251875Speter    register char *p = buf_end;
378251875Speter    apr_uint64_t magnitude = num;
379251875Speter
380251875Speter    /*
381251875Speter     * We see if we can use the faster non-quad version by checking the
382251875Speter     * number against the largest long value it can be. If <=, we
383251875Speter     * punt to the quicker version.
384251875Speter     */
385251875Speter    if ((magnitude <= APR_UINT32_MAX && is_unsigned)
386251875Speter        || (num <= APR_INT32_MAX && num >= APR_INT32_MIN && !is_unsigned))
387251875Speter            return(conv_10((apr_int32_t)num, is_unsigned, is_negative, buf_end, len));
388251875Speter
389251875Speter    if (is_unsigned) {
390251875Speter        *is_negative = FALSE;
391251875Speter    }
392251875Speter    else {
393251875Speter        *is_negative = (num < 0);
394251875Speter
395251875Speter        /*
396251875Speter         * On a 2's complement machine, negating the most negative integer
397251875Speter         * results in a number that cannot be represented as a signed integer.
398251875Speter         * Here is what we do to obtain the number's magnitude:
399251875Speter         *      a. add 1 to the number
400251875Speter         *      b. negate it (becomes positive)
401251875Speter         *      c. convert it to unsigned
402251875Speter         *      d. add 1
403251875Speter         */
404251875Speter        if (*is_negative) {
405251875Speter            apr_int64_t t = num + 1;
406251875Speter            magnitude = ((apr_uint64_t) -t) + 1;
407251875Speter        }
408251875Speter    }
409251875Speter
410251875Speter    /*
411251875Speter     * We use a do-while loop so that we write at least 1 digit
412251875Speter     */
413251875Speter    do {
414251875Speter        apr_uint64_t new_magnitude = magnitude / 10;
415251875Speter
416251875Speter        *--p = (char) (magnitude - new_magnitude * 10 + '0');
417251875Speter        magnitude = new_magnitude;
418251875Speter    }
419251875Speter    while (magnitude);
420251875Speter
421251875Speter    *len = buf_end - p;
422251875Speter    return (p);
423251875Speter}
424251875Speter
425251875Speterstatic char *conv_in_addr(struct in_addr *ia, char *buf_end, apr_size_t *len)
426251875Speter{
427251875Speter    unsigned addr = ntohl(ia->s_addr);
428251875Speter    char *p = buf_end;
429251875Speter    int is_negative;
430251875Speter    apr_size_t sub_len;
431251875Speter
432251875Speter    p = conv_10((addr & 0x000000FF)      , TRUE, &is_negative, p, &sub_len);
433251875Speter    *--p = '.';
434251875Speter    p = conv_10((addr & 0x0000FF00) >>  8, TRUE, &is_negative, p, &sub_len);
435251875Speter    *--p = '.';
436251875Speter    p = conv_10((addr & 0x00FF0000) >> 16, TRUE, &is_negative, p, &sub_len);
437251875Speter    *--p = '.';
438251875Speter    p = conv_10((addr & 0xFF000000) >> 24, TRUE, &is_negative, p, &sub_len);
439251875Speter
440251875Speter    *len = buf_end - p;
441251875Speter    return (p);
442251875Speter}
443251875Speter
444251875Speter
445251875Speter/* Must be passed a buffer of size NUM_BUF_SIZE where buf_end points
446251875Speter * to 1 byte past the end of the buffer. */
447251875Speterstatic char *conv_apr_sockaddr(apr_sockaddr_t *sa, char *buf_end, apr_size_t *len)
448251875Speter{
449251875Speter    char *p = buf_end;
450251875Speter    int is_negative;
451251875Speter    apr_size_t sub_len;
452251875Speter    char *ipaddr_str;
453251875Speter
454251875Speter    p = conv_10(sa->port, TRUE, &is_negative, p, &sub_len);
455251875Speter    *--p = ':';
456251875Speter    ipaddr_str = buf_end - NUM_BUF_SIZE;
457251875Speter    if (apr_sockaddr_ip_getbuf(ipaddr_str, sa->addr_str_len, sa)) {
458251875Speter        /* Should only fail if the buffer is too small, which it
459251875Speter         * should not be; but fail safe anyway: */
460251875Speter        *--p = '?';
461251875Speter        *len = buf_end - p;
462251875Speter        return p;
463251875Speter    }
464251875Speter    sub_len = strlen(ipaddr_str);
465251875Speter#if APR_HAVE_IPV6
466251875Speter    if (sa->family == APR_INET6 &&
467251875Speter        !IN6_IS_ADDR_V4MAPPED(&sa->sa.sin6.sin6_addr)) {
468251875Speter        *(p - 1) = ']';
469251875Speter        p -= sub_len + 2;
470251875Speter        *p = '[';
471251875Speter        memcpy(p + 1, ipaddr_str, sub_len);
472251875Speter    }
473251875Speter    else
474251875Speter#endif
475251875Speter    {
476251875Speter        p -= sub_len;
477251875Speter        memcpy(p, ipaddr_str, sub_len);
478251875Speter    }
479251875Speter
480251875Speter    *len = buf_end - p;
481251875Speter    return (p);
482251875Speter}
483251875Speter
484251875Speter
485251875Speter
486251875Speter#if APR_HAS_THREADS
487251875Speterstatic char *conv_os_thread_t(apr_os_thread_t *tid, char *buf_end, apr_size_t *len)
488251875Speter{
489251875Speter    union {
490251875Speter        apr_os_thread_t tid;
491251875Speter        apr_uint64_t u64;
492251875Speter        apr_uint32_t u32;
493251875Speter    } u;
494251875Speter    int is_negative;
495251875Speter
496251875Speter    u.tid = *tid;
497251875Speter    switch(sizeof(u.tid)) {
498251875Speter    case sizeof(apr_int32_t):
499251875Speter        return conv_10(u.u32, TRUE, &is_negative, buf_end, len);
500251875Speter    case sizeof(apr_int64_t):
501251875Speter        return conv_10_quad(u.u64, TRUE, &is_negative, buf_end, len);
502251875Speter    default:
503251875Speter        /* not implemented; stick 0 in the buffer */
504251875Speter        return conv_10(0, TRUE, &is_negative, buf_end, len);
505251875Speter    }
506251875Speter}
507251875Speter#endif
508251875Speter
509251875Speter
510251875Speter
511251875Speter/*
512251875Speter * Convert a floating point number to a string formats 'f', 'e' or 'E'.
513251875Speter * The result is placed in buf, and len denotes the length of the string
514251875Speter * The sign is returned in the is_negative argument (and is not placed
515251875Speter * in buf).
516251875Speter */
517251875Speterstatic char *conv_fp(register char format, register double num,
518251875Speter    boolean_e add_dp, int precision, int *is_negative,
519251875Speter    char *buf, apr_size_t *len)
520251875Speter{
521251875Speter    register char *s = buf;
522251875Speter    register char *p;
523251875Speter    int decimal_point;
524251875Speter    char buf1[NDIG];
525251875Speter
526251875Speter    if (format == 'f')
527251875Speter        p = apr_fcvt(num, precision, &decimal_point, is_negative, buf1);
528251875Speter    else /* either e or E format */
529251875Speter        p = apr_ecvt(num, precision + 1, &decimal_point, is_negative, buf1);
530251875Speter
531251875Speter    /*
532251875Speter     * Check for Infinity and NaN
533251875Speter     */
534251875Speter    if (apr_isalpha(*p)) {
535251875Speter        *len = strlen(p);
536251875Speter        memcpy(buf, p, *len + 1);
537251875Speter        *is_negative = FALSE;
538251875Speter        return (buf);
539251875Speter    }
540251875Speter
541251875Speter    if (format == 'f') {
542251875Speter        if (decimal_point <= 0) {
543251875Speter            *s++ = '0';
544251875Speter            if (precision > 0) {
545251875Speter                *s++ = '.';
546251875Speter                while (decimal_point++ < 0)
547251875Speter                    *s++ = '0';
548251875Speter            }
549251875Speter            else if (add_dp)
550251875Speter                *s++ = '.';
551251875Speter        }
552251875Speter        else {
553251875Speter            while (decimal_point-- > 0)
554251875Speter                *s++ = *p++;
555251875Speter            if (precision > 0 || add_dp)
556251875Speter                *s++ = '.';
557251875Speter        }
558251875Speter    }
559251875Speter    else {
560251875Speter        *s++ = *p++;
561251875Speter        if (precision > 0 || add_dp)
562251875Speter            *s++ = '.';
563251875Speter    }
564251875Speter
565251875Speter    /*
566251875Speter     * copy the rest of p, the NUL is NOT copied
567251875Speter     */
568251875Speter    while (*p)
569251875Speter        *s++ = *p++;
570251875Speter
571251875Speter    if (format != 'f') {
572251875Speter        char temp[EXPONENT_LENGTH];        /* for exponent conversion */
573251875Speter        apr_size_t t_len;
574251875Speter        int exponent_is_negative;
575251875Speter
576251875Speter        *s++ = format;                /* either e or E */
577251875Speter        decimal_point--;
578251875Speter        if (decimal_point != 0) {
579251875Speter            p = conv_10((apr_int32_t) decimal_point, FALSE, &exponent_is_negative,
580251875Speter                        &temp[EXPONENT_LENGTH], &t_len);
581251875Speter            *s++ = exponent_is_negative ? '-' : '+';
582251875Speter
583251875Speter            /*
584251875Speter             * Make sure the exponent has at least 2 digits
585251875Speter             */
586251875Speter            if (t_len == 1)
587251875Speter                *s++ = '0';
588251875Speter            while (t_len--)
589251875Speter                *s++ = *p++;
590251875Speter        }
591251875Speter        else {
592251875Speter            *s++ = '+';
593251875Speter            *s++ = '0';
594251875Speter            *s++ = '0';
595251875Speter        }
596251875Speter    }
597251875Speter
598251875Speter    *len = s - buf;
599251875Speter    return (buf);
600251875Speter}
601251875Speter
602251875Speter
603251875Speter/*
604251875Speter * Convert num to a base X number where X is a power of 2. nbits determines X.
605251875Speter * For example, if nbits is 3, we do base 8 conversion
606251875Speter * Return value:
607251875Speter *      a pointer to a string containing the number
608251875Speter *
609251875Speter * The caller provides a buffer for the string: that is the buf_end argument
610251875Speter * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
611251875Speter * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
612251875Speter *
613251875Speter * As with conv_10, we have a faster version which is used when
614251875Speter * the number isn't quad size.
615251875Speter */
616251875Speterstatic char *conv_p2(register apr_uint32_t num, register int nbits,
617251875Speter                     char format, char *buf_end, register apr_size_t *len)
618251875Speter{
619251875Speter    register int mask = (1 << nbits) - 1;
620251875Speter    register char *p = buf_end;
621251875Speter    static const char low_digits[] = "0123456789abcdef";
622251875Speter    static const char upper_digits[] = "0123456789ABCDEF";
623251875Speter    register const char *digits = (format == 'X') ? upper_digits : low_digits;
624251875Speter
625251875Speter    do {
626251875Speter        *--p = digits[num & mask];
627251875Speter        num >>= nbits;
628251875Speter    }
629251875Speter    while (num);
630251875Speter
631251875Speter    *len = buf_end - p;
632251875Speter    return (p);
633251875Speter}
634251875Speter
635251875Speterstatic char *conv_p2_quad(apr_uint64_t num, register int nbits,
636251875Speter                     char format, char *buf_end, register apr_size_t *len)
637251875Speter{
638251875Speter    register int mask = (1 << nbits) - 1;
639251875Speter    register char *p = buf_end;
640251875Speter    static const char low_digits[] = "0123456789abcdef";
641251875Speter    static const char upper_digits[] = "0123456789ABCDEF";
642251875Speter    register const char *digits = (format == 'X') ? upper_digits : low_digits;
643251875Speter
644251875Speter    if (num <= APR_UINT32_MAX)
645251875Speter        return(conv_p2((apr_uint32_t)num, nbits, format, buf_end, len));
646251875Speter
647251875Speter    do {
648251875Speter        *--p = digits[num & mask];
649251875Speter        num >>= nbits;
650251875Speter    }
651251875Speter    while (num);
652251875Speter
653251875Speter    *len = buf_end - p;
654251875Speter    return (p);
655251875Speter}
656251875Speter
657251875Speter#if APR_HAS_THREADS
658251875Speterstatic char *conv_os_thread_t_hex(apr_os_thread_t *tid, char *buf_end, apr_size_t *len)
659251875Speter{
660251875Speter    union {
661251875Speter        apr_os_thread_t tid;
662251875Speter        apr_uint64_t u64;
663251875Speter        apr_uint32_t u32;
664251875Speter    } u;
665251875Speter    int is_negative;
666251875Speter
667251875Speter    u.tid = *tid;
668251875Speter    switch(sizeof(u.tid)) {
669251875Speter    case sizeof(apr_int32_t):
670251875Speter        return conv_p2(u.u32, 4, 'x', buf_end, len);
671251875Speter    case sizeof(apr_int64_t):
672251875Speter        return conv_p2_quad(u.u64, 4, 'x', buf_end, len);
673251875Speter    default:
674251875Speter        /* not implemented; stick 0 in the buffer */
675251875Speter        return conv_10(0, TRUE, &is_negative, buf_end, len);
676251875Speter    }
677251875Speter}
678251875Speter#endif
679251875Speter
680251875Speter/*
681251875Speter * Do format conversion placing the output in buffer
682251875Speter */
683251875SpeterAPR_DECLARE(int) apr_vformatter(int (*flush_func)(apr_vformatter_buff_t *),
684251875Speter    apr_vformatter_buff_t *vbuff, const char *fmt, va_list ap)
685251875Speter{
686251875Speter    register char *sp;
687251875Speter    register char *bep;
688251875Speter    register int cc = 0;
689251875Speter    register apr_size_t i;
690251875Speter
691251875Speter    register char *s = NULL;
692251875Speter    char *q;
693251875Speter    apr_size_t s_len = 0;
694251875Speter
695251875Speter    register apr_size_t min_width = 0;
696251875Speter    apr_size_t precision = 0;
697251875Speter    enum {
698251875Speter        LEFT, RIGHT
699251875Speter    } adjust;
700251875Speter    char pad_char;
701251875Speter    char prefix_char;
702251875Speter
703251875Speter    double fp_num;
704251875Speter    apr_int64_t i_quad = 0;
705251875Speter    apr_uint64_t ui_quad;
706251875Speter    apr_int32_t i_num = 0;
707251875Speter    apr_uint32_t ui_num = 0;
708251875Speter
709251875Speter    char num_buf[NUM_BUF_SIZE];
710251875Speter    char char_buf[2];                /* for printing %% and %<unknown> */
711251875Speter
712251875Speter    enum var_type_enum {
713251875Speter            IS_QUAD, IS_LONG, IS_SHORT, IS_INT
714251875Speter    };
715251875Speter    enum var_type_enum var_type = IS_INT;
716251875Speter
717251875Speter    /*
718251875Speter     * Flag variables
719251875Speter     */
720251875Speter    boolean_e alternate_form;
721251875Speter    boolean_e print_sign;
722251875Speter    boolean_e print_blank;
723251875Speter    boolean_e adjust_precision;
724251875Speter    boolean_e adjust_width;
725251875Speter    int is_negative;
726251875Speter
727251875Speter    sp = vbuff->curpos;
728251875Speter    bep = vbuff->endpos;
729251875Speter
730251875Speter    while (*fmt) {
731251875Speter        if (*fmt != '%') {
732251875Speter            INS_CHAR(*fmt, sp, bep, cc);
733251875Speter        }
734251875Speter        else {
735251875Speter            /*
736251875Speter             * Default variable settings
737251875Speter             */
738251875Speter            boolean_e print_something = YES;
739251875Speter            adjust = RIGHT;
740251875Speter            alternate_form = print_sign = print_blank = NO;
741251875Speter            pad_char = ' ';
742251875Speter            prefix_char = NUL;
743251875Speter
744251875Speter            fmt++;
745251875Speter
746251875Speter            /*
747251875Speter             * Try to avoid checking for flags, width or precision
748251875Speter             */
749251875Speter            if (!apr_islower(*fmt)) {
750251875Speter                /*
751251875Speter                 * Recognize flags: -, #, BLANK, +
752251875Speter                 */
753251875Speter                for (;; fmt++) {
754251875Speter                    if (*fmt == '-')
755251875Speter                        adjust = LEFT;
756251875Speter                    else if (*fmt == '+')
757251875Speter                        print_sign = YES;
758251875Speter                    else if (*fmt == '#')
759251875Speter                        alternate_form = YES;
760251875Speter                    else if (*fmt == ' ')
761251875Speter                        print_blank = YES;
762251875Speter                    else if (*fmt == '0')
763251875Speter                        pad_char = '0';
764251875Speter                    else
765251875Speter                        break;
766251875Speter                }
767251875Speter
768251875Speter                /*
769251875Speter                 * Check if a width was specified
770251875Speter                 */
771251875Speter                if (apr_isdigit(*fmt)) {
772251875Speter                    STR_TO_DEC(fmt, min_width);
773251875Speter                    adjust_width = YES;
774251875Speter                }
775251875Speter                else if (*fmt == '*') {
776251875Speter                    int v = va_arg(ap, int);
777251875Speter                    fmt++;
778251875Speter                    adjust_width = YES;
779251875Speter                    if (v < 0) {
780251875Speter                        adjust = LEFT;
781251875Speter                        min_width = (apr_size_t)(-v);
782251875Speter                    }
783251875Speter                    else
784251875Speter                        min_width = (apr_size_t)v;
785251875Speter                }
786251875Speter                else
787251875Speter                    adjust_width = NO;
788251875Speter
789251875Speter                /*
790251875Speter                 * Check if a precision was specified
791251875Speter                 */
792251875Speter                if (*fmt == '.') {
793251875Speter                    adjust_precision = YES;
794251875Speter                    fmt++;
795251875Speter                    if (apr_isdigit(*fmt)) {
796251875Speter                        STR_TO_DEC(fmt, precision);
797251875Speter                    }
798251875Speter                    else if (*fmt == '*') {
799251875Speter                        int v = va_arg(ap, int);
800251875Speter                        fmt++;
801251875Speter                        precision = (v < 0) ? 0 : (apr_size_t)v;
802251875Speter                    }
803251875Speter                    else
804251875Speter                        precision = 0;
805251875Speter                }
806251875Speter                else
807251875Speter                    adjust_precision = NO;
808251875Speter            }
809251875Speter            else
810251875Speter                adjust_precision = adjust_width = NO;
811251875Speter
812251875Speter            /*
813251875Speter             * Modifier check.  In same cases, APR_OFF_T_FMT can be
814251875Speter             * "lld" and APR_INT64_T_FMT can be "ld" (that is, off_t is
815251875Speter             * "larger" than int64). Check that case 1st.
816251875Speter             * Note that if APR_OFF_T_FMT is "d",
817251875Speter             * the first if condition is never true. If APR_INT64_T_FMT
818251875Speter             * is "d' then the second if condition is never true.
819251875Speter             */
820251875Speter            if ((sizeof(APR_OFF_T_FMT) > sizeof(APR_INT64_T_FMT)) &&
821251875Speter                ((sizeof(APR_OFF_T_FMT) == 4 &&
822251875Speter                 fmt[0] == APR_OFF_T_FMT[0] &&
823251875Speter                 fmt[1] == APR_OFF_T_FMT[1]) ||
824251875Speter                (sizeof(APR_OFF_T_FMT) == 3 &&
825251875Speter                 fmt[0] == APR_OFF_T_FMT[0]) ||
826251875Speter                (sizeof(APR_OFF_T_FMT) > 4 &&
827251875Speter                 strncmp(fmt, APR_OFF_T_FMT,
828251875Speter                         sizeof(APR_OFF_T_FMT) - 2) == 0))) {
829251875Speter                /* Need to account for trailing 'd' and null in sizeof() */
830251875Speter                var_type = IS_QUAD;
831251875Speter                fmt += (sizeof(APR_OFF_T_FMT) - 2);
832251875Speter            }
833251875Speter            else if ((sizeof(APR_INT64_T_FMT) == 4 &&
834251875Speter                 fmt[0] == APR_INT64_T_FMT[0] &&
835251875Speter                 fmt[1] == APR_INT64_T_FMT[1]) ||
836251875Speter                (sizeof(APR_INT64_T_FMT) == 3 &&
837251875Speter                 fmt[0] == APR_INT64_T_FMT[0]) ||
838251875Speter                (sizeof(APR_INT64_T_FMT) > 4 &&
839251875Speter                 strncmp(fmt, APR_INT64_T_FMT,
840251875Speter                         sizeof(APR_INT64_T_FMT) - 2) == 0)) {
841251875Speter                /* Need to account for trailing 'd' and null in sizeof() */
842251875Speter                var_type = IS_QUAD;
843251875Speter                fmt += (sizeof(APR_INT64_T_FMT) - 2);
844251875Speter            }
845251875Speter            else if (*fmt == 'q') {
846251875Speter                var_type = IS_QUAD;
847251875Speter                fmt++;
848251875Speter            }
849251875Speter            else if (*fmt == 'l') {
850251875Speter                var_type = IS_LONG;
851251875Speter                fmt++;
852251875Speter            }
853251875Speter            else if (*fmt == 'h') {
854251875Speter                var_type = IS_SHORT;
855251875Speter                fmt++;
856251875Speter            }
857251875Speter            else {
858251875Speter                var_type = IS_INT;
859251875Speter            }
860251875Speter
861251875Speter            /*
862251875Speter             * Argument extraction and printing.
863251875Speter             * First we determine the argument type.
864251875Speter             * Then, we convert the argument to a string.
865251875Speter             * On exit from the switch, s points to the string that
866251875Speter             * must be printed, s_len has the length of the string
867251875Speter             * The precision requirements, if any, are reflected in s_len.
868251875Speter             *
869251875Speter             * NOTE: pad_char may be set to '0' because of the 0 flag.
870251875Speter             *   It is reset to ' ' by non-numeric formats
871251875Speter             */
872251875Speter            switch (*fmt) {
873251875Speter            case 'u':
874251875Speter                if (var_type == IS_QUAD) {
875251875Speter                    i_quad = va_arg(ap, apr_uint64_t);
876251875Speter                    s = conv_10_quad(i_quad, 1, &is_negative,
877251875Speter                            &num_buf[NUM_BUF_SIZE], &s_len);
878251875Speter                }
879251875Speter                else {
880251875Speter                    if (var_type == IS_LONG)
881251875Speter                        i_num = (apr_int32_t) va_arg(ap, apr_uint32_t);
882251875Speter                    else if (var_type == IS_SHORT)
883251875Speter                        i_num = (apr_int32_t) (unsigned short) va_arg(ap, unsigned int);
884251875Speter                    else
885251875Speter                        i_num = (apr_int32_t) va_arg(ap, unsigned int);
886251875Speter                    s = conv_10(i_num, 1, &is_negative,
887251875Speter                            &num_buf[NUM_BUF_SIZE], &s_len);
888251875Speter                }
889251875Speter                FIX_PRECISION(adjust_precision, precision, s, s_len);
890251875Speter                break;
891251875Speter
892251875Speter            case 'd':
893251875Speter            case 'i':
894251875Speter                if (var_type == IS_QUAD) {
895251875Speter                    i_quad = va_arg(ap, apr_int64_t);
896251875Speter                    s = conv_10_quad(i_quad, 0, &is_negative,
897251875Speter                            &num_buf[NUM_BUF_SIZE], &s_len);
898251875Speter                }
899251875Speter                else {
900251875Speter                    if (var_type == IS_LONG)
901251875Speter                        i_num = va_arg(ap, apr_int32_t);
902251875Speter                    else if (var_type == IS_SHORT)
903251875Speter                        i_num = (short) va_arg(ap, int);
904251875Speter                    else
905251875Speter                        i_num = va_arg(ap, int);
906251875Speter                    s = conv_10(i_num, 0, &is_negative,
907251875Speter                            &num_buf[NUM_BUF_SIZE], &s_len);
908251875Speter                }
909251875Speter                FIX_PRECISION(adjust_precision, precision, s, s_len);
910251875Speter
911251875Speter                if (is_negative)
912251875Speter                    prefix_char = '-';
913251875Speter                else if (print_sign)
914251875Speter                    prefix_char = '+';
915251875Speter                else if (print_blank)
916251875Speter                    prefix_char = ' ';
917251875Speter                break;
918251875Speter
919251875Speter
920251875Speter            case 'o':
921251875Speter                if (var_type == IS_QUAD) {
922251875Speter                    ui_quad = va_arg(ap, apr_uint64_t);
923251875Speter                    s = conv_p2_quad(ui_quad, 3, *fmt,
924251875Speter                            &num_buf[NUM_BUF_SIZE], &s_len);
925251875Speter                }
926251875Speter                else {
927251875Speter                    if (var_type == IS_LONG)
928251875Speter                        ui_num = va_arg(ap, apr_uint32_t);
929251875Speter                    else if (var_type == IS_SHORT)
930251875Speter                        ui_num = (unsigned short) va_arg(ap, unsigned int);
931251875Speter                    else
932251875Speter                        ui_num = va_arg(ap, unsigned int);
933251875Speter                    s = conv_p2(ui_num, 3, *fmt,
934251875Speter                            &num_buf[NUM_BUF_SIZE], &s_len);
935251875Speter                }
936251875Speter                FIX_PRECISION(adjust_precision, precision, s, s_len);
937251875Speter                if (alternate_form && *s != '0') {
938251875Speter                    *--s = '0';
939251875Speter                    s_len++;
940251875Speter                }
941251875Speter                break;
942251875Speter
943251875Speter
944251875Speter            case 'x':
945251875Speter            case 'X':
946251875Speter                if (var_type == IS_QUAD) {
947251875Speter                    ui_quad = va_arg(ap, apr_uint64_t);
948251875Speter                    s = conv_p2_quad(ui_quad, 4, *fmt,
949251875Speter                            &num_buf[NUM_BUF_SIZE], &s_len);
950251875Speter                }
951251875Speter                else {
952251875Speter                    if (var_type == IS_LONG)
953251875Speter                        ui_num = va_arg(ap, apr_uint32_t);
954251875Speter                    else if (var_type == IS_SHORT)
955251875Speter                        ui_num = (unsigned short) va_arg(ap, unsigned int);
956251875Speter                    else
957251875Speter                        ui_num = va_arg(ap, unsigned int);
958251875Speter                    s = conv_p2(ui_num, 4, *fmt,
959251875Speter                            &num_buf[NUM_BUF_SIZE], &s_len);
960251875Speter                }
961251875Speter                FIX_PRECISION(adjust_precision, precision, s, s_len);
962251875Speter                if (alternate_form && ui_num != 0) {
963251875Speter                    *--s = *fmt;        /* 'x' or 'X' */
964251875Speter                    *--s = '0';
965251875Speter                    s_len += 2;
966251875Speter                }
967251875Speter                break;
968251875Speter
969251875Speter
970251875Speter            case 's':
971251875Speter                s = va_arg(ap, char *);
972251875Speter                if (s != NULL) {
973251875Speter                    if (!adjust_precision) {
974251875Speter                        s_len = strlen(s);
975251875Speter                    }
976251875Speter                    else {
977251875Speter                        /* From the C library standard in section 7.9.6.1:
978251875Speter                         * ...if the precision is specified, no more then
979251875Speter                         * that many characters are written.  If the
980251875Speter                         * precision is not specified or is greater
981251875Speter                         * than the size of the array, the array shall
982251875Speter                         * contain a null character.
983251875Speter                         *
984251875Speter                         * My reading is is precision is specified and
985251875Speter                         * is less then or equal to the size of the
986251875Speter                         * array, no null character is required.  So
987251875Speter                         * we can't do a strlen.
988251875Speter                         *
989251875Speter                         * This figures out the length of the string
990251875Speter                         * up to the precision.  Once it's long enough
991251875Speter                         * for the specified precision, we don't care
992251875Speter                         * anymore.
993251875Speter                         *
994251875Speter                         * NOTE: you must do the length comparison
995251875Speter                         * before the check for the null character.
996251875Speter                         * Otherwise, you'll check one beyond the
997251875Speter                         * last valid character.
998251875Speter                         */
999251875Speter                        const char *walk;
1000251875Speter
1001251875Speter                        for (walk = s, s_len = 0;
1002251875Speter                             (s_len < precision) && (*walk != '\0');
1003251875Speter                             ++walk, ++s_len);
1004251875Speter                    }
1005251875Speter                }
1006251875Speter                else {
1007251875Speter                    s = S_NULL;
1008251875Speter                    s_len = S_NULL_LEN;
1009251875Speter                }
1010251875Speter                pad_char = ' ';
1011251875Speter                break;
1012251875Speter
1013251875Speter
1014251875Speter            case 'f':
1015251875Speter            case 'e':
1016251875Speter            case 'E':
1017251875Speter                fp_num = va_arg(ap, double);
1018251875Speter                /*
1019251875Speter                 * We use &num_buf[ 1 ], so that we have room for the sign
1020251875Speter                 */
1021251875Speter                s = NULL;
1022251875Speter#ifdef HAVE_ISNAN
1023251875Speter                if (isnan(fp_num)) {
1024251875Speter                    s = "nan";
1025251875Speter                    s_len = 3;
1026251875Speter                }
1027251875Speter#endif
1028251875Speter#ifdef HAVE_ISINF
1029251875Speter                if (!s && isinf(fp_num)) {
1030251875Speter                    s = "inf";
1031251875Speter                    s_len = 3;
1032251875Speter                }
1033251875Speter#endif
1034251875Speter                if (!s) {
1035251875Speter                    s = conv_fp(*fmt, fp_num, alternate_form,
1036251875Speter                                (int)((adjust_precision == NO) ? FLOAT_DIGITS : precision),
1037251875Speter                                &is_negative, &num_buf[1], &s_len);
1038251875Speter                    if (is_negative)
1039251875Speter                        prefix_char = '-';
1040251875Speter                    else if (print_sign)
1041251875Speter                        prefix_char = '+';
1042251875Speter                    else if (print_blank)
1043251875Speter                        prefix_char = ' ';
1044251875Speter                }
1045251875Speter                break;
1046251875Speter
1047251875Speter
1048251875Speter            case 'g':
1049251875Speter            case 'G':
1050251875Speter                if (adjust_precision == NO)
1051251875Speter                    precision = FLOAT_DIGITS;
1052251875Speter                else if (precision == 0)
1053251875Speter                    precision = 1;
1054251875Speter                /*
1055251875Speter                 * * We use &num_buf[ 1 ], so that we have room for the sign
1056251875Speter                 */
1057251875Speter                s = apr_gcvt(va_arg(ap, double), (int) precision, &num_buf[1],
1058251875Speter                            alternate_form);
1059251875Speter                if (*s == '-')
1060251875Speter                    prefix_char = *s++;
1061251875Speter                else if (print_sign)
1062251875Speter                    prefix_char = '+';
1063251875Speter                else if (print_blank)
1064251875Speter                    prefix_char = ' ';
1065251875Speter
1066251875Speter                s_len = strlen(s);
1067251875Speter
1068251875Speter                if (alternate_form && (q = strchr(s, '.')) == NULL) {
1069251875Speter                    s[s_len++] = '.';
1070251875Speter                    s[s_len] = '\0'; /* delimit for following strchr() */
1071251875Speter                }
1072251875Speter                if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL)
1073251875Speter                    *q = 'E';
1074251875Speter                break;
1075251875Speter
1076251875Speter
1077251875Speter            case 'c':
1078251875Speter                char_buf[0] = (char) (va_arg(ap, int));
1079251875Speter                s = &char_buf[0];
1080251875Speter                s_len = 1;
1081251875Speter                pad_char = ' ';
1082251875Speter                break;
1083251875Speter
1084251875Speter
1085251875Speter            case '%':
1086251875Speter                char_buf[0] = '%';
1087251875Speter                s = &char_buf[0];
1088251875Speter                s_len = 1;
1089251875Speter                pad_char = ' ';
1090251875Speter                break;
1091251875Speter
1092251875Speter
1093251875Speter            case 'n':
1094251875Speter                if (var_type == IS_QUAD)
1095251875Speter                    *(va_arg(ap, apr_int64_t *)) = cc;
1096251875Speter                else if (var_type == IS_LONG)
1097251875Speter                    *(va_arg(ap, long *)) = cc;
1098251875Speter                else if (var_type == IS_SHORT)
1099251875Speter                    *(va_arg(ap, short *)) = cc;
1100251875Speter                else
1101251875Speter                    *(va_arg(ap, int *)) = cc;
1102251875Speter                print_something = NO;
1103251875Speter                break;
1104251875Speter
1105251875Speter                /*
1106251875Speter                 * This is where we extend the printf format, with a second
1107251875Speter                 * type specifier
1108251875Speter                 */
1109251875Speter            case 'p':
1110251875Speter                switch(*++fmt) {
1111251875Speter                /*
1112251875Speter                 * If the pointer size is equal to or smaller than the size
1113251875Speter                 * of the largest unsigned int, we convert the pointer to a
1114251875Speter                 * hex number, otherwise we print "%p" to indicate that we
1115251875Speter                 * don't handle "%p".
1116251875Speter                 */
1117251875Speter                case 'p':
1118251875Speter#if APR_SIZEOF_VOIDP == 8
1119251875Speter                    if (sizeof(void *) <= sizeof(apr_uint64_t)) {
1120251875Speter                        ui_quad = (apr_uint64_t) va_arg(ap, void *);
1121251875Speter                        s = conv_p2_quad(ui_quad, 4, 'x',
1122251875Speter                                &num_buf[NUM_BUF_SIZE], &s_len);
1123251875Speter                    }
1124251875Speter#else
1125251875Speter                    if (sizeof(void *) <= sizeof(apr_uint32_t)) {
1126251875Speter                        ui_num = (apr_uint32_t) va_arg(ap, void *);
1127251875Speter                        s = conv_p2(ui_num, 4, 'x',
1128251875Speter                                &num_buf[NUM_BUF_SIZE], &s_len);
1129251875Speter                    }
1130251875Speter#endif
1131251875Speter                    else {
1132251875Speter                        s = "%p";
1133251875Speter                        s_len = 2;
1134251875Speter                        prefix_char = NUL;
1135251875Speter                    }
1136251875Speter                    pad_char = ' ';
1137251875Speter                    break;
1138251875Speter
1139251875Speter                /* print an apr_sockaddr_t as a.b.c.d:port */
1140251875Speter                case 'I':
1141251875Speter                {
1142251875Speter                    apr_sockaddr_t *sa;
1143251875Speter
1144251875Speter                    sa = va_arg(ap, apr_sockaddr_t *);
1145251875Speter                    if (sa != NULL) {
1146251875Speter                        s = conv_apr_sockaddr(sa, &num_buf[NUM_BUF_SIZE], &s_len);
1147251875Speter                        if (adjust_precision && precision < s_len)
1148251875Speter                            s_len = precision;
1149251875Speter                    }
1150251875Speter                    else {
1151251875Speter                        s = S_NULL;
1152251875Speter                        s_len = S_NULL_LEN;
1153251875Speter                    }
1154251875Speter                    pad_char = ' ';
1155251875Speter                }
1156251875Speter                break;
1157251875Speter
1158251875Speter                /* print a struct in_addr as a.b.c.d */
1159251875Speter                case 'A':
1160251875Speter                {
1161251875Speter                    struct in_addr *ia;
1162251875Speter
1163251875Speter                    ia = va_arg(ap, struct in_addr *);
1164251875Speter                    if (ia != NULL) {
1165251875Speter                        s = conv_in_addr(ia, &num_buf[NUM_BUF_SIZE], &s_len);
1166251875Speter                        if (adjust_precision && precision < s_len)
1167251875Speter                            s_len = precision;
1168251875Speter                    }
1169251875Speter                    else {
1170251875Speter                        s = S_NULL;
1171251875Speter                        s_len = S_NULL_LEN;
1172251875Speter                    }
1173251875Speter                    pad_char = ' ';
1174251875Speter                }
1175251875Speter                break;
1176251875Speter
1177251875Speter                /* print the error for an apr_status_t */
1178251875Speter                case 'm':
1179251875Speter                {
1180251875Speter                    apr_status_t *mrv;
1181251875Speter
1182251875Speter                    mrv = va_arg(ap, apr_status_t *);
1183251875Speter                    if (mrv != NULL) {
1184251875Speter                        s = apr_strerror(*mrv, num_buf, NUM_BUF_SIZE-1);
1185251875Speter                        s_len = strlen(s);
1186251875Speter                    }
1187251875Speter                    else {
1188251875Speter                        s = S_NULL;
1189251875Speter                        s_len = S_NULL_LEN;
1190251875Speter                    }
1191251875Speter                    pad_char = ' ';
1192251875Speter                }
1193251875Speter                break;
1194251875Speter
1195251875Speter                case 'T':
1196251875Speter#if APR_HAS_THREADS
1197251875Speter                {
1198251875Speter                    apr_os_thread_t *tid;
1199251875Speter
1200251875Speter                    tid = va_arg(ap, apr_os_thread_t *);
1201251875Speter                    if (tid != NULL) {
1202251875Speter                        s = conv_os_thread_t(tid, &num_buf[NUM_BUF_SIZE], &s_len);
1203251875Speter                        if (adjust_precision && precision < s_len)
1204251875Speter                            s_len = precision;
1205251875Speter                    }
1206251875Speter                    else {
1207251875Speter                        s = S_NULL;
1208251875Speter                        s_len = S_NULL_LEN;
1209251875Speter                    }
1210251875Speter                    pad_char = ' ';
1211251875Speter                }
1212251875Speter#else
1213251875Speter                    char_buf[0] = '0';
1214251875Speter                    s = &char_buf[0];
1215251875Speter                    s_len = 1;
1216251875Speter                    pad_char = ' ';
1217251875Speter#endif
1218251875Speter                    break;
1219251875Speter
1220251875Speter                case 't':
1221251875Speter#if APR_HAS_THREADS
1222251875Speter                {
1223251875Speter                    apr_os_thread_t *tid;
1224251875Speter
1225251875Speter                    tid = va_arg(ap, apr_os_thread_t *);
1226251875Speter                    if (tid != NULL) {
1227251875Speter                        s = conv_os_thread_t_hex(tid, &num_buf[NUM_BUF_SIZE], &s_len);
1228251875Speter                        if (adjust_precision && precision < s_len)
1229251875Speter                            s_len = precision;
1230251875Speter                    }
1231251875Speter                    else {
1232251875Speter                        s = S_NULL;
1233251875Speter                        s_len = S_NULL_LEN;
1234251875Speter                    }
1235251875Speter                    pad_char = ' ';
1236251875Speter                }
1237251875Speter#else
1238251875Speter                    char_buf[0] = '0';
1239251875Speter                    s = &char_buf[0];
1240251875Speter                    s_len = 1;
1241251875Speter                    pad_char = ' ';
1242251875Speter#endif
1243251875Speter                    break;
1244251875Speter
1245251875Speter                case 'B':
1246251875Speter                case 'F':
1247251875Speter                case 'S':
1248251875Speter                {
1249251875Speter                    char buf[5];
1250251875Speter                    apr_off_t size = 0;
1251251875Speter
1252251875Speter                    if (*fmt == 'B') {
1253251875Speter                        apr_uint32_t *arg = va_arg(ap, apr_uint32_t *);
1254251875Speter                        size = (arg) ? *arg : 0;
1255251875Speter                    }
1256251875Speter                    else if (*fmt == 'F') {
1257251875Speter                        apr_off_t *arg = va_arg(ap, apr_off_t *);
1258251875Speter                        size = (arg) ? *arg : 0;
1259251875Speter                    }
1260251875Speter                    else {
1261251875Speter                        apr_size_t *arg = va_arg(ap, apr_size_t *);
1262251875Speter                        size = (arg) ? *arg : 0;
1263251875Speter                    }
1264251875Speter
1265251875Speter                    s = apr_strfsize(size, buf);
1266251875Speter                    s_len = strlen(s);
1267251875Speter                    pad_char = ' ';
1268251875Speter                }
1269251875Speter                break;
1270251875Speter
1271251875Speter                case NUL:
1272251875Speter                    /* if %p ends the string, oh well ignore it */
1273251875Speter                    continue;
1274251875Speter
1275251875Speter                default:
1276251875Speter                    s = "bogus %p";
1277251875Speter                    s_len = 8;
1278251875Speter                    prefix_char = NUL;
1279251875Speter                    (void)va_arg(ap, void *); /* skip the bogus argument on the stack */
1280251875Speter                    break;
1281251875Speter                }
1282251875Speter                break;
1283251875Speter
1284251875Speter            case NUL:
1285251875Speter                /*
1286251875Speter                 * The last character of the format string was %.
1287251875Speter                 * We ignore it.
1288251875Speter                 */
1289251875Speter                continue;
1290251875Speter
1291251875Speter
1292251875Speter                /*
1293251875Speter                 * The default case is for unrecognized %'s.
1294251875Speter                 * We print %<char> to help the user identify what
1295251875Speter                 * option is not understood.
1296251875Speter                 * This is also useful in case the user wants to pass
1297251875Speter                 * the output of format_converter to another function
1298251875Speter                 * that understands some other %<char> (like syslog).
1299251875Speter                 * Note that we can't point s inside fmt because the
1300251875Speter                 * unknown <char> could be preceded by width etc.
1301251875Speter                 */
1302251875Speter            default:
1303251875Speter                char_buf[0] = '%';
1304251875Speter                char_buf[1] = *fmt;
1305251875Speter                s = char_buf;
1306251875Speter                s_len = 2;
1307251875Speter                pad_char = ' ';
1308251875Speter                break;
1309251875Speter            }
1310251875Speter
1311251875Speter            if (prefix_char != NUL && s != S_NULL && s != char_buf) {
1312251875Speter                *--s = prefix_char;
1313251875Speter                s_len++;
1314251875Speter            }
1315251875Speter
1316251875Speter            if (adjust_width && adjust == RIGHT && min_width > s_len) {
1317251875Speter                if (pad_char == '0' && prefix_char != NUL) {
1318251875Speter                    INS_CHAR(*s, sp, bep, cc);
1319251875Speter                    s++;
1320251875Speter                    s_len--;
1321251875Speter                    min_width--;
1322251875Speter                }
1323251875Speter                PAD(min_width, s_len, pad_char);
1324251875Speter            }
1325251875Speter
1326251875Speter            /*
1327251875Speter             * Print the string s.
1328251875Speter             */
1329251875Speter            if (print_something == YES) {
1330251875Speter                for (i = s_len; i != 0; i--) {
1331251875Speter                      INS_CHAR(*s, sp, bep, cc);
1332251875Speter                    s++;
1333251875Speter                }
1334251875Speter            }
1335251875Speter
1336251875Speter            if (adjust_width && adjust == LEFT && min_width > s_len)
1337251875Speter                PAD(min_width, s_len, pad_char);
1338251875Speter        }
1339251875Speter        fmt++;
1340251875Speter    }
1341251875Speter    vbuff->curpos = sp;
1342251875Speter
1343251875Speter    return cc;
1344251875Speter}
1345251875Speter
1346251875Speter
1347251875Speterstatic int snprintf_flush(apr_vformatter_buff_t *vbuff)
1348251875Speter{
1349251875Speter    /* if the buffer fills we have to abort immediately, there is no way
1350251875Speter     * to "flush" an apr_snprintf... there's nowhere to flush it to.
1351251875Speter     */
1352251875Speter    return -1;
1353251875Speter}
1354251875Speter
1355251875Speter
1356251875SpeterAPR_DECLARE_NONSTD(int) apr_snprintf(char *buf, apr_size_t len,
1357251875Speter                                     const char *format, ...)
1358251875Speter{
1359251875Speter    int cc;
1360251875Speter    va_list ap;
1361251875Speter    apr_vformatter_buff_t vbuff;
1362251875Speter
1363251875Speter    if (len == 0) {
1364251875Speter        /* NOTE: This is a special case; we just want to return the number
1365251875Speter         * of chars that would be written (minus \0) if the buffer
1366251875Speter         * size was infinite. We leverage the fact that INS_CHAR
1367251875Speter         * just does actual inserts iff the buffer pointer is non-NULL.
1368251875Speter         * In this case, we don't care what buf is; it can be NULL, since
1369251875Speter         * we don't touch it at all.
1370251875Speter         */
1371251875Speter        vbuff.curpos = NULL;
1372251875Speter        vbuff.endpos = NULL;
1373251875Speter    } else {
1374251875Speter        /* save one byte for nul terminator */
1375251875Speter        vbuff.curpos = buf;
1376251875Speter        vbuff.endpos = buf + len - 1;
1377251875Speter    }
1378251875Speter    va_start(ap, format);
1379251875Speter    cc = apr_vformatter(snprintf_flush, &vbuff, format, ap);
1380251875Speter    va_end(ap);
1381251875Speter    if (len != 0) {
1382251875Speter        *vbuff.curpos = '\0';
1383251875Speter    }
1384251875Speter    return (cc == -1) ? (int)len - 1 : cc;
1385251875Speter}
1386251875Speter
1387251875Speter
1388251875SpeterAPR_DECLARE(int) apr_vsnprintf(char *buf, apr_size_t len, const char *format,
1389251875Speter                               va_list ap)
1390251875Speter{
1391251875Speter    int cc;
1392251875Speter    apr_vformatter_buff_t vbuff;
1393251875Speter
1394251875Speter    if (len == 0) {
1395251875Speter        /* See above note */
1396251875Speter        vbuff.curpos = NULL;
1397251875Speter        vbuff.endpos = NULL;
1398251875Speter    } else {
1399251875Speter        /* save one byte for nul terminator */
1400251875Speter        vbuff.curpos = buf;
1401251875Speter        vbuff.endpos = buf + len - 1;
1402251875Speter    }
1403251875Speter    cc = apr_vformatter(snprintf_flush, &vbuff, format, ap);
1404251875Speter    if (len != 0) {
1405251875Speter        *vbuff.curpos = '\0';
1406251875Speter    }
1407251875Speter    return (cc == -1) ? (int)len - 1 : cc;
1408251875Speter}
1409