// // Call_Core: C // // flags: // 1: wait, is implied when I/O redirection is enabled // 2: console // 4: shell // 8: info // 16: show // // Return -1 on error, otherwise the process return code. // // POSIX previous simple version was just 'return system(call);' // This uses 'execvp' which is "POSIX.1 conforming, UNIX compatible" // REB_R Call_Core(REBFRM *frame_) { PROCESS_INCLUDE_PARAMS_OF_CALL_INTERNAL_P; UNUSED(REF(console)); // !!! actually not paid attention to, why? // SECURE was never actually done for R3-Alpha // Check_Security(Canon(SYM_CALL), POL_EXEC, ARG(command)); // Make sure that if the output or error series are STRING! or BINARY!, // they are not read-only, before we try appending to them. // if (IS_TEXT(ARG(output)) or IS_BINARY(ARG(output))) FAIL_IF_READ_ONLY(ARG(output)); if (IS_TEXT(ARG(error)) or IS_BINARY(ARG(error))) FAIL_IF_READ_ONLY(ARG(error)); char *inbuf; size_t inbuf_size; if (not REF(input)) { null_input_buffer: inbuf = nullptr; inbuf_size = 0; } else switch (VAL_TYPE(ARG(input))) { case REB_LOGIC: goto null_input_buffer; case REB_TEXT: { inbuf_size = rebSpellIntoQ(nullptr, 0, ARG(input), rebEND); inbuf = rebAllocN(char, inbuf_size); size_t check; check = rebSpellIntoQ(inbuf, inbuf_size, ARG(input), rebEND); UNUSED(check); break; } case REB_FILE: { size_t size; inbuf = s_cast(rebBytes( // !!! why fileNAME size passed in??? &size, "file-to-local", ARG(input), rebEND )); inbuf_size = size; break; } case REB_BINARY: { inbuf = s_cast(rebBytes(&inbuf_size, ARG(input), rebEND)); break; } default: panic (ARG(input)); // typechecking should not have allowed it } bool flag_wait; if ( REF(wait) or ( IS_TEXT(ARG(input)) or IS_BINARY(ARG(input)) or IS_TEXT(ARG(output)) or IS_BINARY(ARG(output)) or IS_TEXT(ARG(error)) or IS_BINARY(ARG(error)) ) // I/O redirection implies /WAIT ){ flag_wait = true; } else flag_wait = false; // We synthesize the argc and argv from the "command", and in the process // we do dynamic allocations of argc strings through the API. These need // to be freed before we return. // char *cmd; int argc; const char **argv; if (IS_TEXT(ARG(command))) { // // !!! POSIX does not offer the ability to take a single command // line string when invoking a process. You have to use an argv[] // array. The only workaround to this is to run through a shell-- // but that would give you a new environment. We only parse the // command line if forced (Windows can call with a single command // line, but has the reverse problem: it has to make the command // line out of argv[] parts if you pass an array). // if (not REF(shell)) { REBVAL *block = rebValue( "parse-command-to-argv*", ARG(command), rebEND ); Move_Value(ARG(command), block); rebRelease(block); goto block_command; } cmd = rebSpell(ARG(command), rebEND); argc = 1; argv = rebAllocN(const char*, (argc + 1)); // !!! Make two copies because it frees cmd and all the argv. Review. // argv[0] = rebSpell(ARG(command), rebEND); argv[1] = nullptr; } else if (IS_BLOCK(ARG(command))) {
// // Do_Path_Throws_Core: C // // Evaluate an ANY_PATH! REBVAL, starting from the index position of that // path value and continuing to the end. // // The evaluator may throw because GROUP! is evaluated, e.g. `foo/(throw 1020)` // // If label_sym is passed in as being non-null, then the caller is implying // readiness to process a path which may be a function with refinements. // These refinements will be left in order on the data stack in the case // that `out` comes back as IS_FUNCTION(). // // If `opt_setval` is given, the path operation will be done as a "SET-PATH!" // if the path evaluation did not throw or error. HOWEVER the set value // is NOT put into `out`. This provides more flexibility on performance in // the evaluator, which may already have the `val` where it wants it, and // so the extra assignment would just be overhead. // // !!! Path evaluation is one of the parts of R3-Alpha that has not been // vetted very heavily by Ren-C, and needs a review and overhaul. // REBOOL Do_Path_Throws_Core( REBVAL *out, REBSTR **label_out, const RELVAL *path, REBCTX *specifier, REBVAL *opt_setval ) { REBPVS pvs; REBDSP dsp_orig = DSP; assert(ANY_PATH(path)); // !!! There is a bug in the dispatch such that if you are running a // set path, it does not always assign the output, because it "thinks you // aren't going to look at it". This presumably originated from before // parens were allowed in paths, and neglects cases like: // // foo/(throw 1020): value // // We always have to check to see if a throw occurred. Until this is // streamlined, we have to at minimum set it to something that is *not* // thrown so that we aren't testing uninitialized memory. A safe trash // will do, which is unset in release builds. // if (opt_setval) SET_TRASH_SAFE(out); // None of the values passed in can live on the data stack, because // they might be relocated during the path evaluation process. // assert(!IN_DATA_STACK_DEBUG(out)); assert(!IN_DATA_STACK_DEBUG(path)); assert(!opt_setval || !IN_DATA_STACK_DEBUG(opt_setval)); // Not currently robust for reusing passed in path or value as the output assert(out != path && out != opt_setval); assert(!opt_setval || !THROWN(opt_setval)); // Initialize REBPVS -- see notes in %sys-do.h // pvs.opt_setval = opt_setval; pvs.store = out; pvs.orig = path; pvs.item = VAL_ARRAY_AT(pvs.orig); // may not be starting at head of PATH! // The path value that's coming in may be relative (in which case it // needs to use the specifier passed in). Or it may be specific already, // in which case we should use the specifier in the value to process // its array contents. // if (IS_RELATIVE(path)) { #if !defined(NDEBUG) assert(specifier != SPECIFIED); if (VAL_RELATIVE(path) != VAL_FUNC(CTX_FRAME_FUNC_VALUE(specifier))) { Debug_Fmt("Specificity mismatch found in path dispatch"); PROBE_MSG(path, "the path being evaluated"); PROBE_MSG(FUNC_VALUE(VAL_RELATIVE(path)), "expected func"); PROBE_MSG(CTX_FRAME_FUNC_VALUE(specifier), "actual func"); assert(FALSE); } #endif pvs.item_specifier = specifier; } else pvs.item_specifier = VAL_SPECIFIER(const_KNOWN(path)); // Seed the path evaluation process by looking up the first item (to // get a datatype to dispatch on for the later path items) // if (IS_WORD(pvs.item)) { pvs.value = GET_MUTABLE_VAR_MAY_FAIL(pvs.item, pvs.item_specifier); pvs.value_specifier = SPECIFIED; if (IS_VOID(pvs.value)) fail (Error_No_Value_Core(pvs.item, pvs.item_specifier)); } else { // !!! Ideally there would be some way to deal with writes to // temporary locations, like this pvs.value...if a set-path sets // it, then it will be discarded. COPY_VALUE(pvs.store, VAL_ARRAY_AT(pvs.orig), pvs.item_specifier); pvs.value = pvs.store; pvs.value_specifier = SPECIFIED; } // Start evaluation of path: if (IS_END(pvs.item + 1)) { // If it was a single element path, return the value rather than // try to dispatch it (would cause a crash at time of writing) // // !!! Is this the desired behavior, or should it be an error? } else if (Path_Dispatch[VAL_TYPE(pvs.value)]) { REBOOL threw = Next_Path_Throws(&pvs); // !!! See comments about why the initialization of out is necessary. // Without it this assertion can change on some things: // // t: now // t/time: 10:20:03 // // (It thinks pvs.value has its THROWN bit set when it completed // successfully. It was a PE_USE_STORE case where pvs.value was reset to // pvs.store, and pvs.store has its thrown bit set. Valgrind does not // catch any uninitialized variables.) // // There are other cases that do trip valgrind when omitting the // initialization, though not as clearly reproducible. // assert(threw == THROWN(pvs.value)); if (threw) return TRUE; // Check for errors: if (NOT_END(pvs.item + 1) && !IS_FUNCTION(pvs.value)) { // // Only function refinements should get by this line: REBVAL specified_orig; COPY_VALUE(&specified_orig, pvs.orig, specifier); REBVAL specified_item; COPY_VALUE(&specified_item, pvs.item, specifier); fail (Error(RE_INVALID_PATH, &specified_orig, &specified_item)); } } else if (!IS_FUNCTION(pvs.value)) { REBVAL specified; COPY_VALUE(&specified, pvs.orig, specifier); fail (Error(RE_BAD_PATH_TYPE, &specified, Type_Of(pvs.value))); } if (opt_setval) { // If SET then we don't return anything assert(IS_END(pvs.item) + 1); return FALSE; } // If storage was not used, then copy final value back to it: if (pvs.value != pvs.store) COPY_VALUE(pvs.store, pvs.value, pvs.value_specifier); assert(!THROWN(out)); // Return 0 if not function or is :path/word... if (!IS_FUNCTION(pvs.value)) { assert(IS_END(pvs.item) + 1); return FALSE; } if (label_out) { REBVAL refinement; // When a function is hit, path processing stops as soon as the // processed sub-path resolves to a function. The path is still sitting // on the position of the last component of that sub-path. Usually, // this last component in the sub-path is a word naming the function. // if (IS_WORD(pvs.item)) { *label_out = VAL_WORD_SPELLING(pvs.item); } else { // In rarer cases, the final component (completing the sub-path to // the function to call) is not a word. Such as when you use a path // to pick by index out of a block of functions: // // functions: reduce [:add :subtract] // functions/1 10 20 // // Or when you have an immediate function value in a path with a // refinement. Tricky to make, but possible: // // do reduce [ // to-path reduce [:append 'only] [a] [b] // ] // // !!! When a function was not invoked through looking up a word // (or a word in a path) to use as a label, there were once three // different alternate labels used. One was SYM__APPLY_, another // was ROOT_NONAME, and another was to be the type of the function // being executed. None are fantastic, we do the type for now. *label_out = Canon(SYM_FROM_KIND(VAL_TYPE(pvs.value))); } // Move on to the refinements (if any) ++pvs.item; // !!! Currently, the mainline path evaluation "punts" on refinements. // When it finds a function, it stops the path evaluation and leaves // the position pvs.path before the list of refinements. // // A more elegant solution would be able to process and notice (for // instance) that `:APPEND/ONLY` should yield a function value that // has been specialized with a refinement. Path chaining should thus // be able to effectively do this and give the refined function object // back to the evaluator or other client. // // If a label_sym is passed in, we recognize that a function dispatch // is going to be happening. We do not want to pay to generate the // new series that would be needed to make a temporary function that // will be invoked and immediately GC'd So we gather the refinements // on the data stack. // // This code simulates that path-processing-to-data-stack, but it // should really be something in dispatch iself. In any case, we put // refinements on the data stack...and caller knows refinements are // from dsp_orig to DSP (thanks to accounting, all other operations // should balance!) for (; NOT_END(pvs.item); ++pvs.item) { // "the refinements" if (IS_VOID(pvs.item)) continue; if (IS_GROUP(pvs.item)) { // // Note it is not legal to use the data stack directly as the // output location for a DO (might be resized) if (Do_At_Throws( &refinement, VAL_ARRAY(pvs.item), VAL_INDEX(pvs.item), IS_RELATIVE(pvs.item) ? pvs.item_specifier // if relative, use parent's : VAL_SPECIFIER(const_KNOWN(pvs.item)) // else embedded )) { *out = refinement; DS_DROP_TO(dsp_orig); return TRUE; } if (IS_VOID(&refinement)) continue; DS_PUSH(&refinement); } else if (IS_GET_WORD(pvs.item)) { DS_PUSH_TRASH; *DS_TOP = *GET_OPT_VAR_MAY_FAIL(pvs.item, pvs.item_specifier); if (IS_VOID(DS_TOP)) { DS_DROP; continue; } } else DS_PUSH_RELVAL(pvs.item, pvs.item_specifier); // Whatever we were trying to use as a refinement should now be // on the top of the data stack, and only words are legal ATM // if (!IS_WORD(DS_TOP)) { fail (Error(RE_BAD_REFINE, DS_TOP)); } // Go ahead and canonize the word symbol so we don't have to // do it each time in order to get a case-insenstive compare // INIT_WORD_SPELLING(DS_TOP, VAL_WORD_CANON(DS_TOP)); } // To make things easier for processing, reverse the refinements on // the data stack (we needed to evaluate them in forward order). // This way we can just pop them as we go, and know if they weren't // all consumed if it doesn't get back to `dsp_orig` by the end. if (dsp_orig != DSP) { REBVAL *bottom = DS_AT(dsp_orig + 1); REBVAL *top = DS_TOP; while (top > bottom) { refinement = *bottom; *bottom = *top; *top = refinement; top--; bottom++; } } } else { // !!! Historically this just ignores a result indicating this is a // function with refinements, e.g. ':append/only'. However that // ignoring seems unwise. It should presumably create a modified // function in that case which acts as if it has the refinement. // // If the caller did not pass in a label pointer we assume they are // likely not ready to process any refinements. // if (NOT_END(pvs.item + 1)) fail (Error(RE_TOO_LONG)); // !!! Better error or add feature } return FALSE; }
// // Init_Serial_Scheme: C // void Init_Serial_Scheme(void) { Register_Scheme(Canon(SYM_SERIAL), Serial_Actor); }
// // Mold_Vector: C // void Mold_Vector(const REBVAL *value, REB_MOLD *mold, REBOOL molded) { REBSER *vect = VAL_SERIES(value); REBYTE *data = SER_DATA_RAW(vect); REBCNT bits = VECT_TYPE(vect); // REBCNT dims = vect->size >> 8; REBCNT len; REBCNT n; REBCNT c; union {REBU64 i; REBDEC d;} v; REBYTE buf[32]; REBYTE l; if (GET_MOPT(mold, MOPT_MOLD_ALL)) { len = VAL_LEN_HEAD(value); n = 0; } else { len = VAL_LEN_AT(value); n = VAL_INDEX(value); } if (molded) { enum Reb_Kind kind = (bits >= VTSF08) ? REB_DECIMAL : REB_INTEGER; Pre_Mold(value, mold); if (!GET_MOPT(mold, MOPT_MOLD_ALL)) Append_Codepoint_Raw(mold->series, '['); if (bits >= VTUI08 && bits <= VTUI64) Append_Unencoded(mold->series, "unsigned "); Emit( mold, "N I I [", Canon(SYM_FROM_KIND(kind)), bit_sizes[bits & 3], len ); if (len) New_Indented_Line(mold); } c = 0; for (; n < SER_LEN(vect); n++) { v.i = get_vect(bits, data, n); if (bits < VTSF08) { l = Emit_Integer(buf, v.i); } else { l = Emit_Decimal(buf, v.d, 0, '.', mold->digits); } Append_Unencoded_Len(mold->series, s_cast(buf), l); if ((++c > 7) && (n + 1 < SER_LEN(vect))) { New_Indented_Line(mold); c = 0; } else Append_Codepoint_Raw(mold->series, ' '); } if (len) { // // remove final space (overwritten with terminator) // TERM_UNI_LEN(mold->series, UNI_LEN(mold->series) - 1); } if (molded) { if (len) New_Indented_Line(mold); Append_Codepoint_Raw(mold->series, ']'); if (!GET_MOPT(mold, MOPT_MOLD_ALL)) { Append_Codepoint_Raw(mold->series, ']'); } else { Post_Mold(value, mold); } } }