Fossil SCM

fossil-scm / tools / translate.c
Blame History Raw 271 lines
1
/*
2
** Copyright (c) 2002 D. Richard Hipp
3
**
4
** This program is free software; you can redistribute it and/or
5
** modify it under the terms of the Simplified BSD License (also
6
** known as the "2-Clause License" or "FreeBSD License".)
7
**
8
** This program is distributed in the hope that it will be useful,
9
** but without any warranty; without even the implied warranty of
10
** merchantability or fitness for a particular purpose.
11
**
12
** Author contact information:
13
** [email protected]
14
** http://www.hwaci.com/drh/
15
**
16
*******************************************************************************
17
**
18
** SYNOPSIS:
19
**
20
** Input lines that begin with the "@" character are translated into
21
** either cgi_printf() statements or string literals and the
22
** translated code is written on standard output.
23
**
24
** The problem this program is attempt to solve is as follows: When
25
** writing CGI programs in C, we typically want to output a lot of HTML
26
** text to standard output. In pure C code, this involves doing a
27
** printf() with a big string containing all that text. But we have
28
** to insert special codes (ex: \n and \") for many common characters,
29
** which interferes with the readability of the HTML.
30
**
31
** This tool allows us to put raw HTML, without the special codes, in
32
** the middle of a C program. This program then translates the text
33
** into standard C by inserting all necessary backslashes and other
34
** punctuation.
35
**
36
** Enhancement #1:
37
**
38
** If the last non-whitespace character prior to the first "@" of a
39
** @-block is "=" or "," then the @-block is a string literal initializer
40
** rather than text that is to be output via cgi_printf(). Render it
41
** as such.
42
**
43
** Enhancement #2:
44
**
45
** Comments of the form: "|* @-comment: CC" (where "|" is really "/")
46
** cause CC to become a comment character for the @-substitution.
47
** Typical values for CC are "--" (for SQL text) or "#" (for Tcl script)
48
** or "//" (for C++ code). Lines of subsequent @-blocks that begin with
49
** CC are omitted from the output.
50
**
51
** Enhancement #3:
52
**
53
** If a non-enhancement #1 line ends in backslash, the backslash and the
54
** newline (\n) are not included in the argument to cgi_printf(). This
55
** is used to split one long output line across multiple source lines.
56
*/
57
#include <stdio.h>
58
#include <ctype.h>
59
#include <stdlib.h>
60
#include <string.h>
61
62
/*
63
** Space to hold arguments at the end of the cgi_printf()
64
*/
65
#define MX_ARG_SP 10000
66
static char zArg[MX_ARG_SP];
67
static int nArg = 0;
68
69
/*
70
** True if we are currently in a cgi_printf()
71
*/
72
static int inPrint = 0;
73
74
/*
75
** True if we are currently doing a free string
76
*/
77
static int inStr = 0;
78
79
/*
80
** Name of files being processed
81
*/
82
static const char *zInFile = "(stdin)";
83
84
/*
85
** The `fossil_isspace()' function copied from the Fossil source code.
86
** Some MSVC runtime library versions of `isspace()' break with an `assert()' if
87
** the input is smaller than -1 or greater than 255 in debug builds, due to sign
88
** extension when promoting `signed char' to `int' for non-ASCII characters. Use
89
** an `isspace()' replacement instead of explicit type casts to `unsigned char'.
90
*/
91
int fossil_isspace(char c){
92
return c==' ' || (c<='\r' && c>='\t');
93
}
94
95
/*
96
** Terminate an active cgi_printf() or free string
97
*/
98
static void end_block(FILE *out){
99
if( inPrint ){
100
zArg[nArg] = 0;
101
fprintf(out, "%s);\n", zArg);
102
nArg = 0;
103
inPrint = 0;
104
}
105
}
106
107
/*
108
** Translate the input stream into the output stream
109
*/
110
static void trans(FILE *in, FILE *out){
111
int i, j, k; /* Loop counters */
112
char c1, c2; /* Characters used to start a comment */
113
int lastWasEq = 0; /* True if last non-whitespace character was "=" */
114
int lastWasComma = 0; /* True if last non-whitespace character was "," */
115
int lineNo = 0; /* Line number */
116
char zLine[2000]; /* A single line of input */
117
char zOut[4000]; /* The input line translated into appropriate output */
118
119
c1 = c2 = '-';
120
while( fgets(zLine, sizeof(zLine), in) ){
121
lineNo++;
122
for(i=0; zLine[i] && fossil_isspace(zLine[i]); i++){}
123
if( zLine[i]!='@' ){
124
if( inPrint || inStr ) end_block(out);
125
fprintf(out,"%s",zLine);
126
/* 0123456789 12345 */
127
if( strncmp(zLine, "/* @-comment: ", 14)==0 ){
128
c1 = zLine[14];
129
c2 = zLine[15];
130
}
131
i += strlen(&zLine[i]);
132
while( i>0 && fossil_isspace(zLine[i-1]) ){ i--; }
133
lastWasEq = i>0 && zLine[i-1]=='=';
134
lastWasComma = i>0 && zLine[i-1]==',';
135
}else if( lastWasEq || lastWasComma){
136
/* If the last non-whitespace character before the first @ was
137
** an "="(var init/set) or a ","(const definition in list) then
138
** generate a string literal. But skip comments
139
** consisting of all text between c1 and c2 (default "--")
140
** and end of line.
141
*/
142
int indent, omitline;
143
char *zNewline = "\\n";
144
i++;
145
if( fossil_isspace(zLine[i]) ){ i++; }
146
indent = i - 2;
147
if( indent<0 ) indent = 0;
148
omitline = 0;
149
for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){
150
if( zLine[i]==c1 && (c2==' ' || zLine[i+1]==c2) ){
151
omitline = 1; break;
152
}
153
if( zLine[i]=='\\' && (zLine[i+1]==0 || zLine[i+1]=='\r'
154
|| zLine[i+1]=='\n') ){
155
zLine[i] = 0;
156
zNewline = "";
157
/* fprintf(stderr, "%s:%d: omit newline\n", zInFile, lineNo); */
158
break;
159
}
160
if( zLine[i]=='\\' || zLine[i]=='"' ){ zOut[j++] = '\\'; }
161
zOut[j++] = zLine[i];
162
}
163
if( zNewline[0] ) while( j>0 && fossil_isspace(zOut[j-1]) ){ j--; }
164
zOut[j] = 0;
165
if( j<=0 && omitline ){
166
fprintf(out,"\n");
167
}else{
168
fprintf(out,"%*s\"%s%s\"\n",indent, "", zOut, zNewline);
169
}
170
}else{
171
/* Otherwise (if the last non-whitespace was not '=') then generate a
172
** cgi_printf() statement whose format is the text following the '@'.
173
** Substrings of the form "%C(...)" (where C is any sequence of characters
174
** other than \000 and '(') will put "%C" in the format and add the
175
** "(...)" as an argument to the cgi_printf call. Each '*' character
176
** present in C (max two) causes one more "(...)" sequence to be consumed.
177
** For example, "%*.*d(4)(2)(1)" converts to "%*.*d" with arguments "4",
178
** "2", and "1", which will be used as the field width, precision, and
179
** value, respectively, producing a final formatted result of " 01".
180
*/
181
const char *zNewline = "\\n";
182
int indent;
183
int nC;
184
int nParam;
185
char c;
186
i++;
187
if( fossil_isspace(zLine[i]) ){ i++; }
188
indent = i;
189
for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){
190
if( zLine[i]=='\\' && (!zLine[i+1] || zLine[i+1]=='\r'
191
|| zLine[i+1]=='\n') ){
192
zNewline = "";
193
break;
194
}
195
if( zLine[i]=='"' || zLine[i]=='\\' ){ zOut[j++] = '\\'; }
196
zOut[j++] = zLine[i];
197
if( zLine[i]!='%' || zLine[i+1]=='%' || zLine[i+1]==0 ) continue;
198
nParam=1;
199
for(nC=1; zLine[i+nC] && zLine[i+nC]!='('; nC++){
200
if( zLine[i+nC]=='*' && nParam < 3 ) nParam++;
201
}
202
if( zLine[i+nC]!='(' || !isalpha(zLine[i+nC-1]) ) continue;
203
while( --nC ) zOut[j++] = zLine[++i];
204
do{
205
zArg[nArg++] = ',';
206
k = 0; i++;
207
if( zLine[i]!='(' ) break;
208
while( (c = zLine[i])!=0 ){
209
zArg[nArg++] = c;
210
if( c==')' ){
211
k--;
212
if( k==0 ) break;
213
}else if( c=='(' ){
214
k++;
215
}
216
i++;
217
}
218
}while( --nParam );
219
}
220
zOut[j] = 0;
221
if( !inPrint ){
222
fprintf(out,"%*scgi_printf(\"%s%s\"",indent-2,"", zOut, zNewline);
223
inPrint = 1;
224
}else{
225
fprintf(out,"\n%*s\"%s%s\"",indent+5, "", zOut, zNewline);
226
}
227
}
228
}
229
}
230
231
static void print_source_ref(const char *zSrcFile, FILE *out){
232
/* Set source line reference to the original source file.
233
* This makes compiler show the original file name in the compile error
234
* messages, instead of referring to the translated file.
235
* NOTE: This somewhat complicates stepping in debugger, as the resuling
236
* code would not match the referenced sources.
237
*/
238
#ifndef FOSSIL_DEBUG
239
const char *arg;
240
if( !*zSrcFile ){
241
return;
242
}
243
fprintf(out,"#line 1 \"");
244
for(arg=zSrcFile; *arg; arg++){
245
if( *arg!='\\' ){
246
fprintf(out,"%c", *arg);
247
}else{
248
fprintf(out,"\\\\");
249
}
250
}
251
fprintf(out,"\"\n");
252
#endif
253
}
254
255
int main(int argc, char **argv){
256
if( argc==2 ){
257
FILE *in = fopen(argv[1], "r");
258
if( in==0 ){
259
fprintf(stderr,"can not open %s\n", argv[1]);
260
exit(1);
261
}
262
zInFile = argv[1];
263
print_source_ref(zInFile, stdout);
264
trans(in, stdout);
265
fclose(in);
266
}else{
267
trans(stdin, stdout);
268
}
269
return 0;
270
}
271

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button