*/ void Expand_Frame(REBSER *frame, REBCNT delta, REBCNT copy) /* ** Expand a frame. Copy words if flagged. ** ***********************************************************************/ { REBSER *words = FRM_WORD_SERIES(frame); Extend_Series(frame, delta); // Expand or copy WORDS block: if (copy) FRM_WORD_SERIES(frame) = Copy_Expand_Block(words, delta); else Extend_Series(words, delta); }
*/ void Expand_Frame(REBSER *frame, REBCNT delta, REBCNT copy) /* ** Expand a frame. Copy words if flagged. ** ***********************************************************************/ { REBSER *words = FRM_WORD_SERIES(frame); Extend_Series(frame, delta); BLK_TERM(frame); // Expand or copy WORDS block: if (copy) { REBOOL managed = SERIES_GET_FLAG(FRM_WORD_SERIES(frame), SER_MANAGED); FRM_WORD_SERIES(frame) = Copy_Array_Extra_Shallow(words, delta); if (managed) MANAGE_SERIES(FRM_WORD_SERIES(frame)); } else { Extend_Series(words, delta); BLK_TERM(words); } }
*/ REBVAL *Append_Event(void) /* ** Append an event to the end of the current event port queue. ** Return a pointer to the event value. ** ** Note: this function may be called from out of environment, ** so do NOT extend the event queue here. If it does not have ** space, return 0. (Should it overwrite or wrap???) ** ***********************************************************************/ { REBVAL *port; REBVAL *value; REBVAL *state; port = Get_System(SYS_PORTS, PORTS_SYSTEM); if (!IS_PORT(port)) return 0; // verify it is a port object // Get queue block: state = VAL_OBJ_VALUE(port, STD_PORT_STATE); if (!IS_BLOCK(state)) return 0; // Append to tail if room: if (SERIES_FULL(VAL_SERIES(state))) { if (VAL_TAIL(state) > EVENTS_LIMIT) { Panic_DEAD_END(RP_MAX_EVENTS); } else { Extend_Series(VAL_SERIES(state), EVENTS_CHUNK); //RL_Print("event queue increased to :%d\n", SERIES_REST(VAL_SERIES(state))); } } VAL_TAIL(state)++; value = VAL_BLK_TAIL(state); SET_END(value); value--; SET_NONE(value); //Dump_Series(VAL_SERIES(state), "state"); //Print("Tail: %d %d", VAL_TAIL(state), nn++); return value; }
// // Transport_Actor: C // static REB_R Transport_Actor(struct Reb_Call *call_, REBSER *port, REBCNT action, enum Transport_Types proto) { REBREQ *sock; // IO request REBVAL *spec; // port spec REBVAL *arg; // action argument value REBVAL *val; // e.g. port number value REBINT result; // IO result REBCNT refs; // refinement argument flags REBCNT len; // generic length REBSER *ser; // simplifier Validate_Port(port, action); *D_OUT = *D_ARG(1); arg = DS_ARGC > 1 ? D_ARG(2) : NULL; sock = cast(REBREQ*, Use_Port_State(port, RDI_NET, sizeof(*sock))); if (proto == TRANSPORT_UDP) { SET_FLAG(sock->modes, RST_UDP); } //Debug_Fmt("Sock: %x", sock); spec = OFV(port, STD_PORT_SPEC); if (!IS_OBJECT(spec)) fail (Error(RE_INVALID_PORT)); // sock->timeout = 4000; // where does this go? !!! // HOW TO PREVENT OVERWRITE DURING BUSY OPERATION!!! // Should it just ignore it or cause an error? // Actions for an unopened socket: if (!IS_OPEN(sock)) { switch (action) { // Ordered by frequency case A_OPEN: arg = Obj_Value(spec, STD_PORT_SPEC_NET_HOST); val = Obj_Value(spec, STD_PORT_SPEC_NET_PORT_ID); if (OS_DO_DEVICE(sock, RDC_OPEN)) fail (Error_On_Port(RE_CANNOT_OPEN, port, -12)); SET_OPEN(sock); // Lookup host name (an extra TCP device step): if (IS_STRING(arg)) { sock->common.data = VAL_BIN(arg); sock->special.net.remote_port = IS_INTEGER(val) ? VAL_INT32(val) : 80; result = OS_DO_DEVICE(sock, RDC_LOOKUP); // sets remote_ip field if (result < 0) fail (Error_On_Port(RE_NO_CONNECT, port, sock->error)); return R_OUT; } // Host IP specified: else if (IS_TUPLE(arg)) { sock->special.net.remote_port = IS_INTEGER(val) ? VAL_INT32(val) : 80; memcpy(&sock->special.net.remote_ip, VAL_TUPLE(arg), 4); break; } // No host, must be a LISTEN socket: else if (IS_NONE(arg)) { SET_FLAG(sock->modes, RST_LISTEN); sock->common.data = 0; // where ACCEPT requests are queued sock->special.net.local_port = IS_INTEGER(val) ? VAL_INT32(val) : 8000; break; } else fail (Error_On_Port(RE_INVALID_SPEC, port, -10)); case A_CLOSE: return R_OUT; case A_OPENQ: return R_FALSE; case A_UPDATE: // allowed after a close break; default: fail (Error_On_Port(RE_NOT_OPEN, port, -12)); } } // Actions for an open socket: switch (action) { // Ordered by frequency case A_UPDATE: // Update the port object after a READ or WRITE operation. // This is normally called by the WAKE-UP function. arg = OFV(port, STD_PORT_DATA); if (sock->command == RDC_READ) { if (ANY_BINSTR(arg)) VAL_TAIL(arg) += sock->actual; } else if (sock->command == RDC_WRITE) { SET_NONE(arg); // Write is done. } return R_NONE; case A_READ: // Read data into a buffer, expanding the buffer if needed. // If no length is given, program must stop it at some point. refs = Find_Refines(call_, ALL_READ_REFS); if ( !GET_FLAG(sock->modes, RST_UDP) && !GET_FLAG(sock->state, RSM_CONNECT) ) { fail (Error_On_Port(RE_NOT_CONNECTED, port, -15)); } // Setup the read buffer (allocate a buffer if needed): arg = OFV(port, STD_PORT_DATA); if (!IS_STRING(arg) && !IS_BINARY(arg)) { Val_Init_Binary(arg, Make_Binary(NET_BUF_SIZE)); } ser = VAL_SERIES(arg); sock->length = SERIES_AVAIL(ser); // space available if (sock->length < NET_BUF_SIZE/2) Extend_Series(ser, NET_BUF_SIZE); sock->length = SERIES_AVAIL(ser); sock->common.data = STR_TAIL(ser); // write at tail //if (SERIES_TAIL(ser) == 0) sock->actual = 0; // Actual for THIS read, not for total. //Print("(max read length %d)", sock->length); result = OS_DO_DEVICE(sock, RDC_READ); // recv can happen immediately if (result < 0) fail (Error_On_Port(RE_READ_ERROR, port, sock->error)); break; case A_WRITE: // Write the entire argument string to the network. // The lower level write code continues until done. refs = Find_Refines(call_, ALL_WRITE_REFS); if (!GET_FLAG(sock->modes, RST_UDP) && !GET_FLAG(sock->state, RSM_CONNECT)) fail (Error_On_Port(RE_NOT_CONNECTED, port, -15)); // Determine length. Clip /PART to size of string if needed. spec = D_ARG(2); len = VAL_LEN(spec); if (refs & AM_WRITE_PART) { REBCNT n = Int32s(D_ARG(ARG_WRITE_LIMIT), 0); if (n <= len) len = n; } // Setup the write: *OFV(port, STD_PORT_DATA) = *spec; // keep it GC safe sock->length = len; sock->common.data = VAL_BIN_DATA(spec); sock->actual = 0; //Print("(write length %d)", len); result = OS_DO_DEVICE(sock, RDC_WRITE); // send can happen immediately if (result < 0) fail (Error_On_Port(RE_WRITE_ERROR, port, sock->error)); if (result == DR_DONE) SET_NONE(OFV(port, STD_PORT_DATA)); break; case A_PICK: // FIRST server-port returns new port connection. len = Get_Num_Arg(arg); // Position if (len == 1 && GET_FLAG(sock->modes, RST_LISTEN) && sock->common.data) Accept_New_Port(D_OUT, port, sock); // sets D_OUT else fail (Error_Out_Of_Range(arg)); break; case A_QUERY: // Get specific information - the scheme's info object. // Special notation allows just getting part of the info. Ret_Query_Net(port, sock, D_OUT); break; case A_OPENQ: // Connect for clients, bind for servers: if (sock->state & ((1<<RSM_CONNECT) | (1<<RSM_BIND))) return R_TRUE; return R_FALSE; case A_CLOSE: if (IS_OPEN(sock)) { OS_DO_DEVICE(sock, RDC_CLOSE); SET_CLOSED(sock); } break; case A_LENGTH: arg = OFV(port, STD_PORT_DATA); len = ANY_SERIES(arg) ? VAL_TAIL(arg) : 0; SET_INTEGER(D_OUT, len); break; case A_OPEN: result = OS_DO_DEVICE(sock, RDC_CONNECT); if (result < 0) fail (Error_On_Port(RE_NO_CONNECT, port, sock->error)); break; case A_DELETE: // Temporary to TEST error handler! { REBVAL *event = Append_Event(); // sets signal VAL_SET(event, REB_EVENT); // (has more space, if we need it) VAL_EVENT_TYPE(event) = EVT_ERROR; VAL_EVENT_DATA(event) = 101; VAL_EVENT_REQ(event) = sock; } break; default: fail (Error_Illegal_Action(REB_PORT, action)); } return R_OUT; }
*/ static REB_R Console_Actor(struct Reb_Call *call_, REBSER *port, REBCNT action) /* ***********************************************************************/ { REBREQ *req; REBINT result; REBVAL *arg = D_ARG(2); REBSER *ser; Validate_Port(port, action); arg = D_ARG(2); *D_OUT = *D_ARG(1); req = cast(REBREQ*, Use_Port_State(port, RDI_STDIO, sizeof(REBREQ))); switch (action) { case A_READ: // If not open, open it: if (!IS_OPEN(req)) { if (OS_DO_DEVICE(req, RDC_OPEN)) Trap_Port_DEAD_END(RE_CANNOT_OPEN, port, req->error); } // If no buffer, create a buffer: arg = OFV(port, STD_PORT_DATA); if (!IS_STRING(arg) && !IS_BINARY(arg)) { Set_Binary(arg, MAKE_OS_BUFFER(OUT_BUF_SIZE)); } ser = VAL_SERIES(arg); RESET_SERIES(ser); req->common.data = BIN_HEAD(ser); req->length = SERIES_AVAIL(ser); #ifdef nono // Is the buffer large enough? req->length = SERIES_AVAIL(ser); // space available if (req->length < OUT_BUF_SIZE/2) Extend_Series(ser, OUT_BUF_SIZE); req->length = SERIES_AVAIL(ser); // Don't make buffer too large: Bug #174 ????? if (req->length > 1024) req->length = 1024; //??? req->common.data = STR_TAIL(ser); // write at tail //??? if (SERIES_TAIL(ser) == 0) req->actual = 0; //??? #endif result = OS_DO_DEVICE(req, RDC_READ); if (result < 0) Trap_Port_DEAD_END(RE_READ_ERROR, port, req->error); #ifdef nono // Does not belong here!! // Remove or replace CRs: result = 0; for (n = 0; n < req->actual; n++) { chr = GET_ANY_CHAR(ser, n); if (chr == CR) { chr = LF; // Skip LF if it follows: if ((n+1) < req->actual && LF == GET_ANY_CHAR(ser, n+1)) n++; } SET_ANY_CHAR(ser, result, chr); result++; } #endif // !!! Among many confusions in this file, it said "Another copy???" //Set_String(D_OUT, Copy_OS_Str(ser->data, result)); Set_Binary(D_OUT, Copy_Bytes(req->common.data, req->actual)); break; case A_OPEN: // ?? why??? //if (OS_DO_DEVICE(req, RDC_OPEN)) Trap_Port_DEAD_END(RE_CANNOT_OPEN, port); SET_OPEN(req); break; case A_CLOSE: SET_CLOSED(req); //OS_DO_DEVICE(req, RDC_CLOSE); break; case A_OPENQ: if (IS_OPEN(req)) return R_TRUE; return R_FALSE; default: Trap_Action_DEAD_END(REB_PORT, action); } return R_OUT; }
*/ static REBINT Do_Cmd(REBDIA *dia) /* ** Returns the length of command processed or error. See below. ** ***********************************************************************/ { REBVAL *fargs; REBINT size; REBVAL *val; REBINT err; REBINT n; // Get formal arguments block for this command: fargs = FRM_VALUES(dia->dialect) + dia->cmd; if (!IS_BLOCK(fargs)) return -REB_DIALECT_BAD_SPEC; dia->fargs = VAL_SERIES(fargs); fargs = VAL_BLK_DATA(fargs); size = Count_Dia_Args(fargs); // approximate // Preallocate output block (optimize for large blocks): if (dia->len > size) size = dia->len; if (GET_FLAG(dia->flags, RDIA_ALL)) { Extend_Series(dia->out, size+1); } else { Resize_Series(dia->out, size+1); // tail = 0 CLEAR_SERIES(dia->out); // Be sure it is entirely cleared } // Insert command word: if (!GET_FLAG(dia->flags, RDIA_NO_CMD)) { val = Append_Value(dia->out); Set_Word(val, FRM_WORD_SYM(dia->dialect, dia->cmd), dia->dialect, dia->cmd); if (GET_FLAG(dia->flags, RDIA_LIT_CMD)) VAL_SET(val, REB_LIT_WORD); dia->outi++; size++; } if (dia->cmd > 1) dia->argi++; // default cmd has no word arg // Foreach argument provided: for (n = dia->len; n > 0; n--, dia->argi++) { val = Eval_Arg(dia); if (!val) return -REB_DIALECT_BAD_ARG; if (IS_END(val)) break; if (!IS_NONE(val)) { //Print("n %d len %d argi %d", n, dia->len, dia->argi); err = Add_Arg(dia, val); // 1: good, 0: no-type, -N: error if (err == 0) return n; // remainder if (err < 0) return err; } } // If not enough args, pad with NONE values: if (dia->cmd > 1) { for (n = SERIES_TAIL(dia->out); n < size; n++) { Append_Value(dia->out); } } dia->outi = SERIES_TAIL(dia->out); return 0; }
*/ REBCNT Make_Word(REBYTE *str, REBCNT len) /* ** Given a string and its length, compute its hash value, ** search for a match, and if not found, add it to the table. ** Length of zero indicates you provided a zero terminated string. ** Return the table index for the word (whether found or new). ** ***********************************************************************/ { REBINT hash; REBINT size; REBINT skip; REBINT n; REBCNT h; REBCNT *hashes; REBVAL *words; REBVAL *w; //REBYTE *sss = Get_Sym_Name(1); // (Debugging method) if (len == 0) len = LEN_BYTES(str); // If hash part of word table is too dense, expand it: if (PG_Word_Table.series->tail > PG_Word_Table.hashes->tail/2) Expand_Word_Table(); ASSERT((SERIES_TAIL(PG_Word_Table.series) == SERIES_TAIL(Bind_Table)), RP_BIND_TABLE_SIZE); // If word symbol part of word table is full, expand it: if (SERIES_FULL(PG_Word_Table.series)) { Extend_Series(PG_Word_Table.series, 256); } if (SERIES_FULL(Bind_Table)) { Extend_Series(Bind_Table, 256); CLEAR_SERIES(Bind_Table); } size = (REBINT)PG_Word_Table.hashes->tail; words = BLK_HEAD(PG_Word_Table.series); hashes = (REBCNT *)PG_Word_Table.hashes->data; // Hash the word, including a skip factor for lookup: hash = Hash_Word(str, len); skip = (hash & 0x0000FFFF) % size; if (skip == 0) skip = 1; hash = (hash & 0x00FFFF00) % size; //Debug_Fmt("%s hash %d skip %d", str, hash, skip); // Search hash table for word match: while (NZ(h = hashes[hash])) { while ((n = Compare_UTF8(VAL_SYM_NAME(words+h), str, len)) >= 0) { //if (Match_String("script", str, len)) // Debug_Fmt("---- %s %d %d\n", VAL_SYM_NAME(&words[h]), n, h); if (n == 0) return h; // direct hit if (VAL_SYM_ALIAS(words+h)) h = VAL_SYM_ALIAS(words+h); else goto make_sym; // Create new alias for word } hash += skip; if (hash >= size) hash -= size; } make_sym: n = PG_Word_Table.series->tail; w = words + n; if (h) { // Alias word (h = canon word) VAL_SYM_ALIAS(words+h) = n; VAL_SYM_CANON(w) = VAL_SYM_CANON(words+h); } else { // Canon (base version of) word (h == 0) hashes[hash] = n; VAL_SYM_CANON(w) = n; } VAL_SYM_ALIAS(w) = 0; VAL_SYM_NINDEX(w) = Make_Word_Name(str, len); VAL_SET(w, REB_HANDLE); // These are allowed because of the SERIES_FULL checks above which // add one extra to the TAIL check comparision. However, their // termination values (nulls) will be missing. PG_Word_Table.series->tail++; Bind_Table->tail++; return n; }
*/ REBYTE *Scan_Item(REBYTE *src, REBYTE *end, REBUNI term, REBYTE *invalid) /* ** Scan as UTF8 an item like a file or URL. ** ** Returns continuation point or zero for error. ** ** Put result into the MOLD_BUF as uni-chars. ** ***********************************************************************/ { REBUNI c; REBSER *buf; buf = BUF_MOLD; RESET_TAIL(buf); while (src < end && *src != term) { c = *src; // End of stream? if (c == 0) break; // If no term, then any white will terminate: if (!term && IS_WHITE(c)) break; // Ctrl chars are invalid: if (c < ' ') return 0; // invalid char if (c == '\\') c = '/'; // Accept %xx encoded char: else if (c == '%') { if (!Scan_Hex2(src+1, &c, FALSE)) return 0; src += 2; } // Accept ^X encoded char: else if (c == '^') { if (src+1 == end) return 0; // nothing follows ^ c = Scan_Char(&src); if (!term && IS_WHITE(c)) break; src--; } // Accept UTF8 encoded char: else if (c >= 0x80) { c = Decode_UTF8_Char(&src, 0); // zero on error if (c == 0) return 0; } // Is char as literal valid? (e.g. () [] etc.) else if (invalid && strchr(invalid, c)) return 0; src++; *UNI_SKIP(buf, buf->tail) = c; // not affected by Extend_Series if (++(buf->tail) >= SERIES_REST(buf)) Extend_Series(buf, 1); } if (*src && *src == term) src++; UNI_TERM(buf); return src; }
*/ REBYTE *Scan_Quote(REBYTE *src, SCAN_STATE *scan_state) /* ** Scan a quoted string, handling all the escape characters. ** ** The result will be put into the temporary MOLD_BUF unistring. ** ***********************************************************************/ { REBINT nest = 0; REBUNI term; REBINT chr; REBCNT lines = 0; REBSER *buf = BUF_MOLD; RESET_TAIL(buf); term = (*src++ == '{') ? '}' : '"'; // pick termination while (*src != term || nest > 0) { chr = *src; switch (chr) { case 0: return 0; // Scan_state shows error location. case '^': chr = Scan_Char(&src); if (chr == -1) return 0; src--; break; case '{': if (term != '"') nest++; break; case '}': if (term != '"' && nest > 0) nest--; break; case CR: if (src[1] == LF) src++; // fall thru case LF: if (term == '"') return 0; lines++; chr = LF; break; default: if (chr >= 0x80) { chr = Decode_UTF8_Char(&src, 0); // zero on error if (chr == 0) return 0; } } src++; *UNI_SKIP(buf, buf->tail) = chr; if (++(buf->tail) >= SERIES_REST(buf)) Extend_Series(buf, 1); } src++; // Skip ending quote or brace. if (scan_state) scan_state->line_count += lines; UNI_TERM(buf); return src; }
// // Serial_Actor: C // static REB_R Serial_Actor(REBFRM *frame_, REBCTX *port, REBSYM action) { REBREQ *req; // IO request REBVAL *spec; // port spec REBVAL *arg; // action argument value REBVAL *val; // e.g. port number value REBINT result; // IO result REBCNT refs; // refinement argument flags REBCNT len; // generic length REBSER *ser; // simplifier REBVAL *path; Validate_Port(port, action); *D_OUT = *D_ARG(1); // Validate PORT fields: spec = CTX_VAR(port, STD_PORT_SPEC); if (!IS_OBJECT(spec)) fail (Error(RE_INVALID_PORT)); path = Obj_Value(spec, STD_PORT_SPEC_HEAD_REF); if (!path) fail (Error(RE_INVALID_SPEC, spec)); //if (!IS_FILE(path)) fail (Error(RE_INVALID_SPEC, path)); req = cast(REBREQ*, Use_Port_State(port, RDI_SERIAL, sizeof(*req))); // Actions for an unopened serial port: if (!IS_OPEN(req)) { switch (action) { case SYM_OPEN: arg = Obj_Value(spec, STD_PORT_SPEC_SERIAL_PATH); if (! (IS_FILE(arg) || IS_STRING(arg) || IS_BINARY(arg))) fail (Error(RE_INVALID_PORT_ARG, arg)); req->special.serial.path = ALLOC_N(REBCHR, MAX_SERIAL_DEV_PATH); OS_STRNCPY( req->special.serial.path, // // !!! This is assuming VAL_DATA contains native chars. // Should it? (2 bytes on windows, 1 byte on linux/mac) // SER_AT(REBCHR, VAL_SERIES(arg), VAL_INDEX(arg)), MAX_SERIAL_DEV_PATH ); arg = Obj_Value(spec, STD_PORT_SPEC_SERIAL_SPEED); if (! IS_INTEGER(arg)) fail (Error(RE_INVALID_PORT_ARG, arg)); req->special.serial.baud = VAL_INT32(arg); //Secure_Port(SYM_SERIAL, ???, path, ser); arg = Obj_Value(spec, STD_PORT_SPEC_SERIAL_DATA_SIZE); if (!IS_INTEGER(arg) || VAL_INT64(arg) < 5 || VAL_INT64(arg) > 8 ) { fail (Error(RE_INVALID_PORT_ARG, arg)); } req->special.serial.data_bits = VAL_INT32(arg); arg = Obj_Value(spec, STD_PORT_SPEC_SERIAL_STOP_BITS); if (!IS_INTEGER(arg) || VAL_INT64(arg) < 1 || VAL_INT64(arg) > 2 ) { fail (Error(RE_INVALID_PORT_ARG, arg)); } req->special.serial.stop_bits = VAL_INT32(arg); arg = Obj_Value(spec, STD_PORT_SPEC_SERIAL_PARITY); if (IS_BLANK(arg)) { req->special.serial.parity = SERIAL_PARITY_NONE; } else { if (!IS_WORD(arg)) fail (Error(RE_INVALID_PORT_ARG, arg)); switch (VAL_WORD_SYM(arg)) { case SYM_ODD: req->special.serial.parity = SERIAL_PARITY_ODD; break; case SYM_EVEN: req->special.serial.parity = SERIAL_PARITY_EVEN; break; default: fail (Error(RE_INVALID_PORT_ARG, arg)); } } arg = Obj_Value(spec, STD_PORT_SPEC_SERIAL_FLOW_CONTROL); if (IS_BLANK(arg)) { req->special.serial.flow_control = SERIAL_FLOW_CONTROL_NONE; } else { if (!IS_WORD(arg)) fail (Error(RE_INVALID_PORT_ARG, arg)); switch (VAL_WORD_SYM(arg)) { case SYM_HARDWARE: req->special.serial.flow_control = SERIAL_FLOW_CONTROL_HARDWARE; break; case SYM_SOFTWARE: req->special.serial.flow_control = SERIAL_FLOW_CONTROL_SOFTWARE; break; default: fail (Error(RE_INVALID_PORT_ARG, arg)); } } if (OS_DO_DEVICE(req, RDC_OPEN)) fail (Error_On_Port(RE_CANNOT_OPEN, port, -12)); SET_OPEN(req); return R_OUT; case SYM_CLOSE: return R_OUT; case SYM_OPEN_Q: return R_FALSE; default: fail (Error_On_Port(RE_NOT_OPEN, port, -12)); } } // Actions for an open socket: switch (action) { case SYM_READ: refs = Find_Refines(frame_, ALL_READ_REFS); // Setup the read buffer (allocate a buffer if needed): arg = CTX_VAR(port, STD_PORT_DATA); if (!IS_STRING(arg) && !IS_BINARY(arg)) { Val_Init_Binary(arg, Make_Binary(32000)); } ser = VAL_SERIES(arg); req->length = SER_AVAIL(ser); // space available if (req->length < 32000/2) Extend_Series(ser, 32000); req->length = SER_AVAIL(ser); // This used STR_TAIL (obsolete, equivalent to BIN_TAIL) but was it // sure the series was byte sized? Added in a check. assert(BYTE_SIZE(ser)); req->common.data = BIN_TAIL(ser); // write at tail //if (SER_LEN(ser) == 0) req->actual = 0; // Actual for THIS read, not for total. #ifdef DEBUG_SERIAL printf("(max read length %d)", req->length); #endif result = OS_DO_DEVICE(req, RDC_READ); // recv can happen immediately if (result < 0) fail (Error_On_Port(RE_READ_ERROR, port, req->error)); #ifdef DEBUG_SERIAL for (len = 0; len < req->actual; len++) { if (len % 16 == 0) printf("\n"); printf("%02x ", req->common.data[len]); } printf("\n"); #endif *D_OUT = *arg; return R_OUT; case SYM_WRITE: refs = Find_Refines(frame_, ALL_WRITE_REFS); // Determine length. Clip /PART to size of string if needed. spec = D_ARG(2); len = VAL_LEN_AT(spec); if (refs & AM_WRITE_PART) { REBCNT n = Int32s(D_ARG(ARG_WRITE_LIMIT), 0); if (n <= len) len = n; } // Setup the write: *CTX_VAR(port, STD_PORT_DATA) = *spec; // keep it GC safe req->length = len; req->common.data = VAL_BIN_AT(spec); req->actual = 0; //Print("(write length %d)", len); result = OS_DO_DEVICE(req, RDC_WRITE); // send can happen immediately if (result < 0) fail (Error_On_Port(RE_WRITE_ERROR, port, req->error)); break; case SYM_UPDATE: // Update the port object after a READ or WRITE operation. // This is normally called by the WAKE-UP function. arg = CTX_VAR(port, STD_PORT_DATA); if (req->command == RDC_READ) { if (ANY_BINSTR(arg)) { SET_SERIES_LEN( VAL_SERIES(arg), VAL_LEN_HEAD(arg) + req->actual ); } } else if (req->command == RDC_WRITE) { SET_BLANK(arg); // Write is done. } return R_BLANK; case SYM_OPEN_Q: return R_TRUE; case SYM_CLOSE: if (IS_OPEN(req)) { OS_DO_DEVICE(req, RDC_CLOSE); SET_CLOSED(req); } break; default: fail (Error_Illegal_Action(REB_PORT, action)); } return R_OUT; }