Merge: More escaping
authorJean Privat <jean@pryen.org>
Thu, 13 Nov 2014 05:10:39 +0000 (00:10 -0500)
committerJean Privat <jean@pryen.org>
Thu, 13 Nov 2014 05:10:39 +0000 (00:10 -0500)
compiler is more robust.

The only issue seems to be the Makefile since there is no way to have an arbitrary named goal.

Pull-Request: #899
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>

lib/standard/string.nit
src/compiler/abstract_compiler.nit
src/compiler/global_compiler.nit
tests/--base_very bad name@1&$|;:.ext'"<>{}`~!#^*()_=+[]\,?.nit [new file with mode: 0644]
tests/sav/--base_very bad name@1&$|;:.ext'"<>{}`~!#^*()_=+[]\,?.res [new file with mode: 0644]
tests/search_tests.sh
tests/tests.sh

index 28ad7b5..0ffb303 100644 (file)
@@ -515,6 +515,29 @@ abstract class Text
                return b.to_s
        end
 
+       # Escape to include in a Makefile
+       #
+       # Unfortunately, some characters are not escapable in Makefile.
+       # These characters are `;`, `|`, `\`, and the non-printable ones.
+       # They will be rendered as `"?{hex}"`.
+       fun escape_to_mk: String do
+               var b = new FlatBuffer
+               for i in [0..length[ do
+                       var c = chars[i]
+                       if c == '$' then
+                               b.append("$$")
+                       else if c == ':' or c == ' ' or c == '#' then
+                               b.add('\\')
+                               b.add(c)
+                       else if c.ascii < 32 or c == ';' or c == '|' or c == '\\' or c == '=' then
+                               b.append("?{c.ascii.to_base(16, false)}")
+                       else
+                               b.add(c)
+                       end
+               end
+               return b.to_s
+       end
+
        # Return a string where Nit escape sequences are transformed.
        #
        #     var s = "\\n"
index 16c0e4b..abfbf3f 100644 (file)
@@ -315,7 +315,14 @@ class MakefileToolchain
 
                var outname = outfile(mainmodule)
 
-               var outpath = compile_dir.relpath(outname)
+               var real_outpath = compile_dir.relpath(outname)
+               var outpath = real_outpath.escape_to_mk
+               if outpath != real_outpath then
+                       # If the name is crazy and need escaping, we will do an indirection
+                       # 1. generate the binary in the .nit_compile dir under an escaped name
+                       # 2. copy the binary at the right place in the `all` goal.
+                       outpath = mainmodule.c_name
+               end
                var makename = makefile_name(mainmodule)
                var makepath = "{compile_dir}/{makename}"
                var makefile = new OFStream.open(makepath)
@@ -348,7 +355,11 @@ class MakefileToolchain
 
                makefile.write("ifdef NEED_LIBUNWIND\n\tLDLIBS += -lunwind\nendif\n")
 
-               makefile.write("all: {outpath}\n\n")
+               makefile.write("all: {outpath}\n")
+               if outpath != real_outpath then
+                       makefile.write("\tcp -- {outpath.escape_to_sh} {real_outpath.escape_to_sh.replace("$","$$")}")
+               end
+               makefile.write("\n")
 
                var ofiles = new Array[String]
                var dep_rules = new Array[String]
@@ -413,9 +424,12 @@ endif
                if not pkgconfigs.is_empty then
                        pkg = "`pkg-config --libs {pkgconfigs.join(" ")}`"
                end
-               makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
+               makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath.escape_to_sh} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
                # Clean
-               makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n")
+               makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n")
+               if outpath != real_outpath then
+                       makefile.write("\trm -- {outpath.escape_to_sh} 2>/dev/null\n")
+               end
                makefile.close
                self.toolcontext.info("Generated makefile: {makepath}", 2)
 
@@ -524,9 +538,9 @@ abstract class AbstractCompiler
                stream.write("static const C_Nit_Names map[{names.length}] = \{\n")
                for i in names.keys do
                        stream.write("\{\"")
-                       stream.write(i)
+                       stream.write(i.escape_to_c)
                        stream.write("\",\"")
-                       stream.write(names[i])
+                       stream.write(names[i].escape_to_c)
                        stream.write("\"\},\n")
                end
                stream.write("\};\n")
index 113b549..510b1ac 100644 (file)
@@ -116,7 +116,7 @@ class GlobalCompiler
 
        init
        do
-               var file = new_file("{mainmodule.name}.nitgg")
+               var file = new_file("{mainmodule.c_name}.nitgg")
                self.header = new CodeWriter(file)
                self.live_primitive_types = new Array[MClassType]
                for t in runtime_type_analysis.live_types do
diff --git a/tests/--base_very bad name@1&$|;:.ext'"<>{}`~!#^*()_=+[]\,?.nit b/tests/--base_very bad name@1&$|;:.ext'"<>{}`~!#^*()_=+[]\,?.nit
new file mode 100644 (file)
index 0000000..11c4b4c
--- /dev/null
@@ -0,0 +1,17 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import kernel
+
+1.output
diff --git a/tests/sav/--base_very bad name@1&$|;:.ext'"<>{}`~!#^*()_=+[]\,?.res b/tests/sav/--base_very bad name@1&$|;:.ext'"<>{}`~!#^*()_=+[]\,?.res
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
index ee239cc..c4e3593 100755 (executable)
@@ -15,7 +15,7 @@
 
 stop=false
 while [ "$stop" = false ]; do
-       case $1 in
+       case "$1" in
                -v) verbose=true; shift;;
                *) stop=true
        esac
