summaryrefslogtreecommitdiff
path: root/scripts/dfn.awk
blob: afa498cb5ab1e983511859325d0ad0b00bd5ef9c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
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
193
194
195
196
197
198
199
200
201
202
203
#!/bin/awk -f
# scripts/dfn.awk - process a .dfn file
#
# last changed in libpng version 1.5.19 - $RDATE%
#
# Copyright (c) 2013-2014 Glenn Randers-Pehrson
#
# 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', which should be set on the command line.
# Error messages are printed to stdout and if any are printed
# the script will exit with error code 1.

BEGIN{
   out="/dev/null"       # as a flag
   out_count=0           # count of output lines
   err=0                 # set if an error occured
   sort=0                # sort the output
   array[""]=""
}

# The output file must be specified before any input:
NR==1 && out == "/dev/null" {
   print "out=output.file must be given on the command line"
   # but continue without setting the error code; this allows the
   # script to be checked easily
}

# Output can be sorted; two lines are recognized
$1 == "PNG_DFN_START_SORT"{
   sort=0+$2
   next
}

$1 ~ /^PNG_DFN_END_SORT/{
   # Do a very simple, slow, sort; notice that blank lines won't be
   # output by this
   for (entry in array) {
      while (array[entry] != "") {
         key = entry
         value = array[key]
         array[key] = ""

         for (alt in array) {
            if (array[alt] != "" && alt < key) {
               array[key] = value
               value = array[alt]
               key = alt
               array[alt] = ""
            }
         }

         print value >out
      }
   }
   sort=0
   next
}

/^[^"]*PNG_DFN *".*"[^"]*$/{
   # A definition line, apparently correctly formatted; extract the
   # definition then replace any doubled "" that remain with a single
   # double quote.  Notice that the original doubled double quotes
   # may have been split by tokenization
   #
   # Sometimes GCC splits the PNG_DFN lines; we know this has happened
   # if the quotes aren't closed and must read another line.  In this
   # case it is essential to reject lines that start with '#' because those
   # are introduced #line directives.
   orig=$0
   line=$0
   lineno=FNR
   if (lineno == "") lineno=NR

   if (sub(/^[^"]*PNG_DFN *"/,"",line) != 1) {
	print "line", lineno ": processing failed:"
	print orig
	err=1
       next
   } else {
	++out_count
   }

   # Now examine quotes within the value:
   #
   #   @" - delete this and any following spaces
   #   "@ - delete this and any preceding spaces
   #   @' - replace this by a double quote
   #
   # This allows macro substitution by the C compiler thus:
   #
   #   #define first_name John
   #   #define last_name Smith
   #
   #	PNG_DFN"#define name @'@" first_name "@ @" last_name "@@'"
   #
   # Might get C preprocessed to:
   #
   #   PNG_DFN "#define foo @'@" John "@ @" Smith "@@'"
   #
   # Which this script reduces to:
   #
   #	#define name "John Smith"
   #
   while (1) {
      # While there is an @" remove it and the next "@
      if (line ~ /@"/) {
         if (line ~ /@".*"@/) {
            # Do this special case first to avoid swallowing extra spaces
            # before or after the @ stuff:
            if (!sub(/@" *"@/, "", line)) {
               # Ok, do it in pieces - there has to be a non-space between the
               # two.  NOTE: really weird things happen if a leading @" is
               # lost - the code will error out below (I believe).
               if (!sub(/@" */, "", line) || !sub(/ *"@/, "", line)) {
                  print "line", lineno, ": internal error:", orig
                  exit 1
               }
            }
         }

         # There is no matching "@.  Assume a split line
         else while (1) {
            if (getline nextline) {
               # If the line starts with '#' it is a preprocesor line directive
               # from cc -E; skip it:
               if (nextline !~ /^#/) {
                  line = line " " nextline
                  break
               }
            } else {
               # This is end-of-input - probably a missing "@ on the first line:
               print "line", lineno ": unbalanced @\" ... \"@ pair"
               err=1
               next
            }
         }

         # Keep going until all the @" have gone
         continue
      }

      # Attempt to remove a trailing " (not preceded by '@') - if this can
      # be done, stop now; if not assume a split line again
      if (sub(/"[^"]*$/, "", line))
         break

      # Read another line
      while (1) {
         if (getline nextline) {
            if (nextline !~ /^#/) {
               line = line " " nextline
               # Go back to stripping @" "@ pairs
               break
            }
         } else {
            print "line", lineno ": unterminated PNG_DFN string"
            err=1
            next
         }
      }
   }

   # Put any needed double quotes in (at the end, because these would otherwise
   # interfere with the processing above.)
   gsub(/@'/,"\"", line)

   # Remove any trailing spaces (not really required, but for
   # editorial consistency
   sub(/ *$/, "", line)

   # Remove trailing CR
   sub(/
$/, "", line)

   if (sort) {
      if (split(line, parts) < sort) {
         print "line", lineno ": missing sort field:", line
         err=1
      } else
         array[parts[sort]] = line
   }

   else
      print line >out
   next
}

/PNG_DFN/{
   print "line", NR, "incorrectly formatted PNG_DFN line:"
   print $0
   err = 1
}

END{
   if (out_count > 0 || err > 0)
	exit err

   print "no definition lines found"
   exit 1
}