1#!/bin/sh 2# 3# MKlib_gen.sh -- generate sources from curses.h macro definitions 4# 5# ($Id: MKlib_gen.sh,v 1.68 2020/08/23 00:02:29 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/[^0-9].*$//'` 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 cat >$ED1 <<EOF1 100/^extern.*implemented/{ 101 h 102 s/GCC_DEPRECATED([^)]*)// 103 s/NCURSES_SP_NAME(\([^)]*\))/NCURSES_SP_NAME___\1/ 104 h 105 s/^.*implemented:\([^ *]*\).*/P_POUNDCif_USE_\1_SUPPORT/p 106 g 107 s/^extern \([^;]*\);.*/\1/p 108 g 109 s/^.*implemented:\([^ *]*\).*/P_POUNDCendif/p 110} 111/^extern.*generated/{ 112 h 113 s/^.*generated:\([^ *]*\).*/P_POUNDCif_USE_\1_SUPPORT/p 114 g 115 s/^extern \([^;]*\);.*/\1/p 116 g 117 s/^.*generated:\([^ *]*\).*/P_POUNDCendif/p 118} 119EOF1 120else 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 = ($1 == "NCURSES_CONST") ? "CPtr" : "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 if (using == "implemented") { 246 printf "#undef %s\n", $myfunc; 247 } 248 print $0; 249 print "{"; 250 argcount = 1; 251 check = NF - 1; 252 if ($check == "void") 253 argcount = 0; 254 if (argcount != 0) { 255 for (i = 1; i <= NF; i++) 256 if ($i == ",") 257 argcount++; 258 } 259 260 # suppress trace-code for functions that we cannot do properly here, 261 # since they return data. 262 dotrace = 1; 263 if ($myfunc ~ /innstr/) 264 dotrace = 0; 265 if ($myfunc ~ /innwstr/) 266 dotrace = 0; 267 268 # workaround functions that we do not parse properly 269 if ($myfunc ~ /ripoffline/) { 270 dotrace = 0; 271 argcount = 2; 272 if ($myfunc ~ /NCURSES_SP_NAME/) { 273 argcount = 3; 274 } 275 } 276 if ($myfunc ~ /wunctrl/) { 277 dotrace = 0; 278 } 279 280 do_getstr = 0; 281 if ($myfunc ~ /get[n]?str/) { 282 do_getstr = 1; 283 } 284 285 call = "@@T((T_CALLED(\"" 286 args = "" 287 comma = "" 288 num = 0; 289 pointer = 0; 290 va_list = 0; 291 varargs = 0; 292 argtype = "" 293 for (i = myfunc; i <= NF; i++) { 294 ch = $i; 295 if ( ch == "*" ) { 296 pointer = 1; 297 } else if ( ch == "va_list" ) { 298 va_list = 1; 299 } else if ( ch == "..." ) { 300 varargs = 1; 301 } else if ( ch == "char" ) { 302 argtype = "char"; 303 } else if ( ch == "int" ) { 304 argtype = "int"; 305 } else if ( ch == "short" ) { 306 argtype = "short"; 307 } else if ( ch == "chtype" ) { 308 argtype = "chtype"; 309 } else if ( ch == "attr_t" || ch == "NCURSES_ATTR_T" ) { 310 argtype = "attr"; 311 } 312 313 if ( ch == "," || ch == ")" ) { 314 argcast = ""; 315 if (va_list) { 316 call = call "%s" 317 } else if (varargs) { 318 call = call "%s" 319 } else if (pointer) { 320 if ( argtype == "char" ) { 321 if (do_getstr) { 322 call = call "%p" 323 } else { 324 call = call "%s" 325 } 326 comma = comma "_nc_visbuf2(" num "," 327 pointer = 0; 328 } else { 329 call = call "%p" 330 comma = comma "(const void *)" 331 } 332 } else if (argcount != 0) { 333 if ( argtype == "int" || argtype == "short" ) { 334 call = call "%d" 335 argtype = "" 336 } else if ( argtype != "" ) { 337 call = call "%s" 338 comma = comma "_trace" argtype "2(" num "," 339 if (argtype == "attr") { 340 argcast = "(chtype)"; 341 } 342 } else { 343 call = call "%#lx" 344 comma = comma "(long)" 345 } 346 } 347 if (ch == ",") { 348 args = args comma "a" ++num; 349 } else if ( argcount != 0 ) { 350 if ( va_list ) { 351 args = args comma "\"va_list\"" 352 } else if ( varargs ) { 353 args = args comma "\"...\"" 354 } else { 355 args = args comma argcast "z" 356 } 357 } 358 call = call ch 359 if (pointer == 0 && argcount != 0 && argtype != "" ) 360 args = args ")" 361 if (args != "") 362 comma = ", " 363 pointer = 0; 364 argtype = "" 365 } 366 if ( i == myfunc || ch == "(" ) 367 call = call ch 368 } 369 call = call "\")" 370 if (args != "") 371 call = call ", " args 372 call = call ")); " 373 374 if (dotrace) 375 printf "%s\n\t@@", call 376 377 if (match($0, "^void")) { 378 call = "" 379 } else if (dotrace) { 380 call = sprintf("return%s( ", returnType); 381 if (returnCast != "") { 382 call = call returnCast; 383 } 384 } else { 385 call = "@@return "; 386 } 387 388 call = call $myfunc "("; 389 for (i = 1; i < argcount; i++) { 390 if (i != 1) 391 call = call ", "; 392 call = call "a" i; 393 } 394 if ( argcount != 0 && $check != "..." ) { 395 if (argcount != 1) 396 call = call ", "; 397 call = call "z"; 398 } 399 if (!match($0, "^void")) 400 call = call ") "; 401 if (dotrace) { 402 call = call ")"; 403 } 404 print call ";" 405 406 if (match($0, "^void")) 407 print "@@returnVoid;" 408 print "}"; 409} 410EOF1 411 412cat >$AW2 <<EOF1 413BEGIN { 414 printf "/* This file was generated by $0 $USE */\n" 415 print "" 416 print "/*" 417 print " * DO NOT EDIT THIS FILE BY HAND!" 418 if ( "$USE" == "generated" ) { 419 print " *" 420 print " * This is a file of trivial functions generated from macro" 421 print " * definitions in curses.h to satisfy the XSI Curses requirement" 422 print " * that every macro also exist as a callable function." 423 print " *" 424 print " * It will never be linked unless you call one of the entry" 425 print " * points with its normal macro definition disabled. In that" 426 print " * case, if you have no shared libraries, it will indirectly" 427 print " * pull most of the rest of the library into your link image." 428 } 429 print " */" 430 print "#define NCURSES_ATTR_T int" 431 print "#include <ncurses_cfg.h>" 432 print "" 433 print "#undef NCURSES_NOMACROS /* _this_ file uses macros */" 434 print "#define NCURSES_NOMACROS 1" 435 print "" 436 print "#include <curses.priv.h>" 437 print "" 438 } 439/^DECLARATIONS/ {start = 1; next;} 440 { 441 if (start) { 442 if ( "$USE" == "generated" ) { 443 print \$0; 444 } else if ( \$0 ~ /^[{}]?\$/ ) { 445 print \$0; 446 } else if ( \$0 ~ /;/ ) { 447 print \$0; 448 } else { 449 calls[start] = \$0; 450 print \$0; 451 start++; 452 } 453 } 454 } 455END { 456 if ( "$USE" != "generated" ) { 457 print "int main(void)" 458 print "{" 459 for (n = 1; n < start; ++n) { 460 value = calls[n]; 461 if ( value !~ /P_POUNDC/ ) { 462 gsub(/[ \t]+/," ",value); 463 sub(/^[0-9a-zA-Z_]+ /,"",value); 464 sub(/^\* /,"",value); 465 gsub(/[0-9a-zA-Z_]+ \* /,"",value); 466 gsub(/ (const) /," ",value); 467 gsub(/ (int|short|attr_t|chtype|wchar_t|NCURSES_BOOL|NCURSES_OUTC|NCURSES_OUTC_sp|va_list) /," ",value); 468 gsub(/ void /,"",value); 469 sub(/^/,"call_",value); 470 gsub(/ (a[0-9]|z) /, " 0 ", value); 471 gsub(/ int[ \t]*[(][^)]+[)][(][^)]+[)]/, "0", value); 472 printf "\t%s;\n", value; 473 } else { 474 print value; 475 } 476 } 477 print " return 0;" 478 print "}" 479 } 480 } 481EOF1 482 483cat >$TMP <<EOF 484#include <ncurses_cfg.h> 485#undef NCURSES_NOMACROS 486#include <curses.h> 487#include <term.h> 488#include <unctrl.h> 489 490DECLARATIONS 491 492EOF 493 494sed -n -f $ED1 \ 495| sed -e 's/NCURSES_EXPORT(\(.*\)) \(.*\) (\(.*\))/\1 \2(\3)/' \ 496| sed -f $ED2 \ 497| "$AWK" -f $AW1 using="$USE" \ 498| sed \ 499 -e 's/ [ ]*$//g' \ 500 -e 's/^\([a-zA-Z_][a-zA-Z_]*[ *]*\)/\1 gen_/' \ 501 -e 's/gen_$//' \ 502 -e 's/ / /g' >>$TMP 503 504$preprocessor $TMP 2>/dev/null \ 505| sed \ 506 -e 's/ / /g' \ 507 -e 's/^ //' \ 508 -e 's/_Bool/NCURSES_BOOL/g' \ 509| "$AWK" -f $AW2 \ 510| sed -f $ED3 \ 511| sed \ 512 -e 's/^.*T_CALLED.*returnCode( \([a-z].*) \));/ return \1;/' \ 513 -e 's/^.*T_CALLED.*returnCode( \((wmove.*) \));/ return \1;/' \ 514 -e 's/gen_//' \ 515 -e 's/^[ ]*#/#/' \ 516 -e '/#ident/d' \ 517 -e '/#line/d' \ 518| sed -f $ED4 519