File: sh.scpp

package info (click to toggle)
shtool 2.0.8-9
  • links: PTS, VCS
  • area: main
  • in suites: buster, stretch
  • size: 808 kB
  • ctags: 12
  • sloc: perl: 399; makefile: 110; sh: 42
file content (402 lines) | stat: -rw-r--r-- 11,390 bytes parent folder | download | duplicates (3)
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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
##
##  scpp -- Sharing C Pre-Processor
##  Copyright (c) 1999-2008 Ralf S. Engelschall <rse@engelschall.com>
##
##  This file is part of shtool and free software; you can redistribute
##  it and/or modify it under the terms of the GNU General Public
##  License as published by the Free Software Foundation; either version
##  2 of the License, or (at your option) any later version.
##
##  This file is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
##  General Public License for more details.
##
##  You should have received a copy of the GNU General Public License
##  along with this program; if not, write to the Free Software
##  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
##  USA, or contact Ralf S. Engelschall <rse@engelschall.com>.
##

str_tool="scpp"
str_usage="[-v|--verbose] [-p|--preserve] [-f|--filter <filter>] [-o|--output <ofile>] [-t|--template <tfile>] [-M|--mark <mark>] [-D|--define <dname>] [-C|--class <cname>] <file> [<file> ...]"
gen_tmpfile=yes
arg_spec="1+"
opt_spec="v.p.f+o:t:M:D:C:"
opt_alias="v:verbose,p:preserve,f:filter,o:output,t:template,M:mark,D:define,C:class"
opt_v=no
opt_p=no
opt_f=""
opt_o="lib.h"
opt_t="lib.h.in"
opt_M="%%MARK%%"
opt_D="cpp"
opt_C="intern"

. ./sh.common

srcs="$*"
output="${opt_o}.n"

#   find a reasonable Awk
awk=''
paths=`echo $PATH |\
       sed -e 's%/*:%:%g' -e 's%/$%%' \
           -e 's/^:/.:/' -e 's/::/:.:/g' -e 's/:$/:./' \
           -e 's/:/ /g'`
for name in gawk nawk awk; do
    for path in $paths; do
        if [ -r "$path/$name" ]; then
            awk="$path/$name"
            break
        fi
    done
    if [ ".$awk" != . ]; then
        break
    fi
done
if [ ".$awk" = . ]; then
    echo "$msgprefix:Error: cannot find a reasonable Awk" 1>&2
    shtool_exit 1
fi

#   parse source file(s)
if [ ".$opt_v" = .yes ]; then
    echo "Parsing:" | $awk '{ printf("%s", $0); }' 1>&2
