options.awk 28.8 KB
Newer Older
1 2 3
#!/bin/awk -f
# scripts/options.awk - library build configuration control
#
4
# last changed in libpng version 1.5.7 - December 15, 2011
5
#
6
# Copyright (c) 1998-2011 Glenn Randers-Pehrson
7 8 9 10 11 12 13 14
#
# This code is released under the libpng license.
# For conditions of distribution and use, see the disclaimer
# and license in png.h

# The output of this script is written to the file given by
# the variable 'out'.  The script is run twice, once with
# an intermediate output file, 'options.tmp' then again on
15
# that file to produce the final output:
16
#
17 18 19 20 21
#  awk -f scripts/options.awk out=options.tmp scripts/options.dfa 1>&2
#  awk -f scripts/options.awk out=options.dfn options.tmp 1>&2
#
# Some options may be specified on the command line:
#
22 23 24 25
#  deb=1            Causes debugging to be output
#  logunsupported=1 Causes all options to be recorded in the output
#  everything=off   Causes all options to be disabled by default
#  everything=on    Causes all options to be enabled by default
26
#
27 28
# If awk fails on your platform, try nawk instead.
#
29 30
# These options may also be specified in the original input file (and
# are copied to the preprocessed file).
31 32

BEGIN{
33
   out=""                       # intermediate, preprocessed, file
34
   pre=-1                       # preprocess (first line)
35 36
   version="libpng version unknown" # version information
   version_file=""              # where to find the version
37
   err=0                        # in-line exit sets this
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
   # The following definitions prevent the C preprocessor noticing the lines
   # that will be in the final output file.  Some C preprocessors tokenise
   # the lines, for example by inserting spaces around operators, and all
   # C preprocessors notice lines that start with '#', most remove comments.
   # The technique adopted here is to make the final output lines into
   # C strings (enclosed in double quotes), preceeded by PNG_DFN.  As a
   # consequence the output cannot contain a 'raw' double quote - instead put
   # @' in, this will be replaced by a single " afterward.  See the parser
   # script dfn.awk for more capabilities (not required here).  Note that if
   # you need a " in a 'setting' in pnglibconf.dfa it must also be @'!
   dq="@'"                      # For a single double quote
   start=" PNG_DFN \""          # Start stuff to output (can't contain a "!)
   end="\" "                    # End stuff to output
   subs="@\" "                  # Substitute start (substitute a C macro)
   sube=" \"@"                  # Substitute end
   comment=start "/*"           # Comment start
54
   cend="*/" end                # Comment end
55 56 57 58 59
   def=start "#define PNG_"     # Arbitrary define
   sup="_SUPPORTED" end         # end supported option
   und=comment "#undef PNG_"    # Unsupported option
   une="_SUPPORTED" cend        # end unsupported option
   error=start "ERROR:"         # error message, terminate with 'end'
60 61 62 63 64

   # Variables
   deb=0                        # debug - set on command line
   everything=""                # do not override defaults
   logunsupported=0             # write unsupported options too
65

66 67 68 69 70 71 72 73 74 75 76 77 78
   # Precreate arrays
   option[""] = ""    # list of all options: default enabled/disabled
   done[""] = 1       # marks option as having been output
   requires[""] = ""  # requires by option
   iffs[""] = ""      # if by option
   enabledby[""] = "" # options that enable it by option
   setting[""] = ""   # requires by setting
   defaults[""] = ""  # used for a defaulted value
   doneset[""] = 1    # marks setting as having been output
   r[""] = ""         # Temporary array

   # For decorating the output file
   protect = ""
79 80
}

81
# The output file must be specified before any input:
82
out == "" {
83 84 85
   print "out=output.file must be given on the command line"
   err = 1
   exit 1
86 87 88 89 90 91
}

# The very first line indicates whether we are reading pre-processed
# input or not, this must come *first* because 'PREPROCESSED' needs
# to be the very first line in the temporary file.
pre == -1{
92 93 94 95 96 97 98 99
   if ($0 == "PREPROCESSED") {
      pre = 0
      next
   } else {
      pre = 1
      print "PREPROCESSED" >out
      # And fall through to continue processing
   }
100 101
}

102 103 104 105 106 107 108 109 110 111 112 113 114 115
# While pre-processing if version is set to "search" look for a version string
# in the following file.
pre && version == "search" && version_file == ""{
   version_file = FILENAME
}

