X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=tools%2Ftranslate.c;h=1e3585a86471ea1d1d2e454a1ef1f7d7b2642abb;hb=087ced532964f78f90072b96a2a16cc8f56d14db;hp=4d685022a9cca27f4f2ab2b713b16327f790ed26;hpb=2eae9f2384fb8c779e5b61658b81eef7dbd4505e;p=ia32rtools.git diff --git a/tools/translate.c b/tools/translate.c index 4d68502..1e3585a 100644 --- a/tools/translate.c +++ b/tools/translate.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "my_assert.h" @@ -60,7 +61,7 @@ enum op_flags { OPF_EBP_S = (1 << 13), /* ebp used as scratch here, not BP */ OPF_DF = (1 << 14), /* DF flag set */ OPF_ATAIL = (1 << 15), /* tail call with reused arg frame */ - OPF_32BIT = (1 << 16), /* 32bit division */ + OPF_32BIT = (1 << 16), /* enough to do 32bit for this op */ OPF_LOCK = (1 << 17), /* op has lock prefix */ OPF_VAPUSH = (1 << 18), /* vararg ptr push (as call arg) */ OPF_DONE = (1 << 19), /* already fully handled by analysis */ @@ -68,8 +69,9 @@ enum op_flags { OPF_NOREGS = (1 << 21), /* don't track regs of this op */ OPF_FPUSH = (1 << 22), /* pushes x87 stack */ OPF_FPOP = (1 << 23), /* pops x87 stack */ - OPF_FSHIFT = (1 << 24), /* x87 stack shift is actually needed */ - OPF_FINT = (1 << 25), /* integer float op arg */ + OPF_FPOPP = (1 << 24), /* pops x87 stack twice */ + OPF_FSHIFT = (1 << 25), /* x87 stack shift is actually needed */ + OPF_FINT = (1 << 26), /* integer float op arg */ }; enum op_op { @@ -283,8 +285,8 @@ enum sct_func_attr { enum x87_const { X87_CONST_1 = 1, - X87_CONST_2T, - X87_CONST_2E, + X87_CONST_L2T, + X87_CONST_L2E, X87_CONST_PI, X87_CONST_LG2, X87_CONST_LN2, @@ -1083,6 +1085,10 @@ static const struct { { "fld", OP_FLD, 1, 1, OPF_FPUSH }, { "fild", OP_FILD, 1, 1, OPF_FPUSH|OPF_FINT }, { "fld1", OP_FLDc, 0, 0, OPF_FPUSH }, + { "fldl2t", OP_FLDc, 0, 0, OPF_FPUSH }, + { "fldl2e", OP_FLDc, 0, 0, OPF_FPUSH }, + { "fldpi", OP_FLDc, 0, 0, OPF_FPUSH }, + { "fldlg2", OP_FLDc, 0, 0, OPF_FPUSH }, { "fldln2", OP_FLDc, 0, 0, OPF_FPUSH }, { "fldz", OP_FLDc, 0, 0, OPF_FPUSH }, { "fst", OP_FST, 1, 1, 0 }, @@ -1109,6 +1115,10 @@ static const struct { { "fisubr", OP_FISUBR, 1, 1, OPF_FINT }, { "fcom", OP_FCOM, 0, 1, 0 }, { "fcomp", OP_FCOM, 0, 1, OPF_FPOP }, + { "fcompp", OP_FCOM, 0, 0, OPF_FPOPP }, + { "fucom", OP_FCOM, 0, 1, 0 }, + { "fucomp", OP_FCOM, 0, 1, OPF_FPOP }, + { "fucompp",OP_FCOM, 0, 0, OPF_FPOPP }, { "fnstsw", OP_FNSTSW, 1, 1, OPF_DATA }, { "fchs", OP_FCHS, 0, 0, 0 }, { "fcos", OP_FCOS, 0, 0, 0 }, @@ -1327,20 +1337,26 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) // fallthrough case OP_MUL: // singleop mul - op->regmask_src |= op->regmask_dst; - op->regmask_dst = (1 << xDX) | (1 << xAX); if (op->operand[0].lmod == OPLM_UNSPEC) op->operand[0].lmod = OPLM_DWORD; + op->regmask_src = mxAX | op->regmask_dst; + op->regmask_dst = mxAX; + if (op->operand[0].lmod != OPLM_BYTE) + op->regmask_dst |= mxDX; break; case OP_DIV: case OP_IDIV: // we could set up operands for edx:eax, but there is no real need to // (see is_opr_modified()) - op->regmask_src |= op->regmask_dst; - op->regmask_dst = (1 << xDX) | (1 << xAX); if (op->operand[0].lmod == OPLM_UNSPEC) op->operand[0].lmod = OPLM_DWORD; + op->regmask_src = mxAX | op->regmask_dst; + op->regmask_dst = mxAX; + if (op->operand[0].lmod != OPLM_BYTE) { + op->regmask_src |= mxDX; + op->regmask_dst |= mxDX; + } break; case OP_SHL: @@ -1395,6 +1411,8 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) break; case OP_CALL: + // needed because of OPF_DATA + op->regmask_src = op->regmask_dst; // trashed regs must be explicitly detected later op->regmask_dst = 0; break; @@ -1413,12 +1431,20 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) op->regmask_dst |= mxST0; if (IS(words[op_w] + 3, "1")) op->operand[0].val = X87_CONST_1; + else if (IS(words[op_w] + 3, "l2t")) + op->operand[0].val = X87_CONST_L2T; + else if (IS(words[op_w] + 3, "l2e")) + op->operand[0].val = X87_CONST_L2E; + else if (IS(words[op_w] + 3, "pi")) + op->operand[0].val = X87_CONST_PI; + else if (IS(words[op_w] + 3, "lg2")) + op->operand[0].val = X87_CONST_LG2; else if (IS(words[op_w] + 3, "ln2")) op->operand[0].val = X87_CONST_LN2; else if (IS(words[op_w] + 3, "z")) op->operand[0].val = X87_CONST_Z; else - aerr("TODO\n"); + aerr("fld what?\n"); break; case OP_FST: @@ -1474,6 +1500,13 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc) case OP_FCOM: op->regmask_src |= mxST0; + if (op->operand_cnt == 0) { + op->operand_cnt = 1; + op->operand[0].type = OPT_REG; + op->operand[0].lmod = OPLM_QWORD; + op->operand[0].reg = xST1; + op->regmask_src |= mxST1; + } break; default: @@ -2117,7 +2150,7 @@ static void check_func_pp(struct parsed_op *po, } static const char *check_label_read_ref(struct parsed_op *po, - const char *name) + const char *name, int *is_import) { const struct parsed_proto *pp; @@ -2128,6 +2161,9 @@ static const char *check_label_read_ref(struct parsed_op *po, if (pp->is_func) check_func_pp(po, pp, "ref"); + if (is_import != NULL) + *is_import = pp->is_import; + return pp->name; } @@ -2146,6 +2182,7 @@ static char *out_src_opr(char *buf, size_t buf_size, char tmp1[256], tmp2[256]; char expr[256]; const char *name; + int is_import = 0; char *p; int ret; @@ -2218,7 +2255,11 @@ static char *out_src_opr(char *buf, size_t buf_size, break; case OPT_LABEL: - name = check_label_read_ref(po, popr->name); + name = check_label_read_ref(po, popr->name, &is_import); + if (is_import) + // for imported data, asm is loading the offset + goto do_offset; + if (cast[0] == 0 && popr->is_ptr) cast = "(u32)"; @@ -2234,7 +2275,8 @@ static char *out_src_opr(char *buf, size_t buf_size, break; case OPT_OFFSET: - name = check_label_read_ref(po, popr->name); + do_offset: + name = check_label_read_ref(po, popr->name, NULL); if (cast[0] == 0) cast = "(u32)"; if (is_lea) @@ -2330,11 +2372,20 @@ static char *out_opr_float(char *buf, size_t buf_size, { const char *cast = NULL; char tmp[256]; + union { + float f; + int i; + } u; switch (popr->type) { case OPT_REG: - if (popr->reg < xST0 || popr->reg > xST7) - ferr(po, "bad reg: %d\n", popr->reg); + if (popr->reg < xST0 || popr->reg > xST7) { + // func arg + ferr_assert(po, po->op == OP_PUSH); + ferr_assert(po, popr->lmod == OPLM_DWORD); + snprintf(buf, buf_size, "*(float *)&%s", opr_reg_p(po, popr)); + break; + } if (need_float_stack) { if (popr->reg == xST0) @@ -2371,6 +2422,16 @@ static char *out_opr_float(char *buf, size_t buf_size, snprintf(buf, buf_size, "*(%s *)(%s)", cast, tmp); break; + case OPT_CONST: + // only for func float args pushes + ferr_assert(po, po->op == OP_PUSH); + u.i = po->operand[0].val; + if (ceilf(u.f) == u.f) + snprintf(buf, buf_size, "%.1ff", u.f); + else + snprintf(buf, buf_size, "%.8ff", u.f); + break; + default: ferr(po, "invalid float type: %d\n", popr->type); } @@ -3775,8 +3836,12 @@ static void resolve_branches_parse_calls(int opcnt) else if (po->operand[0].type == OPT_LABEL) { tmpname = opr_name(po, 0); - if (IS_START(tmpname, "loc_")) - ferr(po, "call to loc_*\n"); + if (IS_START(tmpname, "loc_")) { + if (!g_seh_found) + ferr(po, "call to loc_*\n"); + // eliminate_seh() must take care of it + continue; + } if (IS(tmpname, "__alloca_probe")) continue; if (IS(tmpname, "__SEH_prolog")) { @@ -3878,6 +3943,7 @@ tailcall: static int resolve_origin(int i, const struct parsed_opr *opr, int magic, int *op_i, int *is_caller); +static void set_label(int i, const char *name); static void eliminate_seh_writes(int opcnt) { @@ -3905,6 +3971,87 @@ static void eliminate_seh_writes(int opcnt) } } +static void eliminate_seh_finally(int opcnt) +{ + const char *target_name = NULL; + const char *return_name = NULL; + int exits[MAX_EXITS]; + int exit_count = 0; + int call_i = -1; + int target_i = -1; + int return_i = -1; + int tgend_i = -1; + int i; + + for (i = 0; i < opcnt; i++) { + if (ops[i].op != OP_CALL) + continue; + if (!IS_START(opr_name(&ops[i], 0), "loc_")) + continue; + if (target_name != NULL) + ferr(&ops[i], "multiple finally calls? (last was %s)\n", + target_name); + target_name = opr_name(&ops[i], 0); + call_i = i; + + if (g_labels[i + 1] == NULL) + set_label(i + 1, "seh_fin_done"); + return_name = g_labels[i + 1]; + return_i = i + 1; + } + + if (call_i == -1) + // no finally block + return; + + // find finally code (bt_i is not set because it's call) + for (i = 0; i < opcnt; i++) { + if (g_labels[i] == NULL) + continue; + if (!IS(g_labels[i], target_name)) + continue; + + ferr_assert(&ops[i], target_i == -1); + target_i = i; + } + ferr_assert(&ops[0], target_i != -1); + + find_reachable_exits(target_i, opcnt, target_i + opcnt * 24, + exits, &exit_count); + ferr_assert(&ops[target_i], exit_count == 1); + ferr_assert(&ops[target_i], ops[exits[0]].op == OP_RET); + tgend_i = exits[0]; + + // convert to jumps, link + ops[call_i].op = OP_JMP; + ops[call_i].bt_i = target_i; + add_label_ref(&g_label_refs[target_i], call_i); + + ops[tgend_i].op = OP_JMP; + ops[tgend_i].flags &= ~OPF_TAIL; + ops[tgend_i].flags |= OPF_JMP; + ops[tgend_i].bt_i = return_i; + ops[tgend_i].operand_cnt = 1; + ops[tgend_i].operand[0].type = OPT_LABEL; + snprintf(ops[tgend_i].operand[0].name, NAMELEN, "%s", return_name); + add_label_ref(&g_label_refs[return_i], tgend_i); + + // rm seh finally entry code + for (i = target_i - 1; i >= 0; i--) { + if (g_labels[i] != NULL && g_label_refs[i].i != -1) + return; + if (ops[i].flags & OPF_CJMP) + return; + if (ops[i].flags & (OPF_JMP | OPF_TAIL)) + break; + } + for (i = target_i - 1; i >= 0; i--) { + if (ops[i].flags & (OPF_JMP | OPF_TAIL)) + break; + ops[i].flags |= OPF_RMD | OPF_DONE | OPF_NOREGS; + } +} + static void eliminate_seh(int opcnt) { int i, j, k, ret; @@ -3947,6 +4094,7 @@ static void eliminate_seh(int opcnt) } eliminate_seh_writes(opcnt); + eliminate_seh_finally(opcnt); } static void eliminate_seh_calls(int opcnt) @@ -3985,6 +4133,7 @@ static void eliminate_seh_calls(int opcnt) ferr_assert(ops, epilog_found); eliminate_seh_writes(opcnt); + eliminate_seh_finally(opcnt); } static int scan_prologue(int i, int opcnt, int *ecx_push, int *esp_sub) @@ -4894,6 +5043,37 @@ out: return pp; } +static void mark_float_arg(struct parsed_op *po, + struct parsed_proto *pp, int arg, int *regmask_ffca) +{ + po->p_argnext = -1; + po->p_argnum = arg + 1; + ferr_assert(po, pp->arg[arg].datap == NULL); + pp->arg[arg].datap = po; + po->flags |= OPF_DONE | OPF_FARGNR | OPF_FARG; + if (regmask_ffca != NULL) + *regmask_ffca |= 1 << arg; +} + +static int check_for_stp(int i, int i_to) +{ + struct parsed_op *po; + + for (; i < i_to; i++) { + po = &ops[i]; + if (po->op == OP_FST) + return i; + if (g_labels[i] != NULL || (po->flags & OPF_JMP)) + return -1; + if (po->op == OP_CALL || po->op == OP_PUSH || po->op == OP_POP) + return -1; + if (po->op == OP_ADD && po->operand[0].reg == xSP) + return -1; + } + + return -1; +} + static int collect_call_args_no_push(int i, struct parsed_proto *pp, int *regmask_ffca) { @@ -4927,13 +5107,7 @@ static int collect_call_args_no_push(int i, struct parsed_proto *pp, } arg = base_arg + offset / 4; - po->p_argnext = -1; - po->p_argnum = arg + 1; - ferr_assert(po, pp->arg[arg].datap == NULL); - pp->arg[arg].datap = po; - po->flags |= OPF_DONE | OPF_FARGNR | OPF_FARG; - if (regmask_ffca != NULL) - *regmask_ffca |= 1 << arg; + mark_float_arg(po, pp, arg, regmask_ffca); } else if (po->op == OP_SUB && po->operand[0].reg == xSP && po->operand[1].type == OPT_CONST) @@ -4960,7 +5134,8 @@ static int collect_call_args_early(int i, struct parsed_proto *pp, { struct parsed_op *po; int arg, ret; - int j; + int offset; + int j, k; for (arg = 0; arg < pp->argc; arg++) if (pp->arg[arg].reg == NULL) @@ -5028,10 +5203,24 @@ static int collect_call_args_early(int i, struct parsed_proto *pp, { ops[j].p_argnext = -1; ferr_assert(&ops[j], pp->arg[arg].datap == NULL); - pp->arg[arg].datap = &ops[j]; - if (regmask != NULL && ops[j].operand[0].type == OPT_REG) - *regmask |= 1 << ops[j].operand[0].reg; + k = check_for_stp(j + 1, i); + if (k != -1) { + // push ecx; fstp dword ptr [esp] + ret = parse_stack_esp_offset(&ops[k], + ops[k].operand[0].name, &offset); + if (ret == 0 && offset == 0) { + if (!pp->arg[arg].type.is_float) + ferr(&ops[i], "arg %d should be float\n", arg + 1); + mark_float_arg(&ops[k], pp, arg, regmask_ffca); + } + } + + if (pp->arg[arg].datap == NULL) { + pp->arg[arg].datap = &ops[j]; + if (regmask != NULL && ops[j].operand[0].type == OPT_REG) + *regmask |= 1 << ops[j].operand[0].reg; + } ops[j].flags |= OPF_RMD | OPF_DONE | OPF_FARGNR | OPF_FARG; ops[j].flags &= ~OPF_RSAVE; @@ -5509,7 +5698,15 @@ static void reg_use_pass(int i, int opcnt, unsigned char *cbits, *regmask |= regmask_now; // released regs - if (po->flags & OPF_FPOP) { + if (po->flags & OPF_FPOPP) { + if ((regmask_now & mxSTa) == 0) + ferr(po, "float pop on empty stack?\n"); + if (regmask_now & mxST7_2) + po->flags |= OPF_FSHIFT; + if (!(regmask_now & mxST7_2)) + regmask_now &= ~mxST1_0; + } + else if (po->flags & OPF_FPOP) { if ((regmask_now & mxSTa) == 0) ferr(po, "float pop on empty stack?\n"); if (regmask_now & (mxST7_2 | mxST1)) @@ -6090,7 +6287,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) save_arg_vars[po->p_arggrp] |= 1 << (po->p_argnum - 1); // correct for "full stack" mode late enable - if ((po->flags & (OPF_PPUSH|OPF_FPOP)) && need_float_stack) + if ((po->flags & (OPF_PPUSH|OPF_FPOP|OPF_FPOPP)) + && need_float_stack) po->flags |= OPF_FSHIFT; } @@ -6129,6 +6327,9 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (pp->is_fptr && !(pp->name[0] != 0 && pp->is_arg)) { if (pp->name[0] != 0) { + if (IS_START(pp->name, "guess")) + pp->is_guessed = 1; + memmove(pp->name + 2, pp->name, strlen(pp->name) + 1); memcpy(pp->name, "i_", 2); @@ -7166,9 +7367,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) fprintf(fout, "%s%s = %s;\n", buf3, pp->name, out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], "(void *)", 0)); - if (pp->is_unresolved || IS_START(pp->name, "i_guess")) - fprintf(fout, "%sunresolved_call(\"%s:%d\", %s);\n", - buf3, asmfn, po->asmln, pp->name); + } + if (pp->is_fptr && (pp->is_unresolved || pp->is_guessed)) { + fprintf(fout, "%sunresolved_call(\"%s:%d\", %s);\n", + buf3, asmfn, po->asmln, pp->name); } fprintf(fout, "%s", buf3); @@ -7276,15 +7478,37 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) if (tmp_op->operand[0].lmod == OPLM_QWORD) arg++; } + else if (pp->arg[arg].type.is_64bit) { + ferr_assert(po, tmp_op->p_argpass == 0); + ferr_assert(po, !pp->arg[arg].is_saved); + ferr_assert(po, !pp->arg[arg].type.is_float); + ferr_assert(po, cast[0] == 0); + out_src_opr(buf1, sizeof(buf1), + tmp_op, &tmp_op->operand[0], cast, 0); + tmp_op = pp->arg[++arg].datap; + ferr_assert(po, tmp_op != NULL); + out_src_opr(buf2, sizeof(buf2), + tmp_op, &tmp_op->operand[0], cast, 0); + fprintf(fout, "((u64)(%s) << 32) | (%s)", + buf2, buf1); + } else if (tmp_op->p_argpass != 0) { + ferr_assert(po, !pp->arg[arg].type.is_float); fprintf(fout, "a%d", tmp_op->p_argpass); } else if (pp->arg[arg].is_saved) { ferr_assert(po, tmp_op->p_argnum > 0); + ferr_assert(po, !pp->arg[arg].type.is_float); fprintf(fout, "%s%s", cast, saved_arg_name(buf1, sizeof(buf1), tmp_op->p_arggrp, tmp_op->p_argnum)); } + else if (pp->arg[arg].type.is_float) { + ferr_assert(po, !pp->arg[arg].type.is_64bit); + fprintf(fout, "%s", + out_src_opr_float(buf1, sizeof(buf1), + tmp_op, &tmp_op->operand[0], need_float_stack)); + } else { fprintf(fout, "%s", out_src_opr(buf1, sizeof(buf1), @@ -7499,9 +7723,13 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) } switch (po->operand[0].val) { case X87_CONST_1: fprintf(fout, "1.0;"); break; - case X87_CONST_LN2: fprintf(fout, "0.693147180559945;"); break; + case X87_CONST_L2T: fprintf(fout, "3.321928094887362;"); break; + case X87_CONST_L2E: fprintf(fout, "M_LOG2E;"); break; + case X87_CONST_PI: fprintf(fout, "M_PI;"); break; + case X87_CONST_LG2: fprintf(fout, "0.301029995663981;"); break; + case X87_CONST_LN2: fprintf(fout, "M_LN2;"); break; case X87_CONST_Z: fprintf(fout, "0.0;"); break; - default: ferr(po, "TODO\n"); break; + default: ferr_assert(po, 0); break; } break; @@ -7649,7 +7877,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) fprintf(fout, " f_sw = %s < %s ? 0x0100 : 0;", float_st0, buf1); } - else if (mask == 0x4000) { // C3 -> = + else if (mask == 0x4000 || mask == 0x4400) { // C3 -> = fprintf(fout, " f_sw = %s == %s ? 0x4000 : 0;", float_st0, buf1); } @@ -7668,10 +7896,16 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt) else ferr(po, "unhandled sw mask: %x\n", mask); if (po->flags & OPF_FSHIFT) { - if (need_float_stack) - fprintf(fout, " f_stp++;"); - else + if (need_float_stack) { + if (po->flags & OPF_FPOPP) + fprintf(fout, " f_stp += 2;"); + else + fprintf(fout, " f_stp++;"); + } + else { + ferr_assert(po, !(po->flags & OPF_FPOPP)); fprintf(fout, " f_st0 = f_st1;"); + } } break; } @@ -7864,6 +8098,7 @@ struct func_prototype { int has_ret:3; // -1, 0, 1: unresolved, no, yes unsigned int dep_resolved:1; unsigned int is_stdcall:1; + unsigned int eax_pass:1; // returns without touching eax struct func_proto_dep *dep_func; int dep_func_cnt; const struct parsed_proto *pp; // seed pp, if any @@ -8078,6 +8313,7 @@ static void gen_hdr_dep_pass(int i, int opcnt, unsigned char *cbits, if (ret != 1 && from_caller) { // unresolved eax - probably void func *has_ret = 0; + fp->eax_pass = 1; } else { if (j >= 0 && ops[j].op == OP_CALL) { @@ -8269,8 +8505,11 @@ static void gen_hdr(const char *funcn, int opcnt) // noreturn OS functions break; } - if (ops[i].op != OP_NOP && ops[i].op != OPP_ABORT) + if (!(ops[i].flags & OPF_RMD) + && ops[i].op != OP_NOP && ops[i].op != OPP_ABORT) + { ferr(&ops[i], "unreachable code\n"); + } } for (i = 0; i < g_eqcnt; i++) { @@ -8492,6 +8731,12 @@ static void output_hdr(FILE *fout) // adjust functions referenced from data segment do_func_refs_from_data(); + // final adjustments + for (i = 0; i < hg_fp_cnt; i++) { + if (hg_fp[i].eax_pass && (hg_fp[i].regmask_dep & mxAX)) + hg_fp[i].has_ret = 1; + } + // note: messes up .proto ptr, don't use //qsort(hg_fp, hg_fp_cnt, sizeof(hg_fp[0]), hg_fp_cmp_id); @@ -8915,7 +9160,7 @@ int main(int argc, char *argv[]) int pi = 0; int i, j; int ret, len; - char *p; + char *p, *p2; int wordc; for (arg = 1; arg < argc; arg++) { @@ -9192,6 +9437,11 @@ parse_words: // allow asm patches in comments if (*p == ';') { + // skip IDA's forced non-removable comment + if (!IS_START(p, "; sct") && (p2 = strchr(p + 1, ';'))) + p = p2; + } + if (*p == ';' && IS_START(p, "; sct")) { if (IS_START(p, "; sctpatch:")) { p = sskip(p + 11); if (*p == 0 || *p == ';')