fi
for src in $srcs; do
    if [ ".$opt_v" = .yes ]; then
        echo $src | $awk '{ printf(" %s", $0); }' 1>&2
    fi
    if [ ".$opt_f" != . ]; then
        inputcmd="sed"
        OIFS="$IFS"; IFS="$ASC_NL"; set -- $opt_f; IFS="$OIFS"
        for e
        do
            inputcmd="$inputcmd -e '$e'"
        done
        inputcmd="$inputcmd '$src'"
    else
        inputcmd="cat '$src'"
    fi
    eval $inputcmd |\
    $awk '
       BEGIN {
           ln    = 0;
           fln   = 0;
           level = 0;
           mode  = "";
           store = "";
       }
       {
           ln++;
       }
       /^#if.*/ {
           level++;
       }
       /^#if [a-zA-Z_][a-zA-Z0-9_]* *$/ {
           if ($2 == define) {
               mode = "D";
               printf("D:#line %d \"%s\"\n", ln, src);
               next;
           }
       }
       /^#endif.*/ {
           level--;
           if (mode == "D" && level == 0) {
               mode = "";
               next;
           }
       }
       /^[a-zA-Z_][a-zA-Z0-9_].*;.*/ {
           if ($1 == class) {
               printf("V:#line %d \"%s\"\n", ln, src);
               printf("V:%s\n", $0);
               printf("J:%s\n", $0);
               next;
           }
       }
       /^[a-zA-Z_][a-zA-Z0-9_].*=.*/ {
           if ($1 == class) {
               printf("V:#line %d \"%s\"\n", ln, src);
               printf("V:%s\n", $0);
               printf("J:%s\n", $0);
               next;
           }
       }
       /^[a-zA-Z_][a-zA-Z0-9_]*/ {
           if ($1 == class) {
               fln = ln;
               store = $0;
               mode = "F";
               next;
           }
       }
       /^\{ *$/ {
           if (mode == "F") {
               printf("F:#line %d \"%s\"\n", fln, src);
               printf("F:%s;\n", store);
               printf("I:%s;\n", store);
               store = "";
               mode = "";
               next;
           }
       }
       {
           if (mode == "D")
               printf("D:%s\n", $0);
           else if (mode == "F")
               store = store " " $0;
       }
    ' "src=$src" "define=$opt_D" "class=$opt_C" >>$tmpfile
done
if [ ".$opt_v" = .yes ]; then
    echo "" 1>&2
fi

#   start generating output header
echo "/* $opt_o -- autogenerated from $opt_t, DO NOT EDIT! */" >$output
echo "#line 1 \"$opt_t\"" >>$output
sed <$opt_t -e "1,/^${opt_M} *\$/p" -e 'd' |\
sed -e "/^${opt_M} *\$/d" >>$output

#   merge in the define blocks
grep '^D:' $tmpfile | sed -e 's/^D://' >>$output

#   generate standard prolog
echo "#line 1 \"_ON_THE_FLY_\"" >>$output
echo "" >>$output
echo "/* make sure the scpp source extensions are skipped */" >>$output
echo "#define $opt_D 0" >>$output
echo "#define $opt_C /**/" >>$output

#   generate namespace hiding for variables
echo "" >>$output
echo "/* move intern variables to hidden namespace */" >>$output
grep '^J:' $tmpfile | sed >>$output \
    -e 's/^J://' \
    -e 's/  */ /g' \
    -e 's/^[^=;]*[ *]\([a-zA-Z0-9_]*\)\[\];.*$/#define \1 __\1/' \
    -e 's/^[^=;]*[ *]\([a-zA-Z0-9_]*\)\[\] =.*$/#define \1 __\1/' \
    -e 's/^[^=;]*[ *]\([a-zA-Z0-9_]*\);.*$/#define \1 __\1/' \
    -e 's/^[^=;]*[ *]\([a-zA-Z0-9_]*\) =.*$/#define \1 __\1/'

#   generate namespace hiding for functions
echo "" >>$output
echo "/* move intern functions to hidden namespace */" >>$output
grep '^I:' $tmpfile | sed >>$output \
    -e 's/^I://' \
    -e 's/\([ (]\) */\1/g' \
    -e 's/ *\([),]\)/\1/g' \
    -e 's/^[^(]*[ *]\([a-zA-Z0-9_]*\)(.*$/#define \1 __\1/'

#   generate prototypes for variables
echo "" >>$output
echo "/* prototypes for intern variables */" >>$output
grep '^V:' $tmpfile | sed >>$output \
    -e 's/^V://' \
    -e 's/  */ /g' \
    -e 's/^\([^=;]*[ *][a-zA-Z0-9_]*\[\]\);.*$/\1;/' \
    -e 's/^\([^=;]*[ *][a-zA-Z0-9_]*\[\]\) =.*$/\1;/' \
    -e 's/^\([^=;]*[ *][a-zA-Z0-9_]*\);.*$/\1;/' \
    -e 's/^\([^=;]*[ *][a-zA-Z0-9_]*\) =.*$/\1;/' \
    -e 's/ ;/;/g' \
    -e "s/^$opt_C /extern /"

#   generate prototypes for functions
echo "" >>$output
echo "/* prototypes for intern functions */" >>$output
grep '^F:' $tmpfile | sed >>$output \
    -e 's/^F://' \
    -e 's/\([ (]\) */\1/g' \
    -e 's/ *\([),]\)/\1/g' \
    -e 's/\([* ]\)[a-zA-Z0-9_]*,/\1,/g' \
    -e 's/\([* ]\)[a-zA-Z0-9_]*);/\1);/g' \
    -e 's/(\*[a-zA-Z0-9_]*)(/(*)(/g' \
    -e 's/\([ (]\) */\1/g' \
    -e 's/ *\([),]\)/\1/g' \
    -e "s/^$opt_C /extern /"

#   finish generating output header
n=`(echo ''; sed <$opt_t -e "1,/^${opt_M} *\$/p" -e 'd') |\
   wc -l | sed -e 's;^ *\([0-9]*\) *$;\1;'`
echo "#line $n \"$opt_t\"" >>$output
sed <$opt_t -e "/^${opt_M} *\$/,\$p" -e 'd' |\
sed -e "/^${opt_M} *\$/d" >>$output

#   create final output file
if [ -f $opt_o ]; then
    if [ ".$opt_p" = .yes ]; then
        grep -v '^#line' $opt_o  >$tmpfile.o
        grep -v '^#line' $output >$tmpfile.n
        out_old="$tmpfile.o"
        out_new="$tmpfile.n"
    else
        out_old="$opt_o"
        out_new="$output"
    fi
    if cmp -s $out_old $out_new; then
        :
    else
        cp $output $opt_o
    fi
else
    cp $output $opt_o
fi
rm -f $output
rm -f $tmpfile $tmpfile.* >/dev/null 2>&1

shtool_exit 0

##
##  manual page
##

=pod

=head1 NAME

B<shtool-scpp> - B<GNU shtool> C source file pre-processor

=head1 SYNOPSIS

B<shtool scpp>
[B<-v>|B<--verbose>]
[B<-p>|B<--preserve>]
[B<-f>|B<--filter> I<filter>]
[B<-o>|B<--output> I<ofile>]
[B<-t>|B<--template> I<tfile>]
[B<-M>|B<--mark> I<mark>]
[B<-D>|B<--define> I<dname>]
[B<-C>|B<--class> I<cname>]
I<file> [I<file> ...]

=head1 DESCRIPTION

This command is an additional ANSI C source file pre-processor for sharing
cpp(1) code segments, internal variables and internal functions. The intention
for this comes from writing libraries in ANSI C. Here a common shared internal
header file is usually used for sharing information between the library
source files.

The operation is to parse special constructs in I<file>s, generate a few
things out of these constructs and insert them at position I<mark> in I<tfile>
by writing the output to I<ofile>. Additionally the I<file>s are never touched
or modified. Instead the constructs are removed later by the cpp(1) phase of
the build process. The only prerequisite is that every I<file> has a
``C<#include ">I<ofile>C<">'' at the top.

This command provides the following features: First it avoids namespace
pollution and reduces prototyping efforts for internal symbols by recognizing
functions and variables which are defined with the storage class identifier
``I<cname>''.  For instance if I<cname> is ``intern'', a function ``C<intern
void *foobar(int quux)>'' in one of the I<file>s is translated into both a
``C<#define foobar __foobar>'' and a ``C<extern void *foobar(int quux);>'' in
I<ofile>. Additionally a global ``C<#define> I<cname> C</**/>'' is also
created in I<ofile> to let the compiler silently ignore this additional
storage class identifier.

Second, the library source files usually want to share C<typedef>s,
C<#define>s, etc.  over the source file boundaries. To achieve this one can
either place this stuff manually into I<tfile> or use the second feature of
B<scpp>: All code in I<file>s encapsulated with ``C<#if >I<dname> ...
C<#endif>'' is automatically copied to I<ofile>. Additionally a global
``C<#define> I<dname> C<0>'' is also created in I<ofile> to let the compiler
silently skip this parts (because it was already found in the header).

=head1 OPTIONS

The following command line options are available.

=over 4

=item B<-v>, B<--verbose>

Display some processing information.

=item B<-p>, B<--preserve>

Preserves I<ofile> independent of the generated ``#line'' lines. This is
useful for Makefiles if the real contents of I<ofile> will not change,
just line numbers. Default is to overwrite.

=item B<-f>, B<--filter> I<filter>

Apply one or more pre-processing sed(1) I<filter> commands (usually of
type ``C<s/.../.../>'') to each input file before their input is parsed.
This option can occur multiple times.

=item B<-o>, B<--output> I<ofile>

Output file name. Default is C<lib.h>.

=item B<-t>, B<--template> I<tfile>

Template file name. Default is C<lib.h.in>.

=item B<-M>, B<--mark> I<mark>

Mark to be replaced by generated constructs. Default is C<%%MARK%%>.

=item B<-D>, B<--define> I<dname>

FIXME. Default is C<cpp>.

=item B<-C>, B<--class> I<cname>

FIXME. Default is C<intern>.

=back

=head1 EXAMPLE

 #   Makefile
 SRCS=foo_bar.c foo_quux.c
 foo_p.h: foo_p.h.in
      shtool scpp -o foo_p.h -t foo_p.h.in \
                  -M %%MARK%% -D cpp -C intern $(SRCS)

 /* foo_p.h.in */
 #ifndef FOO_P_H
 #define FOO_P_H
 %%MARK%%
 #endif /* FOO_P_H */

 /* foo_bar.c */
 #include "foo_p.h"
 #if cpp
 #define OURS_INIT 4711
 #endif
 intern int ours;
 static int myone = 0815;
 intern int bar(void)
 {
     ours += myone;
 }

 /* foo_quux.c */
 #include "foo_p.h"
 int main(int argc, char *argv[])
 {
     int i;
     ours = OURS_INIT
     for (i = 0; i < 10; i++) {
         bar();
         printf("ours now %d\n", ours);
     }
     return 0;
 }

=head1 HISTORY

The B<GNU shtool> B<scpp> command was originally written by Ralf S.
Engelschall E<lt>rse@engelschall.comE<gt> in 1999 for B<GNU shtool>.
Its was prompted by the need to have a pre-processing facility
in the B<GNU pth> project.

=head1 SEE ALSO

shtool(1), cpp(1).

=cut