@SuppressWarnings({"PMD.NPathComplexity", "PMD.ExcessiveMethodLength"})
    private static int gsub(LuaCallFrame cf) {
        String srcTemp = (String)BaseLib.getArg(cf, 1, BaseLib.TYPE_STRING, names[GSUB]);
        String pTemp = (String)BaseLib.getArg(cf, 2, BaseLib.TYPE_STRING, names[GSUB]);
        Object repl = BaseLib.getArg(cf, 3, null, names[GSUB]);
        {
            String tmp = BaseLib.rawTostring(repl);
            if (tmp != null) {
                repl = tmp;
            }
        }
        Double num = (Double)BaseLib.getOptArg(cf, 4, BaseLib.TYPE_NUMBER);
        // if i isn't supplied, we want to substitute all occurrences of the pattern
        int maxSubstitutions = (num == null) ? Integer.MAX_VALUE : num.intValue();

        StringPointer pattern = new StringPointer (pTemp);
        StringPointer src = new StringPointer (srcTemp);

        boolean anchor = false;
        if (pattern.getChar() == '^') {
            anchor = true;
            pattern.postIncrString ( 1 );
        }

        String replType = BaseLib.type(repl);
        if (!(replType == BaseLib.TYPE_FUNCTION ||
                        replType == BaseLib.TYPE_STRING ||
                        replType == BaseLib.TYPE_TABLE)) {
            BaseLib.fail(("string/function/table expected, got "+replType));
        }

        MatchState ms = new MatchState ();
        ms.callFrame = cf;
        ms.src_init = src.getClone();
        ms.endIndex = src.length();

        int n = 0;
        StringBuffer b = new StringBuffer();
        StringPointer e = null;
        while (n < maxSubstitutions) {
            ms.level = 0;
            e = match(ms, src, pattern);
            if (e != null) {
                n++;
                addValue(ms, repl, b, src, e);
            }

            if (e != null && e.getIndex() > src.getIndex()) { // non empty match?
                src.setIndex (e.getIndex());  // skip it
            } else if (src.getIndex() < ms.endIndex) {
                b.append(src.postIncrString(1));
            } else {
                break;
            }

            if (anchor) {
                break;
            }
        }
        return cf.push(b.append(src.getString()).toString(), new Double(n));
    }
Only works with non-negative numbers
main/src/main/java/cgeo/geocaching/wherigo/kahlua/stdlib/StringLib.java