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