// // 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; }
// // Series_Common_Action_Returns: C // // This routine is called to handle actions on ANY-SERIES! that can be taken // care of without knowing what specific kind of series it is. So generally // index manipulation, and things like LENGTH/etc. // // The strange name is to convey the result in an if statement, in the same // spirit as the `if (XXX_Throws(...)) { /* handle throw */ }` pattern. // REBOOL Series_Common_Action_Returns( REB_R *r, // `r_out` would be slightly confusing, considering R_OUT REBFRM *frame_, REBSYM action ) { REBVAL *value = D_ARG(1); REBVAL *arg = D_ARGC > 1 ? D_ARG(2) : NULL; REBINT index = cast(REBINT, VAL_INDEX(value)); REBINT tail = cast(REBINT, VAL_LEN_HEAD(value)); REBINT len = 0; switch (action) { //-- Navigation: case SYM_HEAD: VAL_INDEX(value) = 0; break; case SYM_TAIL: VAL_INDEX(value) = (REBCNT)tail; break; case SYM_HEAD_Q: *r = (index == 0) ? R_TRUE : R_FALSE; return TRUE; // handled case SYM_TAIL_Q: *r = (index >= tail) ? R_TRUE : R_FALSE; return TRUE; // handled case SYM_PAST_Q: *r = (index > tail) ? R_TRUE : R_FALSE; return TRUE; // handled case SYM_NEXT: if (index < tail) VAL_INDEX(value)++; break; case SYM_BACK: if (index > 0) VAL_INDEX(value)--; break; case SYM_SKIP: case SYM_AT: len = Get_Num_From_Arg(arg); { REBI64 i = (REBI64)index + (REBI64)len; if (action == SYM_SKIP) { if (IS_LOGIC(arg)) i--; } else { // A_AT if (len > 0) i--; } if (i > (REBI64)tail) i = (REBI64)tail; else if (i < 0) i = 0; VAL_INDEX(value) = (REBCNT)i; } break; case SYM_INDEX_OF: SET_INTEGER(D_OUT, cast(REBI64, index) + 1); *r = R_OUT; return TRUE; // handled case SYM_LENGTH: SET_INTEGER(D_OUT, tail > index ? tail - index : 0); *r = R_OUT; return TRUE; // handled case SYM_REMOVE: // /PART length FAIL_IF_LOCKED_SERIES(VAL_SERIES(value)); len = D_REF(2) ? Partial(value, 0, D_ARG(3)) : 1; index = cast(REBINT, VAL_INDEX(value)); if (index < tail && len != 0) Remove_Series(VAL_SERIES(value), VAL_INDEX(value), len); break; case SYM_ADD: // Join_Strings(value, arg); case SYM_SUBTRACT: // "test this" - 10 case SYM_MULTIPLY: // "t" * 4 = "tttt" case SYM_DIVIDE: case SYM_REMAINDER: case SYM_POWER: case SYM_ODD_Q: case SYM_EVEN_Q: case SYM_ABSOLUTE: fail (Error_Illegal_Action(VAL_TYPE(value), action)); default: return FALSE; // not a common operation, not handled } *D_OUT = *value; *r = R_OUT; return TRUE; // handled }
// // 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; }
*/ static REB_R Dir_Actor(struct Reb_Call *call_, REBSER *port, REBCNT action) /* ** Internal port handler for file directories. ** ***********************************************************************/ { REBVAL *spec; REBVAL *path; REBVAL *state; REBREQ dir; REBCNT args = 0; REBINT result; REBCNT len; //REBYTE *flags; Validate_Port(port, action); *D_OUT = *D_ARG(1); CLEARS(&dir); // Validate and fetch relevant PORT fields: spec = BLK_SKIP(port, STD_PORT_SPEC); if (!IS_OBJECT(spec)) raise Error_1(RE_INVALID_SPEC, spec); path = Obj_Value(spec, STD_PORT_SPEC_HEAD_REF); if (!path) raise Error_1(RE_INVALID_SPEC, spec); if (IS_URL(path)) path = Obj_Value(spec, STD_PORT_SPEC_HEAD_PATH); else if (!IS_FILE(path)) raise Error_1(RE_INVALID_SPEC, path); state = BLK_SKIP(port, STD_PORT_STATE); // if block, then port is open. //flags = Security_Policy(SYM_FILE, path); // Get or setup internal state data: dir.port = port; dir.device = RDI_FILE; switch (action) { case A_READ: //Trap_Security(flags[POL_READ], POL_READ, path); args = Find_Refines(call_, ALL_READ_REFS); if (!IS_BLOCK(state)) { // !!! ignores /SKIP and /PART, for now Init_Dir_Path(&dir, path, 1, POL_READ); Val_Init_Block(state, Make_Array(7)); // initial guess result = Read_Dir(&dir, VAL_SERIES(state)); ///OS_FREE(dir.file.path); if (result < 0) raise Error_On_Port(RE_CANNOT_OPEN, port, dir.error); *D_OUT = *state; SET_NONE(state); } else { // !!! This copies the strings in the block, shallowly. What is // the purpose of doing this? Why copy at all? Val_Init_Block( D_OUT, Copy_Array_Core_Managed( VAL_SERIES(state), 0, VAL_BLK_LEN(state), FALSE, // !deep TS_STRING ) ); } break; case A_CREATE: //Trap_Security(flags[POL_WRITE], POL_WRITE, path); if (IS_BLOCK(state)) raise Error_1(RE_ALREADY_OPEN, path); create: Init_Dir_Path(&dir, path, 0, POL_WRITE | REMOVE_TAIL_SLASH); // Sets RFM_DIR too result = OS_DO_DEVICE(&dir, RDC_CREATE); ///OS_FREE(dir.file.path); if (result < 0) raise Error_1(RE_NO_CREATE, path); if (action == A_CREATE) { // !!! Used to return R_ARG2, but create is single arity. :-/ return R_ARG1; } SET_NONE(state); break; case A_RENAME: if (IS_BLOCK(state)) raise Error_1(RE_ALREADY_OPEN, path); else { REBSER *target; Init_Dir_Path(&dir, path, 0, POL_WRITE | REMOVE_TAIL_SLASH); // Sets RFM_DIR too // Convert file name to OS format: if (!(target = Value_To_OS_Path(D_ARG(2), TRUE))) raise Error_1(RE_BAD_FILE_PATH, D_ARG(2)); dir.common.data = BIN_DATA(target); OS_DO_DEVICE(&dir, RDC_RENAME); Free_Series(target); if (dir.error) raise Error_1(RE_NO_RENAME, path); } break; case A_DELETE: //Trap_Security(flags[POL_WRITE], POL_WRITE, path); SET_NONE(state); Init_Dir_Path(&dir, path, 0, POL_WRITE); // !!! add *.r deletion // !!! add recursive delete (?) result = OS_DO_DEVICE(&dir, RDC_DELETE); ///OS_FREE(dir.file.path); if (result < 0) raise Error_1(RE_NO_DELETE, path); // !!! Returned R_ARG2 before, but there is no second argument :-/ return R_ARG1; case A_OPEN: // !! If open fails, what if user does a READ w/o checking for error? if (IS_BLOCK(state)) raise Error_1(RE_ALREADY_OPEN, path); //Trap_Security(flags[POL_READ], POL_READ, path); args = Find_Refines(call_, ALL_OPEN_REFS); if (args & AM_OPEN_NEW) goto create; //if (args & ~AM_OPEN_READ) raise Error_1(RE_INVALID_SPEC, path); Val_Init_Block(state, Make_Array(7)); Init_Dir_Path(&dir, path, 1, POL_READ); result = Read_Dir(&dir, VAL_SERIES(state)); ///OS_FREE(dir.file.path); if (result < 0) raise Error_On_Port(RE_CANNOT_OPEN, port, dir.error); break; case A_OPENQ: if (IS_BLOCK(state)) return R_TRUE; return R_FALSE; case A_CLOSE: SET_NONE(state); break; case A_QUERY: //Trap_Security(flags[POL_READ], POL_READ, path); SET_NONE(state); Init_Dir_Path(&dir, path, -1, REMOVE_TAIL_SLASH | POL_READ); if (OS_DO_DEVICE(&dir, RDC_QUERY) < 0) return R_NONE; Ret_Query_File(port, &dir, D_OUT); ///OS_FREE(dir.file.path); break; //-- Port Series Actions (only called if opened as a port) case A_LENGTH: len = IS_BLOCK(state) ? VAL_BLK_LEN(state) : 0; SET_INTEGER(D_OUT, len); break; default: raise Error_Illegal_Action(REB_PORT, action); } return R_OUT; }