1#!/bin/sh
2#
3# MKlib_gen.sh -- generate sources from curses.h macro definitions
4#
5# ($Id: MKlib_gen.sh,v 1.34 2008/08/30 19:20:50 tom Exp $)
6#
7##############################################################################
8# Copyright (c) 1998-2007,2008 Free Software Foundation, Inc.                #
9#                                                                            #
10# Permission is hereby granted, free of charge, to any person obtaining a    #
11# copy of this software and associated documentation files (the "Software"), #
12# to deal in the Software without restriction, including without limitation  #
13# the rights to use, copy, modify, merge, publish, distribute, distribute    #
14# with modifications, sublicense, and/or sell copies of the Software, and to #
15# permit persons to whom the Software is furnished to do so, subject to the  #
16# following conditions:                                                      #
17#                                                                            #
18# The above copyright notice and this permission notice shall be included in #
19# all copies or substantial portions of the Software.                        #
20#                                                                            #
21# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #
22# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   #
23# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    #
24# THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER      #
25# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    #
26# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER        #
27# DEALINGS IN THE SOFTWARE.                                                  #
28#                                                                            #
29# Except as contained in this notice, the name(s) of the above copyright     #
30# holders shall not be used in advertising or otherwise to promote the sale, #
31# use or other dealings in this Software without prior written               #
32# authorization.                                                             #
33##############################################################################
34#
35# The XSI Curses standard requires all curses entry points to exist as
36# functions, even though many definitions would normally be shadowed
37# by macros.  Rather than hand-hack all that code, we actually
38# generate functions from the macros.
39#
40# This script accepts a file of prototypes on standard input.  It discards
41# any that don't have a `generated' comment attached. It then parses each
42# prototype (relying on the fact that none of the macros take function
43# pointer or array arguments) and generates C source from it.
44#
45# Here is what the pipeline stages are doing:
46#
47# 1. sed: extract prototypes of generated functions
48# 2. sed: decorate prototypes with generated arguments a1. a2,...z
49# 3. awk: generate the calls with args matching the formals
50# 4. sed: prefix function names in prototypes so the preprocessor won't expand
51#         them.
52# 5. cpp: macro-expand the file so the macro calls turn into C calls
53# 6. awk: strip the expansion junk off the front and add the new header
54# 7. sed: squeeze spaces, strip off gen_ prefix, create needed #undef
55#
56
57# keep the editing independent of locale:
58if test "${LANGUAGE+set}"    = set; then LANGUAGE=C;    export LANGUAGE;    fi
59if test "${LANG+set}"        = set; then LANG=C;        export LANG;        fi
60if test "${LC_ALL+set}"      = set; then LC_ALL=C;      export LC_ALL;      fi
61if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
62if test "${LC_CTYPE+set}"    = set; then LC_CTYPE=C;    export LC_CTYPE;    fi
63if test "${LC_COLLATE+set}"  = set; then LC_COLLATE=C;  export LC_COLLATE;  fi
64
65preprocessor="$1 -DNCURSES_INTERNALS -I../include"
66AWK="$2"
67USE="$3"
68
69PID=$$
70ED1=sed1_${PID}.sed
71ED2=sed2_${PID}.sed
72ED3=sed3_${PID}.sed
73ED4=sed4_${PID}.sed
74AW1=awk1_${PID}.awk
75AW2=awk2_${PID}.awk
76TMP=gen__${PID}.c
77trap "rm -f $ED1 $ED2 $ED3 $ED4 $AW1 $AW2 $TMP" 0 1 2 5 15
78
79ALL=$USE
80if test "$USE" = implemented ; then
81	CALL="call_"
82	cat >$ED1 <<EOF1
83/^extern.*implemented/{
84	h
85	s/^.*implemented:\([^ 	*]*\).*/P_POUNDCif_USE_\1_SUPPORT/p
86	g
87	s/^extern \([^;]*\);.*/\1/p
88	g
89	s/^.*implemented:\([^ 	*]*\).*/P_POUNDCendif/p
90}
91/^extern.*generated/{
92	h
93	s/^.*generated:\([^ 	*]*\).*/P_POUNDCif_USE_\1_SUPPORT/p
94	g
95	s/^extern \([^;]*\);.*/\1/p
96	g
97	s/^.*generated:\([^ 	*]*\).*/P_POUNDCendif/p
98}
99EOF1
100else
101	CALL=""
102	cat >$ED1 <<EOF1
103/^extern.*${ALL}/{
104	h
105	s/^.*${ALL}:\([^ 	*]*\).*/P_POUNDCif_USE_\1_SUPPORT/p
106	g
107	s/^extern \([^;]*\);.*/\1/p
108	g
109	s/^.*${ALL}:\([^ 	*]*\).*/P_POUNDCendif/p
110}
111EOF1
112fi
113
114cat >$ED2 <<EOF2
115/^P_/b nc
116/(void)/b nc
117	s/,/ a1% /
118	s/,/ a2% /
119	s/,/ a3% /
120	s/,/ a4% /
121	s/,/ a5% /
122	s/,/ a6% /
123	s/,/ a7% /
124	s/,/ a8% /
125	s/,/ a9% /
126	s/,/ a10% /
127	s/,/ a11% /
128	s/,/ a12% /
129	s/,/ a13% /
130	s/,/ a14% /
131	s/,/ a15% /
132	s/*/ * /g
133	s/%/ , /g
134	s/)/ z)/
135	s/\.\.\. z)/...)/
136:nc
137	s/(/ ( /
138	s/)/ )/
139EOF2
140
141cat >$ED3 <<EOF3
142/^P_/{
143	s/^P_POUNDCif_/#if /
144	s/^P_POUNDCendif/#endif/
145	s/^P_//
146	b done
147}
148	s/		*/ /g
149	s/  */ /g
150	s/ ,/,/g
151	s/( /(/g
152	s/ )/)/g
153	s/ gen_/ /
154	s/^M_/#undef /
155	s/^[ 	]*@[ 	]*@[ 	]*/	/
156:done
157EOF3
158
159if test "$USE" = generated ; then
160cat >$ED4 <<EOF
161	s/^\(.*\) \(.*\) (\(.*\))\$/NCURSES_EXPORT(\1) \2 (\3)/
162EOF
163else
164cat >$ED4 <<EOF
165/^\(.*\) \(.*\) (\(.*\))\$/ {
166	h
167	s/^\(.*\) \(.*\) (\(.*\))\$/extern \1 call_\2 (\3);/
168	p
169	g
170	s/^\(.*\) \(.*\) (\(.*\))\$/\1 call_\2 (\3)/
171	}
172EOF
173fi
174
175cat >$AW1 <<\EOF1
176BEGIN	{
177		skip=0;
178	}
179/^P_POUNDCif/ {
180		print "\n"
181		print $0
182		skip=0;
183}
184/^P_POUNDCendif/ {
185		print $0
186		skip=1;
187}
188$0 !~ /^P_/ {
189	if (skip)
190		print "\n"
191	skip=1;
192
193	first=$1
194	for (i = 1; i <= NF; i++) {
195		if ( $i != "NCURSES_CONST" ) {
196			first = i;
197			break;
198		}
199	}
200	second = first + 1;
201	if ( $first == "chtype" ) {
202		returnType = "Char";
203	} else if ( $first == "SCREEN" ) {
204		returnType = "SP";
205	} else if ( $first == "WINDOW" ) {
206		returnType = "Win";
207	} else if ( $first == "attr_t" || $second == "attrset" || $second == "standout" || $second == "standend" || $second == "wattrset" || $second == "wstandout" || $second == "wstandend" ) {
208		returnType = "Attr";
209	} else if ( $first == "bool" || $first == "NCURSES_BOOL" ) {
210		returnType = "Bool";
211	} else if ( $second == "*" ) {
212		returnType = "Ptr";
213	} else {
214		returnType = "Code";
215	}
216	myfunc = second;
217	for (i = second; i <= NF; i++) {
218		if ($i != "*") {
219			myfunc = i;
220			break;
221		}
222	}
223	if (using == "generated") {
224		print "M_" $myfunc
225	}
226	print $0;
227	print "{";
228	argcount = 1;
229	check = NF - 1;
230	if ($check == "void")
231		argcount = 0;
232	if (argcount != 0) {
233		for (i = 1; i <= NF; i++)
234			if ($i == ",")
235				argcount++;
236	}
237
238	# suppress trace-code for functions that we cannot do properly here,
239	# since they return data.
240	dotrace = 1;
241	if ($myfunc ~ /innstr/)
242		dotrace = 0;
243	if ($myfunc ~ /innwstr/)
244		dotrace = 0;
245
246	# workaround functions that we do not parse properly
247	if ($myfunc ~ /ripoffline/) {
248		dotrace = 0;
249		argcount = 2;
250	}
251	if ($myfunc ~ /wunctrl/) {
252		dotrace = 0;
253	}
254
255	call = "@@T((T_CALLED(\""
256	args = ""
257	comma = ""
258	num = 0;
259	pointer = 0;
260	va_list = 0;
261	varargs = 0;
262	argtype = ""
263	for (i = myfunc; i <= NF; i++) {
264		ch = $i;
265		if ( ch == "*" )
266			pointer = 1;
267		else if ( ch == "va_list" )
268			va_list = 1;
269		else if ( ch == "..." )
270			varargs = 1;
271		else if ( ch == "char" )
272			argtype = "char";
273		else if ( ch == "int" )
274			argtype = "int";
275		else if ( ch == "short" )
276			argtype = "short";
277		else if ( ch == "chtype" )
278			argtype = "chtype";
279		else if ( ch == "attr_t" || ch == "NCURSES_ATTR_T" )
280			argtype = "attr";
281
282		if ( ch == "," || ch == ")" ) {
283			if (va_list) {
284				call = call "%s"
285			} else if (varargs) {
286				call = call "%s"
287			} else if (pointer) {
288				if ( argtype == "char" ) {
289					call = call "%s"
290					comma = comma "_nc_visbuf2(" num ","
291					pointer = 0;
292				} else
293					call = call "%p"
294			} else if (argcount != 0) {
295				if ( argtype == "int" || argtype == "short" ) {
296					call = call "%d"
297					argtype = ""
298				} else if ( argtype != "" ) {
299					call = call "%s"
300					comma = comma "_trace" argtype "2(" num ","
301				} else {
302					call = call "%#lx"
303					comma = comma "(long)"
304				}
305			}
306			if (ch == ",") {
307				args = args comma "a" ++num;
308			} else if ( argcount != 0 ) {
309				if ( va_list ) {
310					args = args comma "\"va_list\""
311				} else if ( varargs ) {
312					args = args comma "\"...\""
313				} else {
314					args = args comma "z"
315				}
316			}
317			call = call ch
318			if (pointer == 0 && argcount != 0 && argtype != "" )
319				args = args ")"
320			if (args != "")
321				comma = ", "
322			pointer = 0;
323			argtype = ""
324		}
325		if ( i == 2 || ch == "(" )
326			call = call ch
327	}
328	call = call "\")"
329	if (args != "")
330		call = call ", " args
331	call = call ")); "
332
333	if (dotrace)
334		printf "%s", call
335
336	if (match($0, "^void"))
337		call = ""
338	else if (dotrace)
339		call = sprintf("return%s( ", returnType);
340	else
341		call = "@@return ";
342
343	call = call $myfunc "(";
344	for (i = 1; i < argcount; i++) {
345		if (i != 1)
346			call = call ", ";
347		call = call "a" i;
348	}
349	if ( argcount != 0 && $check != "..." ) {
350		if (argcount != 1)
351			call = call ", ";
352		call = call "z";
353	}
354	if (!match($0, "^void"))
355		call = call ") ";
356	if (dotrace)
357		call = call ")";
358	print call ";"
359
360	if (match($0, "^void"))
361		print "@@returnVoid;"
362	print "}";
363}
364EOF1
365
366cat >$AW2 <<EOF1
367BEGIN		{
368		print "/*"
369		print " * DO NOT EDIT THIS FILE BY HAND!"
370		printf " * It is generated by $0 %s.\n", "$USE"
371		if ( "$USE" == "generated" ) {
372			print " *"
373			print " * This is a file of trivial functions generated from macro"
374			print " * definitions in curses.h to satisfy the XSI Curses requirement"
375			print " * that every macro also exist as a callable function."
376			print " *"
377			print " * It will never be linked unless you call one of the entry"
378			print " * points with its normal macro definition disabled.  In that"
379			print " * case, if you have no shared libraries, it will indirectly"
380			print " * pull most of the rest of the library into your link image."
381		}
382		print " */"
383		print "#define NCURSES_ATTR_T int"
384		print "#include <curses.priv.h>"
385		print ""
386		}
387/^DECLARATIONS/	{start = 1; next;}
388		{if (start) print \$0;}
389END		{
390		if ( "$USE" != "generated" ) {
391			print "int main(void) { return 0; }"
392		}
393		}
394EOF1
395
396cat >$TMP <<EOF
397#include <ncurses_cfg.h>
398#undef NCURSES_NOMACROS
399#include <curses.h>
400
401DECLARATIONS
402
403EOF
404
405sed -n -f $ED1 \
406| sed -e 's/NCURSES_EXPORT(\(.*\)) \(.*\) (\(.*\))/\1 \2(\3)/' \
407| sed -f $ED2 \
408| $AWK -f $AW1 using=$USE \
409| sed \
410	-e 's/ [ ]*$//g' \
411	-e 's/^\([a-zA-Z_][a-zA-Z_]*[ *]*\)/\1 gen_/' \
412	-e 's/gen_$//' \
413	-e 's/  / /g' >>$TMP
414
415$preprocessor $TMP 2>/dev/null \
416| sed \
417	-e 's/  / /g' \
418	-e 's/^ //' \
419	-e 's/_Bool/NCURSES_BOOL/g' \
420| $AWK -f $AW2 \
421| sed -f $ED3 \
422| sed \
423	-e 's/^.*T_CALLED.*returnCode( \([a-z].*) \));/	return \1;/' \
424	-e 's/^.*T_CALLED.*returnCode( \((wmove.*) \));/	return \1;/' \
425	-e 's/gen_//' \
426	-e 's/^[ 	]*#/#/' \
427	-e '/#ident/d' \
428	-e '/#line/d' \
429| sed -f $ED4
430