1#!/bin/sh
2#
3# MKlib_gen.sh -- generate sources from curses.h macro definitions
4#
5# ($Id: MKlib_gen.sh,v 1.43 2011/01/22 19:47:29 tom Exp $)
6#
7##############################################################################
8# Copyright (c) 1998-2010,2011 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.
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/NCURSES_SP_NAME(\([^)]*\))/NCURSES_SP_NAME___\1/
86	h
87	s/^.*implemented:\([^ 	*]*\).*/P_POUNDCif_USE_\1_SUPPORT/p
88	g
89	s/^extern \([^;]*\);.*/\1/p
90	g
91	s/^.*implemented:\([^ 	*]*\).*/P_POUNDCendif/p
92}
93/^extern.*generated/{
94	h
95	s/^.*generated:\([^ 	*]*\).*/P_POUNDCif_USE_\1_SUPPORT/p
96	g
97	s/^extern \([^;]*\);.*/\1/p
98	g
99	s/^.*generated:\([^ 	*]*\).*/P_POUNDCendif/p
100}
101EOF1
102else
103	CALL=""
104	cat >$ED1 <<EOF1
105/^extern.*${ALL}/{
106	h
107	s/^.*${ALL}:\([^ 	*]*\).*/P_POUNDCif_USE_\1_SUPPORT/p
108	g
109	s/^extern \([^;]*\);.*/\1/p
110	g
111	s/^.*${ALL}:\([^ 	*]*\).*/P_POUNDCendif/p
112}
113EOF1
114fi
115
116cat >$ED2 <<EOF2
117/^P_/b nc
118/(void)/b nc
119	s/,/ a1% /
120	s/,/ a2% /
121	s/,/ a3% /
122	s/,/ a4% /
123	s/,/ a5% /
124	s/,/ a6% /
125	s/,/ a7% /
126	s/,/ a8% /
127	s/,/ a9% /
128	s/,/ a10% /
129	s/,/ a11% /
130	s/,/ a12% /
131	s/,/ a13% /
132	s/,/ a14% /
133	s/,/ a15% /
134	s/*/ * /g
135	s/%/ , /g
136	s/)/ z)/
137	s/\.\.\. z)/...)/
138:nc
139	s/(/ ( /
140	s/)/ )/
141EOF2
142
143cat >$ED3 <<EOF3
144/^P_/{
145	s/^P_POUNDCif_/#if /
146	s/^P_POUNDCendif/#endif/
147	s/^P_//
148	b done
149}
150	s/		*/ /g
151	s/  */ /g
152	s/ ,/,/g
153	s/( /(/g
154	s/ )/)/g
155	s/ gen_/ /
156	s/^[ 	]*@[ 	]*@[ 	]*/	/
157:done
158EOF3
159
160if test "$USE" = generated ; then
161cat >$ED4 <<EOF
162	s/^\(.*\) \(.*\) (\(.*\))\$/NCURSES_EXPORT(\1) (\2) (\3)/
163EOF
164else
165cat >$ED4 <<EOF
166/^\(.*\) \(.*\) (\(.*\))\$/ {
167	h
168	s/^\(.*\) \(.*\) (\(.*\))\$/extern \1 call_\2 (\3);/
169	p
170	g
171	s/^\(.*\) \(.*\) (\(.*\))\$/\1 call_\2 (\3)/
172	}
173s/\([^_]\)NCURSES_SP_NAME___\([a-zA-Z][a-zA-Z_]*\)/\1NCURSES_SP_NAME(\2)/g
174EOF
175fi
176
177cat >$AW1 <<\EOF1
178BEGIN	{
179		skip=0;
180	}
181/^P_POUNDCif/ {
182		print "\n"
183		print $0
184		skip=0;
185}
186/^P_POUNDCendif/ {
187		print $0
188		skip=1;
189}
190$0 !~ /^P_/ {
191	if (skip)
192		print "\n"
193	skip=1;
194
195	first=$1
196	for (i = 1; i <= NF; i++) {
197		if ( $i != "NCURSES_CONST" ) {
198			first = i;
199			break;
200		}
201	}
202	second = first + 1;
203	returnCast = "";
204	if ( $first == "chtype" ) {
205		returnType = "Chtype";
206	} else if ( $first == "SCREEN" ) {
207		returnType = "SP";
208	} else if ( $first == "WINDOW" ) {
209		returnType = "Win";
210	} else if ( $first == "attr_t" || $second == "attrset" || $second == "standout" || $second == "standend" || $second == "wattrset" || $second == "wstandout" || $second == "wstandend" ) {
211		returnType = "IntAttr";
212		returnCast = "(attr_t)";
213	} else if ( $first == "bool" || $first == "NCURSES_BOOL" ) {
214		returnType = "Bool";
215	} else if ( $second == "*" ) {
216		returnType = "Ptr";
217	} else {
218		returnType = "Code";
219	}
220	myfunc = second;
221	for (i = second; i <= NF; i++) {
222		if ($i != "*") {
223			myfunc = i;
224			break;
225		}
226	}
227	print $0;
228	print "{";
229	argcount = 1;
230	check = NF - 1;
231	if ($check == "void")
232		argcount = 0;
233	if (argcount != 0) {
234		for (i = 1; i <= NF; i++)
235			if ($i == ",")
236				argcount++;
237	}
238
239	# suppress trace-code for functions that we cannot do properly here,
240	# since they return data.
241	dotrace = 1;
242	if ($myfunc ~ /innstr/)
243		dotrace = 0;
244	if ($myfunc ~ /innwstr/)
245		dotrace = 0;
246
247	# workaround functions that we do not parse properly
248	if ($myfunc ~ /ripoffline/) {
249		dotrace = 0;
250		argcount = 2;
251		if ($myfunc ~ /NCURSES_SP_NAME/) {
252			argcount = 3;
253		}
254	}
255	if ($myfunc ~ /wunctrl/) {
256		dotrace = 0;
257	}
258
259	call = "@@T((T_CALLED(\""
260	args = ""
261	comma = ""
262	num = 0;
263	pointer = 0;
264	va_list = 0;
265	varargs = 0;
266	argtype = ""
267	for (i = myfunc; i <= NF; i++) {
268		ch = $i;
269		if ( ch == "*" ) {
270			pointer = 1;
271		} else if ( ch == "va_list" ) {
272			va_list = 1;
273		} else if ( ch == "..." ) {
274			varargs = 1;
275		} else if ( ch == "char" ) {
276			argtype = "char";
277		} else if ( ch == "int" ) {
278			argtype = "int";
279		} else if ( ch == "short" ) {
280			argtype = "short";
281		} else if ( ch == "chtype" ) {
282			argtype = "chtype";
283		} else if ( ch == "attr_t" || ch == "NCURSES_ATTR_T" ) {
284			argtype = "attr";
285		}
286
287		if ( ch == "," || ch == ")" ) {
288			argcast = "";
289			if (va_list) {
290				call = call "%s"
291			} else if (varargs) {
292				call = call "%s"
293			} else if (pointer) {
294				if ( argtype == "char" ) {
295					call = call "%s"
296					comma = comma "_nc_visbuf2(" num ","
297					pointer = 0;
298				} else {
299					call = call "%p"
300					comma = comma "(const void *)"
301				}
302			} else if (argcount != 0) {
303				if ( argtype == "int" || argtype == "short" ) {
304					call = call "%d"
305					argtype = ""
306				} else if ( argtype != "" ) {
307					call = call "%s"
308					comma = comma "_trace" argtype "2(" num ","
309					if (argtype == "attr") {
310						argcast = "(chtype)";
311					}
312				} else {
313					call = call "%#lx"
314					comma = comma "(long)"
315				}
316			}
317			if (ch == ",") {
318				args = args comma "a" ++num;
319			} else if ( argcount != 0 ) {
320				if ( va_list ) {
321					args = args comma "\"va_list\""
322				} else if ( varargs ) {
323					args = args comma "\"...\""
324				} else {
325					args = args comma argcast "z"
326				}
327			}
328			call = call ch
329			if (pointer == 0 && argcount != 0 && argtype != "" )
330				args = args ")"
331			if (args != "")
332				comma = ", "
333			pointer = 0;
334			argtype = ""
335		}
336		if ( i == myfunc || ch == "(" )
337			call = call ch
338	}
339	call = call "\")"
340	if (args != "")
341		call = call ", " args
342	call = call ")); "
343
344	if (dotrace)
345		printf "%s", call
346
347	if (match($0, "^void")) {
348		call = ""
349	} else if (dotrace) {
350		call = sprintf("return%s( ", returnType);
351		if (returnCast != "") {
352			call = call returnCast;
353		}
354	} else {
355		call = "@@return ";
356	}
357
358	call = call $myfunc "(";
359	for (i = 1; i < argcount; i++) {
360		if (i != 1)
361			call = call ", ";
362		call = call "a" i;
363	}
364	if ( argcount != 0 && $check != "..." ) {
365		if (argcount != 1)
366			call = call ", ";
367		call = call "z";
368	}
369	if (!match($0, "^void"))
370		call = call ") ";
371	if (dotrace) {
372		call = call ")";
373	}
374	print call ";"
375
376	if (match($0, "^void"))
377		print "@@returnVoid;"
378	print "}";
379}
380EOF1
381
382cat >$AW2 <<EOF1
383BEGIN		{
384		print "/*"
385		print " * DO NOT EDIT THIS FILE BY HAND!"
386		printf " * It is generated by $0 %s.\n", "$USE"
387		if ( "$USE" == "generated" ) {
388			print " *"
389			print " * This is a file of trivial functions generated from macro"
390			print " * definitions in curses.h to satisfy the XSI Curses requirement"
391			print " * that every macro also exist as a callable function."
392			print " *"
393			print " * It will never be linked unless you call one of the entry"
394			print " * points with its normal macro definition disabled.  In that"
395			print " * case, if you have no shared libraries, it will indirectly"
396			print " * pull most of the rest of the library into your link image."
397		}
398		print " */"
399		print "#define NCURSES_ATTR_T int"
400		print "#include <ncurses_cfg.h>"
401		print ""
402		print "#undef NCURSES_NOMACROS	/* _this_ file uses macros */"
403		print ""
404		print "#include <curses.priv.h>"
405		print ""
406		print "#undef vw_scanw"
407		print "#undef vwscanw"
408		print ""
409		print "#undef vw_printw"
410		print "#undef vwprintw"
411		}
412/^DECLARATIONS/	{start = 1; next;}
413		{if (start) print \$0;}
414END		{
415		if ( "$USE" != "generated" ) {
416			print "int main(void) { return 0; }"
417		}
418		}
419EOF1
420
421cat >$TMP <<EOF
422#include <ncurses_cfg.h>
423#undef NCURSES_NOMACROS
424#include <curses.h>
425
426DECLARATIONS
427
428EOF
429
430sed -n -f $ED1 \
431| sed -e 's/NCURSES_EXPORT(\(.*\)) \(.*\) (\(.*\))/\1 \2(\3)/' \
432| sed -f $ED2 \
433| $AWK -f $AW1 using=$USE \
434| sed \
435	-e 's/ [ ]*$//g' \
436	-e 's/^\([a-zA-Z_][a-zA-Z_]*[ *]*\)/\1 gen_/' \
437	-e 's/gen_$//' \
438	-e 's/  / /g' >>$TMP
439
440cat >$ED1 <<EOF
441s/  / /g
442s/^ //
443s/ $//
444s/P_NCURSES_BOOL/NCURSES_BOOL/g
445EOF
446
447# A patch discussed here:
448#	https://gcc.gnu.org/ml/gcc-patches/2014-06/msg02185.html
449# introduces spurious #line markers.  Work around that by ignoring the system's
450# attempt to define "bool" and using our own symbol here.
451sed -e 's/bool/P_NCURSES_BOOL/g' $TMP > $ED2
452cat $ED2 >$TMP
453
454$preprocessor $TMP 2>/dev/null \
455| sed -f $ED1 \
456| $AWK -f $AW2 \
457| sed -f $ED3 \
458| sed \
459	-e 's/^.*T_CALLED.*returnCode( \([a-z].*) \));/	return \1;/' \
460	-e 's/^.*T_CALLED.*returnCode( \((wmove.*) \));/	return \1;/' \
461	-e 's/gen_//' \
462	-e 's/^[ 	]*#/#/' \
463	-e '/#ident/d' \
464	-e '/#line/d' \
465| sed -f $ED4
466