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