pre && version == "search" && version_file != FILENAME{
   print "version string not found in", version_file
   err = 1
   exit 1
}

pre && version == "search" && $0 ~ /^ \* libpng version/{
   version = substr($0, 4)
116
   print "version =", version >out
117
   next
118 119
}

120
pre && FILENAME == version_file{
121 122 123
   next
}

124 125 126 127 128 129 130 131 132
# variable=value
#   Sets the given variable to the given value (the syntax is fairly
#   free form, except for deb (you are expected to understand how to
#   set the debug variable...)
#
#   This happens before the check on 'pre' below skips most of the
#   rest of the actions, so the variable settings happen during
#   preprocessing but are recorded in the END action too.  This
#   allows them to be set on the command line too.
133 134 135 136 137
$0 ~ /^[ 	]*version[ 	]*=/{
   sub(/^[  ]*version[  ]*=[  ]*/, "")
   version = $0
   next
}
138
$0 ~ /^[ 	]*everything[ 	=]*off[ 	]*$/{
139 140
   everything = "off"
   next
141 142
}
$0 ~ /^[ 	]*everything[ 	=]*on[ 	]*$/{
143 144
   everything = "on"
   next
145 146
}
$0 ~ /^[ 	]*logunsupported[ 	=]*0[ 	]*$/{
147 148
   logunsupported = 0
   next
149 150
}
$0 ~ /^[ 	]*logunsupported[ 	=]*1[ 	]*$/{
151 152
   logunsupported = 1
   next
153 154
}
$1 == "deb" && $2 == "=" && NF == 3{
155 156
   deb = $3
   next
157 158
}

159 160
# Preprocessing - this just copies the input file with lines
# that need preprocessing (just chunk at present) expanded
161
# The bare "pre" instead of "pre != 0" crashes under Sunos awk
162
pre && $1 != "chunk"{
163 164
   print >out
   next
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
}

# The first characters of the line determine how it is processed,
# leading spaces are ignored.  In general tokens that are not
# keywords are the names of options.  An option 'name' is
# controlled by the definition of the corresponding macros:
#
#   PNG_name_SUPPORTED    The option is turned on
#   PNG_NO_name
#   PNG_NO_name_SUPPORTED If the first macro is not defined
#                         either of these will turn the option off
#
# If none of these macros are defined the option is turned on, unless
# the keyword 'off' is given in a line relating to the option.  The
# keyword 'on' can also be given, but it will be ignored (since it is
# the default.)
#
# In the syntax below a 'name' is indicated by "NAME", other macro
# values are indicated by "MACRO", as with "NAME" the leading "PNG_"
# is omitted, but in this case the "NO_" prefix and the "_SUPPORTED"
# suffix are never used.
#
# Each line is introduced by a keyword - the first non-space characters
# on the line.  A line starting with a '#' is a comment - it is totally
# ignored.  Keywords are as follows, a NAME, is simply a macro name
# without the leading PNG_, PNG_NO_ or the trailing _SUPPORTED.

$1 ~ /^#/ || $0 ~ /^[ 	]*$/{
193
   next
194 195 196 197 198 199
}

# com <comment>
#   The whole line is placed in the output file as a comment with
#   the preceding 'com' removed
$1 == "com"{
200 201 202 203 204 205 206
   if (NF > 1) {
      # sub(/^[ 	]*com[ 	]*/, "")
      $1 = ""
      print comment, $0, cend >out
   } else
      print start end >out
   next
207 208
}

209 210 211 212 213 214 215 216 217 218 219 220 221
# version
#   Inserts a version comment
$1 == "version" && NF == 1{
   if (version == "") {
      print "ERROR: no version string set"
      err = 1 # prevent END{} running
      exit 1
   }

   print comment, version, cend >out
   next
}

222 223 224 225 226 227
# file output input protect
#   Informational: the official name of the input file (without
#   make generated local directories), the official name of the
#   output file and, if required, a name to use in a protection
#   macro for the contents.
$1 == "file" && NF >= 2{
228 229 230 231 232 233 234 235 236 237
   print comment, $2, cend >out
   print comment, "Machine generated file: DO NOT EDIT", cend >out
   if (NF >= 3)
      print comment, "Derived from:", $3, cend >out
   protect = $4
   if (protect != "") {
      print start "#ifndef", protect end >out
      print start "#define", protect end >out
   }
   next
238 239
}