@@ -42,29 +42,31 @@ for f in "$@"; do
                                echo "$f"
                                continue
                        fi
-                       b=`basename "$f" .nit`
+                       b=`basename -- "$f" .nit`
                        ;;
                *.res)
-                       b=`basename "$f" .res`
+                       b=`basename -- "$f" .res`
                        ;;
                *)
-                       b=`basename "$f"`
+                       b=`basename -- "$f"`
                        ;;
        esac
+       # remove bad chars
+       c=`echo "$b" | sed 's/\([\\.*^$]\|\[\|]\)/./g'`
        # Remove alts of args test variations
-       c=`echo "$b" | sed 's/\(_[0-9]*alt[0-9][0-9]*\)/\\\\(\1\\\\)\\\\?/g;s/\(_args[0-9][0-9]*\)/\\\\(\1\\\\)\\\\?/'`
+       c=`echo "$c" | sed 's/\(_[0-9]*alt[0-9][0-9]*\)/\\\\(\1\\\\)\\\\?/g;s/\(_args[0-9][0-9]*\)/\\\\(\1\\\\)\\\\?/'`
        b=`echo "$b" | sed 's/_[0-9]*alt[0-9][0-9]*//g;s/_args[0-9][0-9]*//'`
        # Search the orig nit file in the list
-       cat listfull.out | grep "\b$c.nit" || {
+       cat listfull.out | grep -- "\(^\|/\)$c.nit" || {
                res=1
                echo >&2 "No test $b.nit found for $f"
                test "$verbose" == "true" || continue
                # Search the nit file outside the list...
-               find ../../nit* -name $b.nit >&2
+               find ../../nit* -name "$b.nit" >&2
                # Search the nit file in the git history...
-               git log -1 -- $b.nit >&2
+               git log -1 -- "$b.nit" >&2
                # Search the orig file in the git history...
-               git log -1 -- $f >&2
+               git log -1 -- "$f" >&2
        }
 done
 
index faed626..8720ca3 100755 (executable)
@@ -65,7 +65,7 @@ saferun()
        local o=
        local a=
        while [ $stop = false ]; do
-               case $1 in
+               case "$1" in
                        -o) o="$2"; shift; shift;;
                        -a) a="-a"; shift;;
                        *) stop=true
