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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
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
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
|
#!/usr/bin/env zsh
# Check possible problems in the MPFR source.
set -e
setopt EXTENDED_GLOB
# mpfrlint can be run from the tools directory
oldpwd=$PWD
[[ -d src ]] || [[ $oldpwd:t != tools ]] || cd ..
err=0
if [[ -t 1 ]] then
term=1
pfx=""
sfx=""
fi
err-if-output()
{
local dir msg checkoutput=1 checkstatus=1 output st h line
while [[ $1 == -* ]]
do
case $1 in
--dir=*)
dir=${1#--dir=} ;;
--msg=*)
msg=${1#--msg=} ;;
-o)
# The command generates output even in case of success;
# do not regard this as an error.
checkoutput="" ;;
-t)
# The command normally returns with a non-zero exit status;
# do not regard this as an error.
checkstatus="" ;;
*)
echo "unrecognized option '$1' for $0" >&2
exit 1 ;;
esac
shift
done
[[ -n $dir ]] && pushd $dir
set +e
# Note: the double quotes for $@[2,-1] are important to pass empty args.
output=(${(f)"$("$@[2,-1]" 2>&1)"})
st=$?
if [[ ( -n "$checkstatus" && $st -ne 0 ) ||
( -n "$checkoutput" && -n "$output" ) ]] then
if [[ -n "$1" && -t 1 ]] then
[[ -n "$pfx" ]] || pfx=$(tput 2>/dev/null bold)
[[ -n "$sfx" ]] || sfx=$(tput 2>/dev/null sgr0)
fi
h=${1:+$pfx$1:$sfx }
for line in ${output:-"exit status = $st"}
do
printf "%s%s\n" $h $line
done
[[ -n "$msg" ]] && printf "%s %s\n" "$pfx-->$sfx" "$msg"
err=1
fi
set -e
[[ -n $dir ]] && popd
# Do not fail.
true
}
if [[ $1 == test ]] then
export LC_ALL=C
err-if-output Err-e exit 17
err-if-output Err-f false
err-if-output Err-t true
err-if-output "" echo "Empty first argument"
err-if-output "1 line" echo foo
err-if-output "2 lines" printf "foo\nbar\n"
err-if-output -o Err-f false
err-if-output -o Err-t true
err-if-output -o "cp test" cp
err-if-output -o "1 line" echo foo
err-if-output -t Err-f false
err-if-output -t Err-t true
err-if-output -t error echo output
echo "Test done."
exit
: <<EOF
The output should be:
Err-e: exit status = 17
Err-f: exit status = 1
Empty first argument
1 line: foo
2 lines: foo
2 lines: bar
Err-f: exit status = 1
cp test: cp: missing file operand
cp test: Try 'cp --help' for more information.
error: output
Test done.
EOF
fi
############################################################################
# Note: In the source, ignore everything related to mini-gmp.
srctests=({src,tests}/**/*.[ch]~*mini-gmp.*)
#srctests=(`find src tests -name '*.[ch]' ! -name '*mini-gmp.*'`)
# Detect the definition of reserved macro names.
#
# See ISO C 7.1.3 (Reserved identifiers) and 7.31 (Future library directions).
# The definition of a reserved identifier is undefined behavior
# (this includes the future library directions).
#
# Note: The following check may have false positives in some cases,
# but this may also correspond to bad coding, in particular because
# code moves. We assume that if a header is used somewhere, it may
# be used everywhere in the MPFR code. This particularly concerns
# macros defined via mpfr-impl.h or mpfr-test.h, which are included
# in almost every tests.
#
# The case of EXP in src/mpfr-gmp.h is not really fixable due to the
# possible use of gmp-impl.h, but we should make sure that <errno.h>
# is never included in this case (see comment in the code).
names='E[0-9A-Z]|FE_[A-Z]|LC_[A-Z]|(PRI|SCN)[Xa-z]|SIG_?[A-Z]|TIME_[A-Z]'
msg='ISO C 7.1.3 (Reserved identifiers) and 7.31 (Future library directions).'
grep -E "# *define ($names)" $srctests | \
grep -v 'src/mpfr-gmp.h:#define EXP(' | \
err-if-output --msg="$msg" "reserved identifiers (macro names)" cat
# Detect the possible use of forbidden macros in mpfr.h, such as those
# starting with "HAVE_" or "WANT_". Public macros defined by MPFR must
# start with "MPFR_".
err-if-output -t "" perl -ne '
/^#/ && ! /^# *error / or next; while (/\b([_A-Z]+)\b/g) {
my $m = $1;
$m =~ /^(_*MPFR_|_*GMP_|__(GNUC|ICC|STDC)(_|$)|_MSC_|U?INTMAX_C$)/
and next;
print "Forbidden macro in mpfr.h line $.: $m\n" }' src/mpfr.h
err-if-output -t "math.h" grep '^# *include *<math\.h>' src/*.c
flaglist="underflow|overflow|divby0|nanflag|inexflag|erangeflag"
grep -E "mpfr_($flaglist)_p" src/*.{c,h} | \
grep -v -i 'mpfr_clear_' | \
grep -v '^src/exceptions.c:' | \
grep -v '^src/mpfr-impl.h:#define mpfr_.*_p()' | \
grep -v '^src/mpfr.h:__MPFR_DECLSPEC ' | \
err-if-output "flags" cat
grep -E "mpfr_(clear|set)_($flaglist) *\(" src/*.{c,h} | \
grep -v '^src/exceptions.c:' | \
grep -v '^src/mpfr.h:' | \
err-if-output "flags" cat
grconf()
{
grep -v '^dnl ' acinclude.m4 configure.ac | \
err-if-output -t "grconf '$1'" grep -E "$1"
}
grconf '^(.*if +|[[:space:]]*)(test|\[).* == '
grconf '="`'
grconf '[^a-z][ef]?grep[^a-z]'
grconf '[^a-z]sed[^a-z]'
err-if-output --msg="Use GMP_NUMB_BITS instead." \
-t "GMP_LIMB_BITS" grep GMP_LIMB_BITS $srctests
grep GMP_RND $srctests | err-if-output -t "GMP_RND*" grep -v '#define GMP_RND'
# Note: GMP internals should not be used. There are some exceptions,
# normally used only if WANT_GMP_INTERNALS is defined (this should be
# checked manually).
grep '__gmp[nz]_' $srctests | \
grep -v __gmpn_rootrem | \
grep -v __gmpn_sbpi1_divappr_q | \
grep -v __gmpn_invert_limb | \
grep -v __gmpn_rsblsh_n | \
err-if-output "GMP internals" cat
err-if-output --msg="Use mpfr_limb_ptr and mpfr_limb_srcptr instead." \
-t "mp_ptr and mp_srcptr" grep -E 'mp_(src)?ptr' $srctests
# Do not use __mpfr_struct structure members in .c files.
err-if-output -t "__mpfr_struct members" \
grep -E '[^0-9a-z_]_mpfr_(prec|sign|exp|d)' {src,tests}/*.c
# Detect some uses of "x != x" and "x == x". If this occurs in source code,
# x is probably a native FP number (otherwise the test is useless), but in
# such a case, the DOUBLE_ISNAN macro should be used.
err-if-output -t "x != x and x == x tests" \
grep '( *\([^[:space:]]*\) *[!=]= *\1 *)' $srctests
for i in exp prec rnd
do
grep "[^a-z]mp_${i}_t" $srctests | \
grep -v "\(# *define\|# *ifndef\|typedef\) *mp_${i}_t" | \
grep -v "\[mp_${i}_t\]" | \
err-if-output "mp_*_t" cat
done
for file in $srctests
do
err-if-output "MPFR_LOG_MSG format" perl -e '
my $f = do { local $/; <> };
while ($f =~ /MPFR_LOG_MSG\s*\(\s*\(.*?\)\s*\)/gs) {
my $s = $&; print "$ARGV: $s\n" if
index($s,"\\n\"") < 0 || $s !~ /"\s*,/
}' $file
done
# Macros of the form:
# #define FOO { ... }
# may be unsafe and could yield obscure failures where writing "FOO;" as
# this is here a block followed by a null statement. The following form
# is preferred in most of the cases:
# #define FOO do { ... } while (0)
# so that "FOO;" is a single statement.
# To avoid false positives, a comment can be inserted, e.g.:
# #define FOO /* initializer */ { 0, 17 }
for file in $srctests
do
err-if-output "Missing 'do ... while (0)'" perl -e '
while (<>) {
my $s = $_;
while ($s =~ s/\\\n//) { $s .= <> }
$s =~ /^#\s*define\s+\w+(\([^)]*\))?\s*{/
and $s =~ tr/ \t/ /s, print "$ARGV: $s";
}' $file
done
# Do not use snprintf as it is not available in ISO C90.
# Even on platforms where it is available, the prototype
# may not be included (e.g. with gcc -ansi), so that the
# code may be compiled incorrectly.
grep '[^a-z_]snprintf *([^)]' $srctests | \
err-if-output -t "snprintf" grep -v '/\*.*[^a-z_]snprintf *([^)]'
# Constant checking should use either MPFR_STAT_STATIC_ASSERT
# or MPFR_ASSERTN(0) for not yet implemented corner cases.
# This test is a heuristic.
# Note: as long as the support of _MPFR_EXP_FORMAT = 4 is not complete,
# run-time assertions involving MPFR_EMAX_MAX, LONG_MAX, etc. should be
# used instead of static assertions to allow testing and correction of
# the code (then the removal of the assertions).
grep 'MPFR_ASSERT[DN][^a-z]*;' src/*.c | grep -v 'MPFR_ASSERTN *(0)' | \
grep -v '\(MPFR_EMAX_MAX\|MPFR_EXP_MAX\).*LONG_MAX' | \
grep -v '\(MPFR_EMIN_MIN\|MPFR_EXP_MIN\).*LONG_MIN' | \
grep -v MPFR_BLOCK_EXCEP | \
err-if-output --msg="Use MPFR_STAT_STATIC_ASSERT in general." \
-t "Constant checking" cat
# ASSERT and ASSERT_ALWAYS must not be used for assertion checking.
# Use MPFR_STAT_STATIC_ASSERT for static assertions, otherwise either
# MPFR_ASSERTD (debug mode / hint for the compiler) or MPFR_ASSERTN.
err-if-output -t "ASSERT / ASSERT_ALWAYS" \
grep -E '[^_]ASSERT(_ALWAYS)? *(\(|$)' {src,tests}/*.c
# Use MPFR_TMP_LIMBS_ALLOC.
err-if-output -t "Use MPFR_TMP_LIMBS_ALLOC" \
grep 'MPFR_TMP_ALLOC.*\(BYTES_PER_MP_LIMB\|sizeof.*mp_limb_t\)' src/*.c
# Use simple mp_limb_t constants: MPFR_LIMB_ZERO, MPFR_LIMB_ONE, etc.
grep '(mp_limb_t) *-\?[01][^0-9]' $srctests | grep -v '#define MPFR_LIMB_' | \
grep -v 'MPFR_ASSERTN *(MPFR_MANT.*== *(mp_limb_t)' | \
err-if-output -t "Use simple mp_limb_t constants" cat
# "~0;" has already been seen in the code. Check also a bit more.
# Such code is either an error or poorly written, and in this case, quite
# fragile. In practice, this may work because most (or all) platforms use
# two's complement, and 0 being of type int (signed), this will give -1;
# then when converted to a larger unsigned type, this will give the maximum
# value as expected. However the reader may not know this explanation, and
# a small change of the code can easily make it incorrect. The correct way
# to obtain this result is to cast the constant to the expected type, then
# do the bitwise complement; alternatively, cast -1 to the expected type.
# For limbs, the macro MPFR_LIMB_MAX should be used.
err-if-output \
--msg="Possibly incorrect type for ~ (use MPFR_LIMB_MAX for limbs)" \
-t "Suspicious code" \
grep '~[0-9] *[-+*&|;]' $srctests
# Code possibly wrong with some C/GMP implementations. In short, if the
# right operand of a shift depends on GMP_NUMB_BITS, then the left operand
# should be of type mp_limb_t. There might be false positives.
err-if-output \
--msg="Use a constant of type mp_limb_t instead of unsigned long?" \
-t "Suspicious code" \
grep -E '[^A_Za-z_][0-9]+[UL]* *<<.*GMP_NUMB_BITS' src/*.c
for file in $srctests */Makefile.am acinclude.m4 configure.ac
do
# Note: this is one less that the POSIX minimum limit in case
# implementations are buggy like POSIX examples. :)
err-if-output "" perl -ne "/.{2047,}/ and print \
\"Line \$. of file $file has more than 2046 bytes.\n\"" "$file"
done
# Code style: a sign decimal constant for mpfr_set_inf and mpfr_set_zero
# should be either 1 or -1 (except for the tests in tset.c).
grep -E 'mpfr_set_(inf|zero) *\([^,]*, *[-+]?([02-9]|1[^)])' $srctests | \
err-if-output -t "mpfr_set_(inf|zero) second argument" \
grep -v tests/tset\\.c:
# In general, one needs to include mpfr-impl.h (note that some platforms
# such as MS Windows use a config.h, which is included by mpfr-impl.h).
for file in src/*.c
do
[[ "$file" == src/(jyn_asympt|mini-gmp|round_raw_generic).c ]] || \
grep -q '^# *include *"\(mpfr-impl\|fits.*\|gen_inverse\|random_deviate\)\.h"' $file || \
{ echo "Missing '#include \"mpfr-impl.h\"' in $file?" && err=1 }
done
# Re-seeding mpfr_rands in the tests is bad practice, as it would affect
# later tests, defeating the purpose of GMP_CHECK_RANDOMIZE.
err-if-output \
--msg='Do not re-seed mpfr_rands; use another gmp_randstate_t variable.' \
-t "mpfr_rands" grep --exclude=tests.c \
'gmp_randseed.*mpfr_rands' tests/*.{c,h}
# "mpfr-impl.h" must not be included directly by the test programs.
# Otherwise __MPFR_WITHIN_MPFR will be defined, yielding failures
# under MS Windows with DLL.
err-if-output \
--msg='Include "mpfr-test.h" instead of "mpfr-impl.h" to prevent __MPFR_WITHIN_MPFR from being defined.' \
-t "mpfr-impl.h inclusion" grep --exclude=mpfr-test.h \
'^ *# *include *"mpfr-impl.h"' tests/*.{c,h}
# Check that the usual test programs call tests_start_mpfr and tests_end_mpfr.
tprg=($(sed -n '/^check_PROGRAMS/,/[^\\]$/ {
s/.*=//
s/\\//
p
}' tests/Makefile.am))
[[ -n $tprg ]]
for t in $tprg
do
[[ $t != mpf*_compat ]] || continue
tc=tests/$t.c
if grep -q "main *(" $tc; then
for fn in tests_start_mpfr tests_end_mpfr
do
err-if-output "missing call to $fn in $tc" grep -q "$fn *();" $tc
done
fi
done
# mpfr_printf-like functions shouldn't be used in the tests,
# as they need <stdarg.h> (HAVE_STDARG defined).
for file in tests/*.c
do
sed '/#if\(def\| *defined *(\)\? *HAVE_STDARG/,/#\(endif\|else\) .*HAVE_STDARG/d
/\/\*.*\*\//d
/\/\*/,/\*\//d' $file | \
err-if-output -t "unprotected mpfr_printf-like function call in $file" \
grep "mpfr_[a-z]*printf"
done
grep __gmp_ tests/*.c | \
err-if-output -t "__gmp_" grep -v '^tests/tabort_defalloc'
grep -E '[^a-z_](m|re)alloc *\(' tests/*.c | \
err-if-output -t "alloc" grep -Ev '^tests/(memory|talloc-cache).c:'
err-if-output --dir=doc "check-typography" ./check-typography
fdlv1="`sed -n '/Version / {
s/.*Version //
s/,.*//
p
q
}' doc/fdl.texi`"
fdlv2="`sed -n '/GNU Free Documentation License/ {
s/.*Version //
s/ or.*//
p
q
}' doc/mpfr.texi`"
[[ $fdlv1 == $fdlv2 ]] || { cat <<EOF && err=1 }
GFDL versions differ:
fdl.texi: $fdlv1
mpfr.texi: $fdlv2
EOF
err-if-output "ck-copyright-notice" tools/ck-copyright-notice
if which gcc > /dev/null 2> /dev/null; then
err-if-output "ck-mparam" tools/ck-mparam
else
echo "Warning! gcc is not installed. Cannot run ck-mparam." >&2
fi
# Note about the TZ value: GMT0 and UTC0 are both specified by POSIX,
# and UTC0 is the preferred value, but old systems only accept GMT0.
# The "0" is important ("GMT" alone does not work on Tru64 Unix).
texisvnd=`LC_ALL=C TZ=GMT0 svn info doc/mpfr.texi 2> /dev/null | sed -n 's/Last Changed Date:.*, [0-9]* \([A-Z][a-z][a-z] [0-9][0-9][0-9][0-9]\)).*/\1/p'`
if [[ $? -eq 0 ]] && [[ -n "$texisvnd" ]] then
texidate=`sed -n 's/@set UPDATED-MONTH \([A-Z][a-z][a-z]\).*\( [0-9][0-9][0-9][0-9]\)/\1\2/p' doc/mpfr.texi`
[[ $texidate == $texisvnd ]] || { cat <<EOF && err=1 }
mpfr.texi's UPDATED-MONTH seems to be incorrect:
mpfr.texi's UPDATED-MONTH: $texidate
Last Changed Date in WC: $texisvnd
EOF
fi
acv1=`sed -n 's/.*autoconf \([0-9.]\+\) (at least).*/\1/p' doc/README.dev`
acv2=`sed -n 's/AC_PREREQ(\([0-9.]\+\).*/\1/p' acinclude.m4`
[[ $acv1 == $acv2 ]] || { cat <<EOF && err=1 }
autoconf minimal versions differ:
README.dev: $acv1
acinclude.m4: $acv2
EOF
gmpv1=`sed -n 's/.*error "GMP \([0-9.]\+\) or .*/\1/p' configure.ac`
gmpv2=`sed -n 's/.*AC_MSG_ERROR.*GMP \([0-9.]\+\) or .*/\1/p' configure.ac`
gmpv3=`sed -n 's/.*MPFR requires GMP version \([0-9.]\+\) or .*/\1/p' INSTALL`
gmpv4=`sed -n 's/.*GNU MP (version \([0-9.]\+\) or .*/\1/p' doc/mpfr.texi`
[[ $gmpv1 == $gmpv2 && $gmpv1 == $gmpv3 && $gmpv1 == $gmpv4 ]] ||
{ cat <<EOF && err=1 }
Minimal GMP version mismatch:
configure.ac 1: $gmpv1
configure.ac 2: $gmpv2
INSTALL: $gmpv3
doc/mpfr.texi: $gmpv4
EOF
# In case of problems, one can also skip files or use "grep -v"...
# In particular, skip the openout.* files, which are created by "make pdf"
# (they are in the doc/mpfr.t2p/mpfr.t2d/pdf/check_recorder directory, and
# unfortunately codespell can't skip directories such as mpfr.t2p).
if which codespell > /dev/null 2> /dev/null; then
err-if-output "codespell" codespell ${term:+--enable-colors} \
-q3 -I codespell.ignore \
-S 'algorithm2e.sty,algorithms.tex,texinfo.tex,openout.*' \
AUTHORS BUGS INSTALL NEWS README TODO doc examples $srctests
else
echo "Warning! codespell is not installed. Cannot check spelling." >&2
fi
err-if-output "ck-clz_tab" tools/ck-clz_tab
err-if-output "ck-inits-clears" tools/ck-inits-clears
err-if-output "ck-version-info" tools/ck-version-info
############################################################################
[[ $err -eq 0 ]] || echo "Terminated with errors." >&2
exit $err
|