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