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