@@ -129,8 +129,8 @@ function compare_to_result()
        local pattern="$1"
        local sav="$2"
        if [ ! -r "$sav" ]; then return 0; fi
-       test "`cat "$sav"`" = "UNDEFINED" && return 1
-       diff -u "$sav" "$outdir/$pattern.res" > "$outdir/$pattern.diff.sav.log"
+       test "`cat -- "$sav"`" = "UNDEFINED" && return 1
+       diff -u -- "$sav" "$outdir/$pattern.res" > "$outdir/$pattern.diff.sav.log"
        if [ "$?" == 0 ]; then
                return 1
        fi
@@ -146,6 +146,13 @@ function compare_to_result()
        fi
 }
 
+function xmlesc()
+{
+       sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g; s/"/\&quot;/g; s/'"'"'/\&#39;/g'<<EOF
+$*
+EOF
+}
+
 # As argument: the pattern used for the file
 function process_result()
 {
@@ -164,7 +171,7 @@ function process_result()
        OLD=""
        LIST=""
        FIRST=""
-       echo >>$xml "<testcase classname='$pack' name='$description' time='`cat $outdir/$pattern.time.out`' `timestamp`>"
+       echo >>$xml "<testcase classname='`xmlesc "$pack"`' name='`xmlesc "$description"`' time='`cat -- "$outdir/$pattern.time.out"`' `timestamp`>"
        #for sav in "sav/$engine/fixme/$pattern.res" "sav/$engine/$pattern.res" "sav/fixme/$pattern.res" "sav/$pattern.res" "sav/$pattern.sav"; do
        for savdir in $savdirs; do
                sav=$savdir/fixme/$pattern.res
@@ -225,7 +232,7 @@ function process_result()
        if [ -n "$SAV" ]; then
                if [ -n "$OLD" ]; then
                        echo "[*ok*] $outdir/$pattern.res $SAV - but $OLD remains!"
-                       echo >>$xml "<error message='ok $outdir/$pattern.res - but $OLD remains'/>"
+                       echo >>$xml "<error message='`xmlesc "ok $outdir/$pattern.res - but $OLD remains"`'/>"
                        remains="$remains $OLD"
                else
                        echo "[ok] $outdir/$pattern.res $SAV"
@@ -234,7 +241,7 @@ function process_result()
        elif [ -n "$FIXME" ]; then
                if [ -n "$OLD" ]; then
                        echo "[*fixme*] $outdir/$pattern.res $FIXME - but $OLD remains!"
-                       echo >>$xml "<error message='ok $outdir/$pattern.res - but $OLD remains'/>"
+                       echo >>$xml "<error message='`xmlesc "ok $outdir/$pattern.res - but $OLD remains"`'/>"
                        remains="$remains $OLD"
                else
                        echo "[fixme] $outdir/$pattern.res $FIXME"
@@ -247,41 +254,41 @@ function process_result()
                todos="$todos $pattern"
        elif [ -n "$SOSO" ]; then
                echo "[======= soso $outdir/$pattern.res $SOSO =======]"
-               echo >>$xml "<error message='soso $outdir/$pattern.res $SOSO'/>"
+               echo >>$xml "<error message='`xmlesc "soso $outdir/$pattern.res $SOSO"`'/>"
                echo >>$xml "<system-out><![CDATA["
-               cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50
+               cat -v -- "$outdir/$pattern.diff.sav.log" | head >>$xml -n 50
                echo >>$xml "]]></system-out>"
                nok="$nok $pattern"
                echo "$ii" >> "$ERRLIST"
        elif [ -n "$SOSOF" ]; then
                echo "[======= fixme soso $outdir/$pattern.res $SOSOF =======]"
-               echo >>$xml "<error message='soso $outdir/$pattern.res $SOSO'/>"
+               echo >>$xml "<error message='`xmlesc "soso $outdir/$pattern.res $SOSO"`'/>"
                echo >>$xml "<system-out><![CDATA["
-               cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50
+               cat -v  -- "$outdir/$pattern.diff.sav.log" | head >>$xml -n 50
                echo >>$xml "]]></system-out>"
                nok="$nok $pattern"
                echo "$ii" >> "$ERRLIST"
        elif [ -n "$NSAV" ]; then
                echo "[======= fail $outdir/$pattern.res $NSAV =======]"
-               echo >>$xml "<error message='fail $outdir/$pattern.res $NSAV'/>"
+               echo >>$xml "<error message='`xmlesc "fail $outdir/$pattern.res $NSAV"`'/>"
                echo >>$xml "<system-out><![CDATA["
-               cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50
+               cat -v -- "$outdir/$pattern.diff.sav.log" | head >>$xml -n 50
                echo >>$xml "]]></system-out>"
                nok="$nok $pattern"
                echo "$ii" >> "$ERRLIST"
        elif [ -n "$NFIXME" ]; then
                echo "[======= changed $outdir/$pattern.res $NFIXME ======]"
-               echo >>$xml "<error message='changed $outdir/$pattern.res $NFIXME'/>"
+               echo >>$xml "<error message='`xmlesc "changed $outdir/$pattern.res $NFIXME"`'/>"
                echo >>$xml "<system-out><![CDATA["
-               cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50
+               cat -v -- "$outdir/$pattern.diff.sav.log" | head >>$xml -n 50
                echo >>$xml "]]></system-out>"
                nok="$nok $pattern"
                echo "$ii" >> "$ERRLIST"
-       elif [ -s $outdir/$pattern.res ]; then
+       elif [ -s "$outdir/$pattern.res" ]; then
                echo "[=== no sav ===] $outdir/$pattern.res is not empty"
                echo >>$xml "<error message='no sav and not empty'/>"
                echo >>$xml "<system-out><![CDATA["
-               cat -v >>$xml $outdir/$pattern.res
+               cat -v >>$xml -- "$outdir/$pattern.res"
                echo >>$xml "]]></system-out>"
                nos="$nos $pattern"
                echo "$ii" >> "$ERRLIST"
@@ -290,9 +297,9 @@ function process_result()
                echo "[0k] $outdir/$pattern.res is empty"
                ok="$ok $pattern"
        fi
-       if test -s $outdir/$pattern.cmp.err; then
+       if test -s "$outdir/$pattern.cmp.err"; then
                echo >>$xml "<system-err><![CDATA["
-               cat -v >>$xml $outdir/$pattern.cmp.err
+               cat -v >>$xml -- "$outdir/$pattern.cmp.err"
                echo >>$xml "]]></system-err>"
        fi
        echo >>$xml "</testcase>"
@@ -303,12 +310,12 @@ need_skip()
        test "$noskip" = true && return 1
        if echo "$1" | grep -f "$engine.skip" >/dev/null 2>&1; then
                echo "=> $2: [skip]"
-               echo >>$xml "<testcase classname='$3' name='$2' `timestamp`><skipped/></testcase>"
+               echo >>$xml "<testcase classname='`xmlesc "$3"`' name='`xmlesc "$2"`' `timestamp`><skipped/></testcase>"
                return 0
        fi
        if test -n "$isinterpret" && echo "$1" | grep -f "exec.skip" >/dev/null 2>&1; then
                echo "=> $2: [skip exec]"
-               echo >>$xml "<testcase classname='$3' name='$2' `timestamp`><skipped/></testcase>"
+               echo >>$xml "<testcase classname='`xmlesc "$3"`' name='`xmlesc "$2"`' `timestamp`><skipped/></testcase>"
                return 0
        fi
        return 1
@@ -470,16 +477,16 @@ fi
 echo >$xml "<testsuites><testsuite>"
 
 for ii in "$@"; do
-       if [ ! -f $ii ]; then
+       if [ ! -f "$ii" ]; then
                echo "File '$ii' does not exist."
                continue
        fi
-       f=`basename "$ii" .nit`
+       f=`basename -- "$ii" .nit`
 
        pack="tests.${engine}".`echo $ii | perl -p -e 's|^../([^/]*)/([a-zA-Z_]*).*|\1.\2| || s|^([a-zA-Z]*)[^_]*_([a-zA-Z]*).*|\1.\2| || s|\W*([a-zA-Z_]*).*|\1|'`
 
        # Sould we skip the file for this engine?
-       need_skip $f $f $pack && continue
+       need_skip "$f" "$f" "$pack" && continue
 
        tmp=${ii/../AA}
        if [ "x$tmp" = "x$ii" ]; then
@@ -488,12 +495,12 @@ for ii in "$@"; do
                includes="-I alt"
        fi
 
-       for i in "$ii" `./alterner.pl --start '#' --altsep '_' $ii`; do
-               bf=`basename $i .nit`
+       for i in "$ii" `./alterner.pl --start '#' --altsep '_' -- "$ii"`; do
+               bf=`basename -- "$i" .nit`
                ff="$outdir/$bf"
 
                # Sould we skip the alternative for this engine?
-               need_skip $bf $bf $pack && continue
+               need_skip "$bf" "$bf" "$pack" && continue
 
                echo -n "=> $bf: "
 
@@ -512,7 +519,7 @@ for ii in "$@"; do
 
                if [ -n "$isinterpret" ]; then
                        cat > "$ff.bin" <<END
-exec $NITC --no-color $OPT $includes -- "$i" "\$@"
+exec $NITC --no-color $OPT $includes -- $(printf '%q' "$i") "\$@"
 END
                        chmod +x "$ff.bin"
                        > "$ff.cmp.err"
@@ -534,8 +541,8 @@ END
                                saferun -o "$ff.time.out" $NITC --no-color $OPT -o "$ffout" $includes $nocc "$i" 2> "$ff.cmp.err" > "$ff.compile.log"
                        ERR=$?
                        if [ "x$verbose" = "xtrue" ]; then
-                               cat "$ff.compile.log"
-                               cat >&2 "$ff.cmp.err"
+                               cat -- "$ff.compile.log"
+                               cat >&2 -- "$ff.cmp.err"
                        fi
                fi
                if [ "$engine" = "emscripten" ]; then
@@ -543,24 +550,24 @@ END
                        chmod +x "$ff.bin"
                        if grep "Fatal Error: more than one primitive class" "$ff.compile.log" > /dev/null; then
                                echo " [skip] do no not imports kernel"
-                               echo >>$xml "<testcase classname='$pack' name='$bf' `timestamp`><skipped/></testcase>"
+                               echo >>$xml "<testcase classname='`xmlesc "$pack"`' name='`xmlesc "$bf"`' `timestamp`><skipped/></testcase>"
                                continue
                        fi
                fi
                if [ "$ERR" != 0 ]; then
                        echo -n "! "
-                       cat "$ff.compile.log" "$ff.cmp.err" > "$ff.res"
-                       process_result $bf $bf $pack
+                       cat -- "$ff.compile.log" "$ff.cmp.err" > "$ff.res"
+                       process_result "$bf" "$bf" "$pack"
                elif [ -n "$nocc" ]; then
                        # not compiled
                        echo -n "nocc "
                        > "$ff.res"
-                       process_result $bf $bf $pack
+                       process_result "$bf" "$bf" "$pack"
                elif [ -x "$ff.bin" ]; then
                        if skip_exec "$bf"; then
                                # No exec
                                > "$ff.res"
-                               process_result $bf $bf $pack
+                               process_result "$bf" "$bf" "$pack"
                                break
                        fi
                        echo -n ". "
@@ -572,21 +579,21 @@ END
                        fi
                        NIT_NO_STACK=1 LD_LIBRARY_PATH=$JNI_LIB_PATH \
                                saferun -a -o "$ff.time.out" "$ff.bin" $args < "$inputs" > "$ff.res" 2>"$ff.err"
-                       mv $ff.time.out $ff.times.out
-                       awk '{ SUM += $1} END { print SUM }' $ff.times.out > $ff.time.out
+                       mv "$ff.time.out" "$ff.times.out"
+                       awk '{ SUM += $1} END { print SUM }' "$ff.times.out" > "$ff.time.out"
 
                        if [ "x$verbose" = "xtrue" ]; then
-                               cat "$ff.res"
-                               cat >&2 "$ff.err"
+                               cat -- "$ff.res"
+                               cat >&2 -- "$ff.err"
                        fi
                        if [ -f "$ff.write" ]; then
-                               cat "$ff.write" >> "$ff.res"
+                               cat -- "$ff.write" >> "$ff.res"
                        elif [ -d "$ff.write" ]; then
-                               LANG=C /bin/ls -F $ff.write >> "$ff.res"
+                               LANG=C /bin/ls -F "$ff.write" >> "$ff.res"
                        fi
-                       cp "$ff.res"  "$ff.res2"
-                       cat "$ff.cmp.err" "$ff.err" "$ff.res2" > "$ff.res"
-                       process_result $bf $bf $pack
+                       cp -- "$ff.res"  "$ff.res2"
+                       cat -- "$ff.cmp.err" "$ff.err" "$ff.res2" > "$ff.res"
+                       process_result "$bf" "$bf" "$pack"
 
                        if [ -f "$f.args" ]; then
                                fargs=$f.args
@@ -599,13 +606,13 @@ END
                                        name="$bf args $cptr"
 
                                        # Sould we skip the input for this engine?
-                                       need_skip $bff "  $name" $pack && continue
+                                       need_skip "$bff" "  $name" "$pack" && continue
 
                                        # use a specific inputs file, if required
                                        if [ -f "$bff.inputs" ]; then
                                                ffinputs="$bff.inputs"
                                        else
-                                               ffinputs=$inputs
+                                               ffinputs="$inputs"
                                        fi
 
                                        rm -rf "$fff.res" "$fff.err" "$fff.write" 2> /dev/null
@@ -618,30 +625,30 @@ END
                                        chmod +x "$fff.bin"
                                        WRITE="$fff.write" saferun -o "$fff.time.out" sh -c "NIT_NO_STACK=1 $fff.bin < $ffinputs > $fff.res 2>$fff.err"
                                        if [ "x$verbose" = "xtrue" ]; then
-                                               cat "$fff.res"
-                                               cat >&2 "$fff.err"
+                                               cat -- "$fff.res"
+                                               cat >&2 -- "$fff.err"
                                        fi
                                        if [ -f "$fff.write" ]; then
-                                               cat "$fff.write" >> "$fff.res"
+                                               cat -- "$fff.write" >> "$fff.res"
                                        elif [ -d "$fff.write" ]; then
-                                               LANG=C /bin/ls -F $fff.write >> "$fff.res"
+                                               LANG=C /bin/ls -F -- "$fff.write" >> "$fff.res"
                                        fi
                                        if [ -s "$fff.err" ]; then
-                                               cp "$fff.res"  "$fff.res2"
-                                               cat "$fff.err" "$fff.res2" > "$fff.res"
+                                               cp -- "$fff.res"  "$fff.res2"
+                                               cat -- "$fff.err" "$fff.res2" > "$fff.res"
                                        fi
-                                       process_result $bff "  $name" $pack
-                               done < $fargs
+                                       process_result "$bff" "  $name" "$pack"
+                               done < "$fargs"
                        fi
                elif [ -f "$ff.bin" ]; then
                        #Not executable (platform?)"
                        > "$ff.res"
-                       process_result $bf "$bf" $pack
+                       process_result "$bf" "$bf" "$pack"
                else
                        echo -n "! "
-                       cat "$ff.cmp.err" > "$ff.res"
+                       cat -- "$ff.cmp.err" > "$ff.res"
                        echo "Compilation error" > "$ff.res"
-                       process_result $bf "$bf" $pack
+                       process_result "$bf" "$bf" "$pack"
                fi
        done
 done