240 241
# option NAME ( (requires|enables|if) NAME* | on | off | disabled )*
#   Declares an option 'NAME' and describes its default setting (disabled)
242 243 244 245
#   and its relationship to other options.  The option is disabled
#   unless *all* the options listed after 'requires' are set and at
#   least one of the options listed after 'if' is set.  If the
#   option is set then it turns on all the options listed after 'enables'.
246 247 248 249 250 251 252
#
#   Note that "enables" takes priority over the required/if/disabled/off
#   setting of the target option.
#
#   The definition file may list an option as 'disabled': off by default,
#   otherwise the option is enabled: on by default.  A later (and it must
#   be later) entry may turn an option on or off explicitly.
253 254

$1 == "option" && NF >= 2{
255 256 257
   opt = $2
   sub(/,$/,"",opt)
   onoff = option[opt]  # records current (and the default is "", enabled)
258
   key = ""
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
   istart = 3
   do {
      if (istart == 1) {     # continuation line
         val = getline

         if (val != 1) { # error reading it
            if (val == 0)
               print "option", opt ": ERROR: missing contination line"
            else
               print "option", opt ": ERROR: error reading continuation line"

            # This is a hard error
            err = 1 # prevent END{} running
            exit 1
         }
      }

      for (i=istart; i<=NF; ++i) {
         val=$(i)
         sub(/,$/,"",val)
         if (val == "on" || val == "off" || val == "disabled") {
            key = ""
            if (onoff != val) {
               # on or off can zap disabled or enabled:
               if (onoff == "" || (onoff == "disabled" || onoff == "enabled") &&
                   (val == "on" || val == "off")) {
                  # It's easy to mis-spell the option when turning it
                  # on or off, so warn about it here:
                  if (onoff == "" && (val == "on" || val == "off")) {
                     print "option", opt ": ERROR: turning unrecognized option", val
                     # For the moment error out - it is safer
                     err = 1 # prevent END{} running
                     exit 1
                  }
                  onoff = val
               } else {
                  # Print a message, otherwise the error
                  # below is incomprehensible
                  print "option", opt ": currently", onoff ": attempt to turn", val
                  break
299 300
               }
            }
301 302 303 304 305 306 307 308 309 310 311 312 313 314
         } else if (val == "requires" || val == "if" || val == "enables") {
            key = val
         } else if (key == "requires") {
            requires[opt] = requires[opt] " " val
         } else if (key == "if") {
            iffs[opt] = iffs[opt] " " val
         } else if (key == "enables") {
            enabledby[val] = enabledby[val] " " opt
         } else
            break # bad line format
      }

      istart = 1
   } while (i > NF && $0 ~ /,$/)
315 316 317 318

   if (i > NF) {
      # Set the option, defaulting to 'enabled'
      if (onoff == "") onoff = "enabled"
319
      option[opt] = onoff
320 321 322
      next
   }
   # Else fall through to the error handler
323 324
}

325
# chunk NAME [requires OPT] [enables LIST] [on|off|disabled]
326 327 328
#   Expands to the 'option' settings appropriate to the reading and
#   writing of an ancilliary PNG chunk 'NAME':
#
329
#   option READ_NAME requires READ_ANCILLARY_CHUNKS [READ_OPT]
330
#   option READ_NAME enables NAME LIST
331 332
#   [option READ_NAME off]
#   option WRITE_NAME requires WRITE_ANCILLARY_CHUNKS [WRITE_OPT]
333
#   option WRITE_NAME enables NAME LIST
334
#   [option WRITE_NAME off]
335

