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