summaryrefslogtreecommitdiffstats
path: root/private/windows/diamond/format.c
blob: 4037f76ee7fed336c5fc9aa9004c04d404ed6e15 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/***    format.c - Parameter Formatter
 *
 *      Microsoft Confidential
 *      Copyright (C) Microsoft Corporation 1994
 *      All Rights Reserved.
 *
 *  Author:
 *      Benjamin W. Slivka
 *
 *  History:
 *      28-Apr-1994 bens    Initial version
 *      10-May-1994 bens    Add braces support
 */

#include <memory.h>
#include <string.h>

#include "types.h"
#include "asrt.h"
#include "error.h"
#include "message.h"
#include "misc.h"

#include "inf.h"
#include "format.h"
#include "format.msg"


/***    SubstituteFormatString - Do parameter substitution
 *
 *  NOTE: See format.h for entry/exit conditions.
 */
BOOL SubstituteFormatString(char          *pszDst,
                            int            cbDst,
                            char          *pszSrc,
                            PFNFORMATPARM  pfnfp,
                            void          *pv,
                            PERROR         perr)
{
    char    achParmName[cbPARM_NAME_MAX];
    int     cb;
    int     cch;
    BOOL    fParmSeen;          // TRUE => parm seen in {braces}
    BOOL    fParmEmpty;         // TRUE => all parms were empty in {opt}
    char   *pch;
    char   *pszAfterParm;       // Points to first char after var subst.
    char   *pszOptStart;        // Start of optional text
    char   *pszParmStart;       // Points to first * in var substitution

    //** Not in conditional test
    pszOptStart = NULL;

    //** Process string for parameters and conditional text
    while (*pszSrc != '\0') {
        switch (*pszSrc) {

        case chFP_LBRACE:
            pszSrc++;                   // Eat the left brace
            if (*pszSrc == chFP_LBRACE) { // Have "{{"
                //** Collapse two {{ into one {
                if (!copyBounded(&pszDst,&cbDst,&pszSrc,1)) {
                    goto error_copying;
                }
            }
            else {
                //** Make sure we're not already in a brace section
                if (pszOptStart) {
                    ErrSet(perr,pszFMTERR_NESTED_BRACES,"%c%s",
                                            chFP_LBRACE,pszSrc);
                    return FALSE;
                }
                pszOptStart = pszDst;   // Save start of conditional text
                fParmSeen = FALSE;      // No parm seen, yet
                fParmEmpty = TRUE;      // Assume parm was empty
            }
            break;

        case chFP_RBRACE:
            pszSrc++;                   // Eat the right brace
            if (*pszSrc == chFP_RBRACE) { // Have "}}"
                //** Collapse two }} into one }
                if (!copyBounded(&pszDst,&cbDst,&pszSrc,1)) {
                    goto error_copying;
                }
            }
            else {
                //** Make sure we are in a brace section
                if (!pszOptStart) {
                    ErrSet(perr,pszFMTERR_RIGHT_WITH_NO_LEFT,"%c%c%s",
                                            chFP_RBRACE,chFP_LBRACE,pszSrc);
                    return FALSE;
                }
                //** Omit text if we need to
                if (fParmSeen && fParmEmpty) { // All parms were empty
                    //** Remove optional text
                    Assert(pszDst >= pszOptStart);
                    cbDst += pszDst - pszOptStart;
                    pszDst = pszOptStart;
                }
                //** Reset state variables
                pszOptStart = NULL;
            }
            break;

        case chFP_MARKER:
            pszParmStart = pszSrc;      // Save start for error messgages
            pszSrc++;                   // Skip first *
            if (*pszSrc == chFP_MARKER) { // Have "**"
                //** Collapse two ** into one *
                if (!copyBounded(&pszDst,&cbDst,&pszSrc,1)) {
                    goto error_copying;
                }
            }
            else {
                //** Attempt parameter substitution
                pch = strchr(pszSrc,chFP_MARKER); // Finding ending *
                if (!pch) {             // No terminating *
                    ErrSet(perr,pszFMTERR_MISSING_SUBST,"%c%s",
                                         chFP_MARKER,pszParmStart);
                    return FALSE;
                }
                pszAfterParm = pch+1;   // Point after ending *

                //** Extract parameter name
                cch =  pch - pszSrc;        // Length of parameter name
                if (cch >= sizeof(achParmName)) {
                    ErrSet(perr,pszFMTERR_PARM_NAME_TOO_LONG,"%d%s",
                                    sizeof(achParmName)-1,pszParmStart);
                    return FALSE;
                }
                memcpy(achParmName,pszSrc,cch); // Copy it
                achParmName[cch] = '\0';        // Terminate it

                //** Get parameter value
                if (-1 == (cb = (*pfnfp)(pszDst, // Destination
                                         cbDst,  // Space remaining
                                         achParmName, // Parm name
                                         pv,     // Context
                                         perr))) {
                    return FALSE;       // Error already filled in
                }
                //** Adjust output buffer and space remaining
                //   NOTE: Don't count NUL byte, as we do that at the very end
		Assert(cbDst >= cb);
                pszDst += cb;
                cbDst  -= cb;

                //** Remember we saw a parm, and if it was not empty
                fParmSeen = TRUE;
                if (cb > 0) {
                    fParmEmpty = FALSE; // At least one parm not empty
                }

                //** Skip over parameter name
                pszSrc = pszAfterParm;
            }
            break;

        default:
            //** Just copy the character
            if (!copyBounded(&pszDst,&cbDst,&pszSrc,1)) {
                goto error_copying;
            }
        }
    } /* while */

    //** Make sure we didn't leave an open optional text section
    if (pszOptStart) {
        ErrSet(perr,pszFMTERR_MISSING_RIGHT_BRACE,"%c",chFP_RBRACE);
        return FALSE;
    }

    //** Terminate processed string
    if (cbDst == 0) {			// No room for terminator	
        goto error_copying;
    }
    *pszDst++ = '\0';			// Terminate string

    //** Success
    return TRUE;

error_copying:
    ErrSet(perr,pszFMTERR_COPYING_OVERFLOW,"%s",pszSrc);
    return FALSE;
} /* SubstituteFormatString() */