336
pre != 0 && $1 == "chunk" && NF >= 2{
337 338
   # 'chunk' is handled on the first pass by writing appropriate
   # 'option' lines into the intermediate file.
339 340
   opt = $2
   sub(/,$/,"",opt)
341 342 343
   onoff = ""
   reqread = ""
   reqwrite = ""
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
   enables = ""
   req = 0
   istart = 3
   do {
      if (istart == 1) {     # continuation line
         val = getline

         if (val != 1) { # error reading it
            if (val == 0)
               print "chunk", opt ": ERROR: missing contination line"
            else
               print "chunk", opt ": ERROR: error reading continuation line"

            # This is a hard error
            err = 1 # prevent END{} running
            exit 1
         }
      }

363
      # read the keywords/additional OPTS
364 365 366 367 368
      for (i=istart; i<=NF; ++i) {
         val = $(i)
         sub(/,$/,"",val)
         if (val == "on" || val == "off" || val == "disabled") {
            if (onoff != val) {
369
               if (onoff == "")
370
                  onoff = val
371 372 373
               else
                  break # on/off conflict
            }
374 375
            req = 0
         } else if (val == "requires")
376
            req = 1
377 378 379 380 381 382 383 384
         else if (val == "enables")
            req = 2
         else if (req == 1){
            reqread = reqread " READ_" val
            reqwrite = reqwrite " WRITE_" val
         } else if (req == 2)
            enables = enables " " val
         else
385
            break # bad line: handled below
386
      }
387 388 389

      istart = 1
   } while (i > NF && $0 ~ /,$/)
390 391 392

   if (i > NF) {
      # Output new 'option' lines to the intermediate file (out)
393 394
      print "option READ_" opt, "requires READ_ANCILLARY_CHUNKS" reqread, "enables", opt enables , onoff >out
      print "option WRITE_" opt, "requires WRITE_ANCILLARY_CHUNKS" reqwrite, "enables", opt enables, onoff >out
395 396 397
      next
   }
   # Else hit the error handler below - bad line format!
398 399 400
}

# setting MACRO ( requires MACRO* )* [ default VALUE ]
401 402 403 404 405 406 407 408
#   Behaves in a similar way to 'option' without looking for NO_ or
#   _SUPPORTED; the macro is enabled if it is defined so long as all
#   the 'requires' macros are also defined.  The definitions may be
#   empty, an error will be issued if the 'requires' macros are
#   *not* defined.  If given the 'default' value is used if the
#   macro is not defined.  The default value will be re-tokenised.
#   (BTW: this is somewhat restrictive, it mainly exists for the
#   support of non-standard configurations and numeric parameters,
409
#   see the uses in scripts/options.dat
410 411

$1 == "setting" && (NF == 2 || NF >= 3 && ($3 == "requires" || $3 == "default")){
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
   reqs = ""
   deflt = ""
   isdef = 0
   key = ""
   for (i=3; i<=NF; ++i)
      if ($(i) == "requires" || $(i) == "default") {
         key = $(i)
         if (key == "default") isdef = 1
      } else if (key == "requires")
         reqs = reqs " " $(i)
      else if (key == "default")
         deflt = deflt " " $(i)
      else
         break # Format error, handled below

   setting[$2] = reqs
   # NOTE: this overwrites a previous value silently
   if (isdef && deflt == "")
      deflt = " " # as a flag to force output
   defaults[$2] = deflt
   next
433 434 435 436
}

# The order of the dependency lines (option, chunk, setting) is irrelevant
# - the 'enables', 'requires' and 'if' settings will be used to determine
437
# the correct order in the output and the final values in pnglibconf.h are
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
# not order dependent.  'requires' and 'if' entries take precedence over
# 'enables' from other options; if an option requires another option it
# won't be set regardless of any options that enable it unless the other
# option is also enabled.
#
# Similarly 'enables' trumps a NO_ definition in CFLAGS or pngusr.h
#
# For simplicity cycles in the definitions are regarded as errors,
# even if they are not ambiguous.
# A given NAME can be specified in as many 'option' lines as required, the
# definitions are additive.

# For backwards compatibility equivalent macros may be listed thus:
#
# = [NO_]NAME MACRO
#   Makes -DMACRO equivalent to -DPNG_NO_NAME or -DPNG_NAME_SUPPORTED
#   as appropriate.
#
# The definition is injected into the C compiler input when encountered
# in the second pass (so all these definitions appear *after* the @
# lines!)
#
# 'NAME' is as above, but 'MACRO' is the full text of the equivalent
# old, deprecated, macro.

$1 == "=" && NF == 3{
464 465 466 467 468 469 470
   print "#ifdef PNG_" $3 >out
   if ($2 ~ /^NO_/)
      print "#   define PNG_" $2 >out
   else
      print "#   define PNG_" $2 "_SUPPORTED" >out
   print "#endif" >out
   next
471 472 473 474 475 476 477
}

# Lines may be injected into the C compiler input by preceding them
# with an "@" character.  The line is copied with just the leading
# @ removed.

