996
|
1 #!/usr/bin/env bash
|
|
2 # -*- coding: utf-8 -*-
|
|
3 ###########################################################################
|
|
4 # #
|
|
5 # cfunge - A standard-conforming Befunge93/98/109 interpreter in C. #
|
|
6 # Copyright (C) 2008-2009 Arvid Norlander #
|
|
7 # #
|
|
8 # This program is free software: you can redistribute it and/or modify #
|
|
9 # it under the terms of the GNU General Public License as published by #
|
|
10 # the Free Software Foundation, either version 3 of the License, or #
|
|
11 # (at the proxy's option) any later version. Arvid Norlander is a #
|
|
12 # proxy who can decide which future versions of the GNU General Public #
|
|
13 # License can be used. #
|
|
14 # #
|
|
15 # This program is distributed in the hope that it will be useful, #
|
|
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
|
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
|
18 # GNU General Public License for more details. #
|
|
19 # #
|
|
20 # You should have received a copy of the GNU General Public License #
|
|
21 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
|
22 # #
|
|
23 ###########################################################################
|
|
24
|
|
25 # This script contains utility functions for the fingerprint scripts. It is
|
|
26 # not meant for stand alone use.
|
|
27
|
|
28 # Clean up locale settings to prevent strange bugs:
|
|
29 unset LANG LC_ALL LC_COLLATE LC_CTYPE LC_NUMERIC
|
|
30 export LANG=C
|
|
31 export LC_ALL=C
|
|
32
|
|
33 # Print error message to stderr and exit. Each parameter is printed on it's own
|
|
34 # line prefixed by "ERROR: ".
|
|
35 die() {
|
|
36 local emsg
|
|
37 for emsg in "$@"; do
|
|
38 echo "ERROR: $emsg" >&2
|
|
39 echo -ne '\a' >&2
|
|
40 done
|
|
41 exit 1
|
|
42 }
|
|
43
|
|
44 warn() {
|
|
45 local emsg
|
|
46 for emsg in "$@"; do
|
|
47 echo "WARNING: $emsg" >&2
|
|
48 echo -ne '\a' >&2
|
|
49 done
|
|
50 echo -ne '\a' >&2
|
|
51 sleep 5
|
|
52 }
|
|
53
|
|
54 progress() {
|
|
55 echo " * ${1}..."
|
|
56 }
|
|
57 progresslvl2() {
|
|
58 echo " * ${1}..."
|
|
59 }
|
|
60
|
|
61 status() {
|
|
62 echo " ${1}"
|
|
63 }
|
|
64 statuslvl2() {
|
|
65 echo " ${1}"
|
|
66 }
|
|
67
|
|
68 # Char to decimal (ASCII value)
|
|
69 # $1 Name of output variable
|
|
70 # $2 Char to convert.
|
|
71 ord() {
|
|
72 printf -v "$1" '%d' "'$2"
|
|
73 }
|
|
74
|
|
75 # $1 is fingerprint name
|
|
76 # Returns if ok, otherwise it dies.
|
|
77 checkfprint() {
|
|
78 local FPRINT="$1"
|
|
79 if [[ $FPRINT =~ ^[A-Z0-9]{4}$ ]]; then
|
|
80 status "Fingerprint name $FPRINT ok style."
|
|
81 elif [[ $FPRINT =~ ^[^\ /\\]{4}$ ]]; then
|
|
82 status "Fingerprint name $FPRINT probably ok (but not common style)."
|
|
83 status "Make sure each char is in the ASCII range 0-254."
|
|
84 status "Note that alphanumeric (upper case only) fingerprint names are strongly preferred."
|
|
85 else
|
|
86 die "Not valid format for fingerprint name."
|
|
87 fi
|
|
88 }
|
|
89
|
|
90 # Parse a spec file. Will make use of FD 4.
|
|
91 # $1 Fingerprint name.
|
|
92 # There must be a file named ${1}.spec in the current directory
|
|
93 # The caller should make sure it exists. The caller should also have
|
|
94 # called checkfprint to validate it.
|
|
95 # This will set/change these globals:
|
|
96 # Strings:
|
|
97 # fp_URL Fingerprint URL
|
|
98 # fp_F109_URI URI for Funge-109
|
|
99 # fp_CONDITION #if condition for compilation.
|
|
100 # fp_SAFE Safe for sandbox mode (true/false)
|
|
101 # fp_OPCODES Defined opcodes.
|
|
102 # fp_DESCRIPTION Description
|
|
103 # Arrays:
|
|
104 # fp_ALIASES An array with aliases for the fingerprint.
|
|
105 # (text format, not hex)
|
|
106 # fp_OPCODE_NAMES Opcode names (index: opcode's ASCII value).
|
|
107 # fp_OPCODE_DESC Opcode descriptions (index: opcode's ASCII value).
|
|
108 #
|
|
109 # On return IFS is unset.
|
|
110 # This function calls die in case of errors.
|
|
111 parse_spec() {
|
|
112 local FPRINT="$1"
|
|
113 # Variables
|
|
114 fp_URL=""
|
|
115 fp_F109_URI="NULL"
|
|
116 fp_CONDITION=""
|
|
117 fp_SAFE=""
|
|
118 fp_OPCODES=""
|
|
119 fp_DESCRIPTION=""
|
|
120 fp_ALIASES=()
|
|
121 fp_OPCODE_NAMES=()
|
|
122 fp_OPCODE_DESC=()
|
|
123
|
|
124 if [[ ! -f "${FPRINT}.spec" ]]; then
|
|
125 die "${FPRINT}.spec not found (or not a normal file)."
|
|
126 fi
|
|
127
|
|
128 progresslvl2 "Opening spec file"
|
|
129 IFS=$'\n'
|
|
130 local line type data instr name desc number
|
|
131 # First line is %fingerprint-spec 1.[234]
|
|
132 # (1.2 is still supported).
|
|
133 exec 4<"${FPRINT}.spec" || die "Couldn't open spec file for reading on FD 4."
|
|
134 statuslvl2 "Success."
|
|
135 progresslvl2 "Parsing spec file"
|
|
136 read -ru 4 line
|
|
137 if ! [[ "$line" =~ ^%fingerprint-spec\ 1\.[234]$ ]]; then
|
|
138 die "Either the spec file is not a fingerprint spec, or it is not a supported version (1.2, 1.3 and 1.4 are currently supported)."
|
|
139 fi
|
|
140
|
|
141 # 0: pre-"begin instrs"
|
|
142 # 1: "begin-instrs"
|
|
143 local parsestate=0
|
|
144
|
|
145 while read -ru 4 line; do
|
|
146 if [[ "$line" =~ ^# ]]; then
|
|
147 continue
|
|
148 fi
|
|
149 if [[ $parsestate == 0 ]]; then
|
|
150 IFS=':' read -rd $'\n' type data <<< "$line" || true
|
|
151 case $type in
|
|
152 "%fprint")
|
|
153 if [[ "$FPRINT" != "$data" ]]; then
|
|
154 die "%fprint and spec file name doesn't match."
|
|
155 fi
|
|
156 ;;
|
|
157 "%url")
|
|
158 fp_URL="$data"
|
|
159 ;;
|
|
160 "%f108-uri")
|
|
161 warn "%f108-uri is deprecated. Replace with %f109-uri and update spec format to 1.4."
|
|
162 fp_F109_URI="\"$data\""
|
|
163 ;;
|
|
164 "%f109-uri")
|
|
165 fp_F109_URI="\"$data\""
|
|
166 ;;
|
|
167 "%condition")
|
|
168 fp_CONDITION="$data"
|
|
169 ;;
|
|
170 "%alias")
|
|
171 fp_ALIASES+=( "$data" )
|
|
172 ;;
|
|
173 "%desc")
|
|
174 fp_DESCRIPTION="$data"
|
|
175 ;;
|
|
176 "%safe")
|
|
177 fp_SAFE="$data"
|
|
178 ;;
|
|
179 "%begin-instrs")
|
|
180 parsestate=1
|
|
181 ;;
|
|
182 "#"*)
|
|
183 # A comment, ignore
|
|
184 ;;
|
|
185 *)
|
|
186 die "Unknown entry $type found in ${FPRINT}."
|
|
187 ;;
|
|
188 esac
|
|
189 else
|
|
190 if [[ "$line" == "%end" ]]; then
|
|
191 break
|
|
192 fi
|
|
193 # Parse instruction lines.
|
|
194 IFS=$' \t' read -rd $'\n' instr name desc <<< "$line"
|
|
195
|
|
196 fp_OPCODES+="$instr"
|
|
197 ord number "${instr:0:1}"
|
|
198 fp_OPCODE_NAMES[$number]="$name"
|
|
199 fp_OPCODE_DESC[$number]="$desc"
|
|
200 fi
|
|
201 done
|
|
202
|
|
203 unset IFS
|
|
204
|
|
205 statuslvl2 "Done parsing."
|
|
206
|
|
207 exec 4<&-
|
|
208
|
|
209 progresslvl2 "Validating the parsed data"
|
|
210
|
|
211 if [[ "$fp_URL" ]]; then
|
|
212 statuslvl2 "%url: Good, not empty"
|
|
213 else
|
|
214 die "%url is not given or is empty."
|
|
215 fi
|
|
216
|
|
217 if [[ "$fp_DESCRIPTION" ]]; then
|
|
218 statuslvl2 "%desc: Good, not empty"
|
|
219 else
|
|
220 die "%desc is not given or is empty."
|
|
221 fi
|
|
222
|
|
223 if [[ ( "$fp_SAFE" == "true" ) || ( "$fp_SAFE" == "false" ) ]]; then
|
|
224 statuslvl2 "%safe: OK"
|
|
225 else
|
|
226 die "%safe must be either true or false."
|
|
227 fi
|
|
228
|
|
229 if [[ "$fp_OPCODES" =~ ^[A-Z]+$ ]]; then
|
|
230 # Check that they are sorted.
|
|
231 local previousnr=0
|
|
232 for (( i = 0; i < ${#fp_OPCODES}; i++ )); do
|
|
233 ord number "${fp_OPCODES:$i:1}"
|
|
234 if [[ $previousnr -ge $number ]]; then
|
|
235 die "Instructions not sorted or there are duplicates"
|
|
236 else
|
|
237 previousnr=$number
|
|
238 fi
|
|
239 done
|
|
240 statuslvl2 "Instructions: OK"
|
|
241 else
|
|
242 die "The opcodes are not valid. The must be in the range A-Z"
|
|
243 fi
|
|
244 return 0
|
|
245 }
|