$1 ~ /^@/{
478 479 480 481
   # sub(/^[ 	]*@/, "")
   $1 = substr($1, 2)
   print >out
   next
482 483 484 485 486 487
}

# Check for unreognized lines, because of the preprocessing chunk
# format errors will be detected on the first pass independent of
# any other format errors.
{
488 489 490
   print "options.awk: bad line (" NR "):", $0
   err = 1 # prevent END{} running
   exit 1
491 492 493
}

# For checking purposes names that start with "ok_" or "fail_" are
494
# not output to pnglibconf.h and must be either enabled or disabled
495 496 497 498 499 500
# respectively for the build to succeed.  This allows interdependencies
# between options of the form "at least one of" or "at most one of"
# to be checked.  For example:
#
# option FLOATING_POINT enables ok_math
# option FIXED_POINT enables ok_math
501 502
#   This ensures that at least one of FLOATING_POINT and FIXED_POINT
#   must be set for the build to succeed.
503 504
#
# option fail_math requires FLOATING_POINT FIXED_POINT
505 506
#   This means the build will fail if *both* FLOATING_POINT and
#   FIXED_POINT are set (this is an example; in fact both are allowed.)
507 508 509 510 511
#
# If all these options were given the build would require exactly one
# of the names to be enabled.

END{
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
   # END{} gets run on an exit (a traditional awk feature)
   if (err) exit 1

   if (pre) {
      # Record the final value of the variables
      print "deb =", deb >out
      if (everything != "") {
         print "everything =", everything >out
      }
      print "logunsupported =", logunsupported >out
      exit 0
   }

   # Do the 'setting' values first, the algorithm the standard
   # tree walk (O(1)) done in an O(2) while/for loop; interations
   # settings x depth, outputing the deepest required macros
   # first.
   print "" >out
   print "/* SETTINGS */" >out
   print comment, "settings", cend >out
532 533
   # Sort (in dfn.awk) on field 2, the setting name
   print "PNG_DFN_START_SORT 2" >out
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
   finished = 0
   while (!finished) {
      finished = 1
      movement = 0 # done nothing
      for (i in setting) if (!doneset[i]) {
         nreqs = split(setting[i], r)
         if (nreqs > 0) {
            for (j=1; j<=nreqs; ++j) if (!doneset[r[j]]) {
               break
            }
            if (j<=nreqs) {
               finished = 0
               continue # try a different setting
            }
         }

         # All the requirements have been processed, output
         # this setting.
         if (deb) print "setting", i
553 554 555 556 557 558 559
         deflt = defaults[i]
         # A leading @ means leave it unquoted so the preprocessor
         # can substitute the build time value
         if (deflt ~ /^ @/)
            deflt = " " subs substr(deflt, 3) sube
         # Remove any spurious trailing spaces
         sub(/ *$/,"",deflt)
560 561 562
         print "" >out
         print "/* setting: ", i >out
         print " *   requires:" setting[i] >out
563
         print " *   default: ", defaults[i] defltinfo, "*/" >out
564 565 566 567 568 569 570 571 572 573 574
         if (defaults[i] == "") { # no default, only check if defined
            print "#ifdef PNG_" i >out
         }
         for (j=1; j<=nreqs; ++j) {
            print "# ifndef PNG_" r[j] >out
            print error, i, "requires", r[j] end >out
            print "# endif" >out
         }
         if (defaults[i] != "") { # default handling
            print "#ifdef PNG_" i >out
         }
575 576
         # PNG_<i> is defined, so substitute the value:
         print def i, subs "PNG_" i sube end >out
577 578 579 580
         if (defaults[i] != "") {
            print "#else /*default*/" >out
            # And add the default definition for the benefit
            # of later settings an options test:
581 582
            print "# define PNG_" i deflt >out
            print def i deflt end >out
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
         }
         print "#endif" >out

         doneset[i] = 1
         ++movement
      }

      if (!finished && !movement) {
         print "setting: loop or missing setting in 'requires', cannot process:"
         for (i in setting) if (!doneset[i]) {
            print "  setting", i, "requires" setting[i]
         }
         exit 1
      }
   }
598
   print "PNG_DFN_END_SORT" >out
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
   print comment, "end of settings", cend >out

   # Now do the options - somewhat more complex.  The dependency
   # tree is thus:
   #
   #   name     >     name
   #   name requires  name
   #   name if        name
   #   name enabledby name
   #
   # First build a list 'tree' by option of all the things on which
   # it depends.
   print "" >out
   print "/* OPTIONS */" >out
   print comment, "options", cend >out
   for (opt in enabledby) tree[opt] = 1  # may not be explicit options
   for (opt in option) if (opt != "") {
      o = option[opt]
      # option should always be one of the following values
      if (o != "on" && o != "off" && o != "disabled" && o != "enabled") {
         print "internal option error (" o ")"
         exit 1
      }
      tree[opt] = ""   # so unlisted options marked
   }
   for (opt in tree) if (opt != "") {
      if (tree[opt] == 1) {
         tree[opt] = ""
         if (option[opt] != "") {
            print "internal error (1)"
            exit 1
         }
         # Macros only listed in 'enables' remain off unless
         # one of the enabling macros is on.
         option[opt] = "disabled"
      }

      split("", list) # clear 'list'
      # Now add every requires, iffs or enabledby entry to 'list'
      # so that we can add a unique list of requirements to tree[i]
      split(requires[opt] iffs[opt] enabledby[opt], r)
      for (i in r) list[r[i]] = 1
      for (i in list) tree[opt] = tree[opt] " " i
   }

   # print the tree for extreme debugging
   if (deb > 2) for (i in tree) if (i != "") print i, "depends-on" tree[i]

   # Ok, now check all options marked explicitly 'on' or 'off':
   #
   # If an option[opt] is 'on' then turn on all requires[opt]
   # If an option[opt] is 'off' then turn off all enabledby[opt]
   #
   # Error out if we have to turn 'on' an 'off' option or vice versa.
   npending = 0
   for (opt in option) if (opt != "") {
      if (option[opt] == "on" || option[opt] == "off") {
         pending[++npending] = opt
      }
   }

   err = 0 # set on error
   while (npending > 0) {
      opt = pending[npending--]
      if (option[opt] == "on") {
         nreqs = split(requires[opt], r)
         for (j=1; j<=nreqs; ++j) {
            if (option[r[j]] == "off") {
               print "option", opt, "turned on, but requirement", r[j], "is turned off"
               err = 1
            } else if (option[r[j]] != "on") {
               option[r[j]] = "on"
               pending[++npending] = r[j]
            }
         }
      } else {
         if (option[opt] != "off") {
            print "internal error (2)"
            exit 1
         }
         nreqs = split(enabledby[opt], r)
         for (j=1; j<=nreqs; ++j) {
            if (option[r[j]] == "on") {
               print "option", opt, "turned off, but enabled by", r[j], "which is turned on"
               err = 1
            } else if (option[r[j]] != "off") {
               option[r[j]] = "off"
               pending[++npending] = r[j]
            }
         }
      }
   }
   if (err) exit 1

693 694 695
   # Sort options too
   print "PNG_DFN_START_SORT 2" >out

696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
   # option[i] is now the complete list of all the tokens we may
   # need to output, go through it as above, depth first.
   finished = 0
   while (!finished) {
      finished = 1
      movement = 0 # done nothing
      for (i in option) if (!done[i]) {
         nreqs = split(tree[i], r)
         if (nreqs > 0) {
            for (j=1; j<=nreqs; ++j) if (!done[r[j]]) {
               break
            }
            if (j<=nreqs) {
               finished = 0
               continue  # next option
            }
         }

         # All the requirements have been processed, output
         # this option.  An option is _SUPPORTED if:
         #
         # all 'requires' are _SUPPORTED AND
         # at least one of the 'if' options are _SUPPORTED AND
         # EITHER:
         #   The name is _SUPPORTED (on the command line)
         # OR:
         #   an 'enabledby' is _SUPPORTED
         # OR:
         #   NO_name is not defined AND
         #   the option is not disabled; an option is disabled if:
         #    option == off
         #    option == disabled && everything != on
         #    option == "" && everything == off
         if (deb) print "option", i
         print "" >out
         print "/* option:", i, option[i] >out
         print " *   requires:  " requires[i] >out
         print " *   if:      " iffs[i] >out
         print " *   enabled-by:" enabledby[i], "*/" >out
         print "#undef PNG_on" >out
         print "#define PNG_on 1" >out

         # requires
         nreqs = split(requires[i], r)
         for (j=1; j<=nreqs; ++j) {
            print "#ifndef PNG_" r[j] "_SUPPORTED" >out
            print "#   undef PNG_on /*!" r[j] "*/" >out
            # this error appears in the final output if something
            # was switched 'on' but the processing above to force
            # the requires did not work
            if (option[i] == "on") {
               print error, i, "requires", r[j] end >out
            }
            print "#endif" >out
         }

         # if
         nreqs = split(iffs[i], r)
         print "#undef PNG_no_if" >out
         if (nreqs > 0) {
            print "/* if" iffs[i], "*/" >out
            print "#define PNG_no_if 1" >out
            for (j=1; j<=nreqs; ++j) {
               print "#ifdef PNG_" r[j] "_SUPPORTED" >out
               print "#   undef PNG_no_if /*" r[j] "*/" >out
               print "#endif" >out
            }
            print "#ifdef PNG_no_if /*missing if*/" >out
            print "#   undef PNG_on" >out
            # There is no checking above for this, because we
            # don't know which 'if' to choose, so whine about
            # it here:
            if (option[i] == "on") {
               print error, i, "needs one of:", iffs[i] end >out
            }
            print "#endif" >out
         }

         print "#ifdef PNG_on /*requires, if*/" >out
         # enables
         print "#   undef PNG_not_enabled" >out
         print "#   define PNG_not_enabled 1" >out
         print "   /* enabled by" enabledby[i], "*/" >out
         nreqs = split(enabledby[i], r)
         for (j=1; j<=nreqs; ++j) {
            print "#ifdef PNG_" r[j] "_SUPPORTED" >out
            print "#   undef PNG_not_enabled /*" r[j] "*/" >out
            # Oops, probably not intended (should be factored
            # out by the checks above).
            if (option[i] == "off") {
               print error, i, "enabled by:", r[j] end >out
            }
            print "#endif" >out
         }

         print "#   ifndef PNG_" i "_SUPPORTED /*!command line*/" >out
         print "#    ifdef PNG_not_enabled /*!enabled*/" >out
         if (option[i] == "off" || option[i] == "disabled" && everything != "on" || option[i] == "enabled" && everything == "off") {
            print "#      undef PNG_on /*default off*/" >out
         } else {
            print "#      ifdef PNG_NO_" i >out
            print "#       undef PNG_on /*turned off*/" >out
            print "#      endif" >out
            print "#      ifdef PNG_NO_" i "_SUPPORTED" >out
            print "#       undef PNG_on /*turned off*/" >out
            print "#      endif" >out
         }
         print "#    endif /*!enabled*/" >out
         print "#    ifdef PNG_on" >out
         # The _SUPPORTED macro must be defined so that dependent
         # options output later work.
         print "#      define PNG_" i "_SUPPORTED" >out
         print "#    endif" >out
         print "#   endif /*!command line*/" >out
         # If PNG_on is still set the option should be defined in
         # pnglibconf.h
         print "#   ifdef PNG_on" >out
         if (i ~ /^fail_/) {
            print error, i, "is on: enabled by:" iffs[i] enabledby[i] ", requires" requires[i] end >out
         } else if (i !~ /^ok_/) {
            print def i sup >out
         }
         print "#   endif /* definition */" >out
         print "#endif /*requires, if*/" >out
         if (logunsupported || i ~ /^ok_/) {
            print "#ifndef  PNG_on" >out
            if (logunsupported) {
               print und i une >out
            }
            if (i ~ /^ok_/) {
               print error, i, "not enabled: requires:" requires[i] ", enabled by:" iffs[i] enabledby[i] end >out
            }
            print "#endif" >out
         }

         done[i] = 1
         ++movement
      }

      if (!finished && !movement) {
         print "option: loop or missing option in dependency tree, cannot process:"
         for (i in option) if (!done[i]) {
            print "  option", i, "depends on" tree[i], "needs:"
            nreqs = split(tree[i], r)
            if (nreqs > 0) for (j=1; j<=nreqs; ++j) if (!done[r[j]]) {
               print "   " r[j]
            }
         }
         exit 1
      }
   }
847
   print "PNG_DFN_END_SORT" >out
848 849 850 851
   print comment, "end of options", cend >out

   # Regular end - everything looks ok
   if (protect != "") {
852
      print start "#endif", "/*", protect, "*/" end >out
853
   }
854
}