diff --git a/src/Home/Net/Programs/Telnet/Extra/TelnetGr.ZC b/src/Home/Net/Programs/Telnet/Extra/TelnetGr.ZC new file mode 100755 index 00000000..c77ef30f --- /dev/null +++ b/src/Home/Net/Programs/Telnet/Extra/TelnetGr.ZC @@ -0,0 +1,909 @@ +// Telnet client for ZealOS by y4my4m +// Public Domain +Cd(__DIR__);; + +#define TELNET_PORT 23 +#define BUF_SIZE 8192 // way too big? +#define INPUT_BUF_SIZE 32 +#define TIMEOUT_DURATION 500000 + +#define NEGOTIATE 0xFF + +#define ANSI_ESC 0x1B +#define ANSI_CSI 0x5B // [ + +#define CHAR_HEIGHT 8 +#define CHAR_WIDTH 8 + +#define MAX_ANSI_PARAMS 32 + +#include "TelnetNegotiation" +#include "TelnetHelpers" + +Bool force_disconnect = FALSE; +Bool redraw_needed = FALSE; + +class ScreenCell { + U8 ch; + I64 color; + I64 bgcolor; +} +class Terminal { + I64 sock; + Bool sock_ready; + I64 window_width; + I64 window_height; + CDoc *doc; + CTask *task; + CDC *dc; + + ScreenCell screen[25][80]; + + I64 current_color; + I64 current_bgcolor; + I64 cursor_x; + I64 cursor_y; + + U8 buffer[BUF_SIZE]; + I64 buffer_len; +} term; + + +I64 TelnetOpen(U8 *host, U16 port) { + I64 socket; + + if (host == NULL) { + return -1; + } + + socket = TCPConnectionCreate(host, port); + "$$GREEN$$Conecting to %s:%d.$$FG$$$$BG$$\n", host, port; + if (socket <= 0) { + PrintErr("Failed to connect to %s:%d\n", host, port); + return socket; + } + + // sock(CTCPSocket *)->timeout = 0; + // sock(CTCPSocket *)->timeout = TCP_TIMEOUT; + return socket; +} + +U0 HandleControlCodes(U8 ch) { + if (ch < 32) { // ASCII code below 32 (control character) + switch (ch) { + case 0: // NUL (Null) - Typically ignored + break; + case 7: // BEL (Bell)d + Beep; + break; + case 8: // BS (Backspace) + term.cursor_x--; + break; + case 9: // HT (Horizontal Tab) + term.cursor_x += 8; + break; + case 10: // LF (Line Feed) + term.cursor_y++; + if (term.cursor_y >= term.window_height) { + // DocClear(term.doc); + + // DCFill(DCAlias,BLACK); + // term.cursor_x = 0; + term.cursor_y = 0; // reset Y position to 0 when it exceeds window height + } + // If the next character is CR, ignore it + // TODO: dont directly manipulate the buffer + // if (*(term.buffer + 1) == 13) term.buffer++; + break; + case 11: // VT (Vertical Tab) + SysLog("Vertical Tab\n"); + break; + case 12: // FF (Form Feed) + // DocClear(term.doc); + // DCFill(DCAlias,BLACK); + term.cursor_x = 0; + term.cursor_y = 0; + break; + case 13: // CR (Carriage Return) + term.cursor_x = 0; + // If the next character is LF, ignore it + // if (*(term.buffer + 1) == 10) term.buffer++; + break; + case 14: // SO (Shift Out) - Switch to an alternate character set + case 15: // SI (Shift In) - Switch back to the default character set + SysLog("Shift In/Out\n"); + break; + case 22: + SysLog("Synchronous Idle\n"); + break; + case 23: + SysLog("End of Transmission Block\n"); + break; + case 24: + SysLog("Cancel\n"); + break; + case 25: + SysLog("End of Medium\n"); + break; + case 26: + SysLog("Sub\n"); + break; + case 27: + SysLog("Esc\n"); + break; + case 28: + SysLog("Fs\n"); + break; + case 29: + SysLog("Gs\n"); + break; + case 30: + SysLog("Rs\n"); + break; + case 31: + SysLog("Unit Separator\n"); + break; + default: + // SysLog("CC %c happened\n", ch); + SysLog("CC happened\n"); + break; + } + } + else { + if (ch == 127) { + SysLog("case 127"); + } + if (ch == 0x24) { + // ch = "//$$$$"; + } + if (ch >= 32 && ch < 256) // ZealOS's ASCII is up to 255 + { + term.screen[term.cursor_y][term.cursor_x].ch = ch; + term.screen[term.cursor_y][term.cursor_x].color = term.current_color; + term.screen[term.cursor_y][term.cursor_x].bgcolor = term.current_bgcolor; + term.cursor_x++; + if (term.cursor_x >= term.window_width) { + term.cursor_x = 0; + term.cursor_y++; + if (term.cursor_y >= term.window_height) { + term.cursor_y = 0; + } + } + } + else { + // "%c", '?'; // unrecognized character + } + } +} + + +I64 LoadSplashScreen(U8 *filename) { + CFile *file = FOpen(filename, "rb"); + if (!file) { + PrintErr("Failed to open file"); + return -1; + } + + // Ensure that the file size isn't larger than the buffer + if (file->de.size > BUF_SIZE) { + PrintErr("File is too large for the buffer."); + FClose(file); + return -1; + } + + // Calculate the number of full blocks to read based on file size and block size + I64 full_blocks = file->de.size / BLK_SIZE; + I64 remaining_bytes = file->de.size % BLK_SIZE; + + SysLog("File size: %d, Number of full blocks: %d, Remaining bytes: %d\n", file->de.size, full_blocks, remaining_bytes); + + // Read the full blocks into the buffer + I64 i, blocks_read = 0; + for (i = 0; i < full_blocks; i++) { + blocks_read += FBlkRead(file, term.buffer + i * BLK_SIZE, i, 1); + } + + // Check if there are any remaining bytes in the last block + if (remaining_bytes != 0) { + // Read the remaining bytes + U8 temp_buffer[BLK_SIZE]; + if (FBlkRead(file, temp_buffer, full_blocks, 1)) { + blocks_read++; + MemCopy(term.buffer + full_blocks * BLK_SIZE, temp_buffer, remaining_bytes); + } + } + + FClose(file); + + if (blocks_read != (full_blocks + (remaining_bytes != 0))) { + PrintErr("Failed to read all the blocks"); + return -1; + } + + return file->de.size; // Return the number of bytes read +} + +U0 TerminalDrawIt(CTask *task, CDC *dc) +{ + // Clear the document + // DocClear(term.doc); + // Sleep(100); + DCFill; + + I64 row, col; + // Loop over the screen array and draw each character + for (row = 0; row < term.window_height; row++) { + for (col = 0; col < term.window_width; col++) { + // Get the character and color from the screen array + U8 ch = term.screen[row][col].ch; + + // Set the color + dc->color = term.screen[row][col].color; + dc->bkcolor = term.screen[row][col].bgcolor; + // term.dc->color = WHITE; + + // Draw the character + // GrPutChar(term.dc, col * CHAR_WIDTH, row * CHAR_HEIGHT, ch); + GrPutChar(dc, col * 8, row * 8, ch); + } + } + + // Draw the curso + // Note: this draws the cursor as a white rectangle. You may want to customize this. + // dc->color = WHITE; + // GrRect(term.dc, term.cursor_x * 8, term.cursor_y * 8, + // (term.cursor_x + 1) * 8 - 1, (term.cursor_y + 1) * 8 - 1); +} + +U0 ANSIParse() +{ + // Parse the buffer and draw the contents + U8 *ptr = term.buffer; + while (ptr < term.buffer + term.buffer_len) { + // Telnet negotiation sequence + if (*ptr == NEGOTIATE) { + // FIXME: i don't think the telnet negotiation is actually working properly? + TelnetNegotiate(term.sock, *ptr); + ptr += 3; + } + else if (*ptr == ANSI_ESC) { + // ANSI escape sequence + ptr++; + if (*ptr == ANSI_CSI) { + ptr++; + I64 ansi_code[MAX_ANSI_PARAMS], counter; + for (counter = 0; counter < MAX_ANSI_PARAMS; counter++) { + ansi_code[counter] = 0; // Initialize all elements to 0 + } + I64 ansi_param_count = 0; + while (IsDigit(*ptr) || *ptr == ';') { + if (IsDigit(*ptr)) { + ansi_code[ansi_param_count] = ansi_code[ansi_param_count] * 10 + (*ptr - '0'); + ptr++; + } + else if (*ptr == ';') { + ansi_param_count++; + if (ansi_param_count >= MAX_ANSI_PARAMS) { + // Error handling: too many parameters + break; + } + ptr++; + if(!IsDigit(*ptr) || *ptr == ';'){ + break; + } + } + } + + // Handle specific ANSI escape sequences + switch (*ptr) { + case 'n': + SysLog("Case n, %d\n",ansi_code[0]); + if (ansi_code[0] == 5) { + // Respond with terminal readiness + SysLog("reported terminal readiness\n"); + U8 deviceStatusResponse[5]; + deviceStatusResponse[0] = ANSI_ESC; + deviceStatusResponse[1] = ANSI_CSI; + deviceStatusResponse[2] = 0x30; // '0' + deviceStatusResponse[3] = 0x6E; // 'n' + deviceStatusResponse[4] = 0x00; // Null-terminator + TCPSocketSend(term.sock, deviceStatusResponse, 4); + // TCPSocketSendString(term.sock, "\x1B[0n"); + } + else if (ansi_code[0] == 6) { + // Respond with cursor position + // U8 response[32] = "\x1B[%d;%dR", window_width, term.window_height; + SysLog("reported cursor position\n"); + U8 cursorResponse[8]; + cursorResponse[0] = ANSI_ESC; + cursorResponse[1] = ANSI_CSI; + cursorResponse[2] = 0x32; + cursorResponse[3] = 0x35; + cursorResponse[4] = 0x3B; + cursorResponse[5] = 0x38; + cursorResponse[6] = 0x30; + cursorResponse[6] = 0x52; + cursorResponse[7] = 0x00; + TCPSocketSend(term.sock, cursorResponse, 7); + // TCPSocketSendString(term.sock, "\x1B\[25;80R"); + } + else if (ansi_code[0] == 255) { + // https://github.com/NuSkooler/enigma-bbs/blob/97cd0c3063b0c9f93a0fa4a44a85318ca81aef43/core/ansi_term.js#L140 + SysLog("reported screensize?\n"); + SendWindowSize(term.sock, 25, 80); + } + ptr++; + break; + case 'c': + // Respond with device attributes + SysLog("reported device attributes\n"); + // TCPSocketSendString(term.sock, "\x1B[?1;0c"); + // Reports at VT101 (not sure why though) + U8 deviceAttributesResponse[8]; + deviceAttributesResponse[0] = ANSI_ESC; + deviceAttributesResponse[1] = ANSI_CSI; + deviceAttributesResponse[2] = 0x3F; // '?' + deviceAttributesResponse[3] = 0x31; // '1' + deviceAttributesResponse[4] = 0x3B; // ';' + deviceAttributesResponse[5] = 0x32; // '0' + deviceAttributesResponse[6] = 0x63; // 'c' + deviceAttributesResponse[7] = 0x00; // Null-terminator + TCPSocketSend(term.sock, deviceAttributesResponse, 7); + ptr++; + break; + case 'm': + // Set graphics mode (colors) + I64 m; + Bool isBright = FALSE; + for (m = 0; m <= ansi_param_count; m++) { + if (ansi_code[m] <= 10) { + switch (ansi_code[m]) { + case 0: + term.current_bgcolor = BLACK; // should be BG FG for full reset + term.current_color = WHITE; // should be BG FG for full reset + isBright = FALSE; + break; // reset + case 1: isBright = TRUE; break; + case 2: isBright = FALSE; break; + } + } + else if ((ansi_code[m] >= 30 && ansi_code[m] <= 39) || (ansi_code[m] >= 90 && ansi_code[m] <= 97)) { + // Set foreground color + // SysLog("ansi_code[%d] = %d\n", m, ansi_code[m]); + if(!isBright){ + switch (ansi_code[m]) { + case 30: + term.current_color = BLACK; + break; + case 31: + term.current_color = RED; + break; + case 32: + term.current_color = GREEN; + break; + case 33: + term.current_color = YELLOW; + break; + case 34: + term.current_color = BLUE; + break; + case 35: + term.current_color = PURPLE; + break; + case 36: + term.current_color = CYAN; + break; + case 37: + term.current_color = WHITE; + break; + case 39: + term.current_color = WHITE; + break; + default: break; + } + } + else { + switch (ansi_code[m]) { + case 90: + case 30: + term.current_color = DKGRAY; + break; + case 91: + case 31: + term.current_color = LTRED; + break; + case 92: + case 32: + term.current_color = LTGREEN; + break; + case 93: + case 33: + term.current_color = YELLOW; + break; + case 94: + case 34: + term.current_color = LTBLUE; + break; + case 95: + case 35: + term.current_color = LTPURPLE; + break; + case 96: + case 36: + term.current_color = LTCYAN; + break; + case 97: + case 37: + term.current_color = LTGRAY; + break; + case 39: + term.current_color = WHITE; + break; + default: break; + } + } + } + // this is a dumb approach, just do a CatPrint or something + // until we properly catch the `;` it will stay buggy + else if ((ansi_code[m] >= 40 && ansi_code[m] <= 49) || (ansi_code[m] >= 100 && ansi_code[m] <= 107)) { + // Set background color + // SysLog("ansi_code[%d] = %d\n", m, ansi_code[m]); + if(!isBright){ + switch (ansi_code[m]) { + case 40: + term.current_bgcolor = BLACK; + break; + case 41: + term.current_bgcolor = RED; + break; + case 42: + term.current_bgcolor = GREEN; + break; + case 43: + term.current_bgcolor = YELLOW; + break; + case 44: + term.current_bgcolor = BLUE; + break; + case 45: + term.current_bgcolor = PURPLE; + break; + case 46: + term.current_bgcolor = CYAN; + break; + case 47: + term.current_bgcolor = WHITE; + break; + case 49: + // reset + term.current_bgcolor = BLACK; + break; + default: break; + } + } + else { + switch (ansi_code[m]) { + case 100: + case 40: + term.current_bgcolor = DKGRAY; + break; + case 101: + case 41: + term.current_bgcolor = LTRED; + break; + case 102: + case 42: + term.current_bgcolor = LTGREEN; + break; + case 103: + case 43: + term.current_bgcolor = YELLOW; + break; + case 104: + case 44: + term.current_bgcolor = LTBLUE; + break; + case 105: + case 45: + term.current_bgcolor = LTPURPLE; + break; + case 106: + case 46: + term.current_bgcolor = LTCYAN; + break; + case 107: + case 47: + term.current_bgcolor = LTGRAY; + break; + case 49: + // reset + term.current_bgcolor = BLACK; + default: break; + } + } + } + } + ptr++; + break; + case 'A': + // Cursor Up + SysLog("Cursor Up\n"); + term.cursor_y -= ansi_code[0]; + ptr++; + break; + case 'B': + // Cursor Down + SysLog("Cursor Down\n"); + term.cursor_y += ansi_code[0]; + ptr++; + break; + case 'C': + // Cursor Right + // SysLog("Cursor Right %d %d\n", ansi_param_count, ansi_code[0]); + term.cursor_x += ansi_code[0]; + // NOTE: this has been "fixed" since we now change the window's background color + // if we just move the cursor, + // you dont get the colored background since we skip over it directly + // I64 C; + // for (C = 0; C < ansi_code[0]; C++) { + // " "; + // } + ptr++; + break; + case 'D': + // Cursor Left + SysLog("Cursor Left\n"); + term.cursor_x -= ansi_code[0]; + ptr++; + break; + case 'E': + // Cursor Next Line + SysLog("Cursor Next Line\n"); + term.cursor_x = 0; + term.cursor_y++; + if (term.cursor_y >= term.window_height) { + // scroll + } + ptr++; + break; + case 'F': + // Cursor Previous Line + SysLog("Cursor Previous Line\n"); + term.cursor_x = 0; + term.cursor_y -= ansi_code[0]; + if (term.cursor_y < 0) { + term.cursor_y = 0; // prevent y from going below 0 + } + ptr++; + break; + case 'G': + // Cursor Horizontal Absolute + SysLog("Cursor Horizontal Absolute\n"); + term.cursor_x = ansi_code[0]; + ptr++; + break; + case 'H': + case 'f': + I64 row = 1, col = 1; // default values + // Parse the row number + if(ansi_code[0] != 1) + row = ansi_code[0]; + if(ansi_code[1] != 1) + col = ansi_code[1]; + + // TODO: This is a hack, dont skip row 0, col 0 (maybe?) + if (row == 0 && col == 0) { + ptr++; + break; + } + // SysLog("H or f AFTER row:%d, col:%d, cnt:%d\n", row, col, ansi_param_count); + + if (row > term.window_height) + row = term.window_height - 1; + if (col > term.window_width) + col = term.window_width - 1; + // "$$CM,0,0$$"; + term.cursor_x = col-1; + term.cursor_y = row-1; + ptr++; + break; + case 'J': + // SysLog("J code, %d %d\n", ansi_param_count, ansi_code[0]); + // Erase in Display + if (ansi_code[0] == 0) { + // Erase from cursor to end of display + // DocDelToNum(Fs->display_doc, Fs->display_doc->cur_entry->line_num); + } else if (ansi_code[0] == 1) { + // Erase from cursor to beginning of display + // DocDelToEntry(Fs->display_doc, Fs->display_doc->cur_entry, FALSE); + } else if (ansi_code[0] == 2) { + // Erase entire display + // DocClear(term.doc); + DCFill(term.dc,BLACK); + // term.cursor_x = 0; + // term.cursor_y = 0; + // redraw_needed = TRUE; + } + ptr++; + break; + case 'K': + // TODO: I have no idea if this actually works + SysLog("K code\n"); + // Erase in Line + // CDocEntry *cur_entry = Fs->display_doc->cur_entry; + // CDocEntry *next_entry = cur_entry->next; + + // // Delete the current entry + // if (!(cur_entry->de_flags & (DOCEF_HOLD | DOCEF_FILTER_SKIP))) { + // Fs->display_doc->cur_entry = next_entry; + // Fs->display_doc->cur_col = next_entry->min_col; + // DocEntryDel(Fs->display_doc, cur_entry); + // } + + // // Create a new entry (line) in its place + // CDocEntry *new_entry = DocEntryNewTag(Fs->display_doc, cur_entry, ""); + // DocInsEntry(Fs->display_doc, new_entry); + + ptr++; + break; + case 'L': + SysLog("L code\n"); + ptr++; + break; + case 'S': + // TODO: Scroll Up + SysLog("Scroll Up"); + ptr++; + break; + case 'T': + // TODO: Scroll Down + SysLog("Scroll Down"); + ptr++; + break; + case 'M': + SysLog("Case M\n"); + term.cursor_y--; + ptr++; + break; + case '?': + ptr++; + I64 code = 0; + + while (IsDigit(*ptr)) { + code = code * 10 + (*ptr - '0'); + ptr++; + } + switch (code) { + case 25: + if (*ptr == 'l') DocCursor(OFF); // Hide cursor + if (*ptr == 'h') DocCursor(ON); // Show cursor + ptr++; // Move past 'l' or 'h' + break; + case 47: + if (*ptr == 'l') SysLog("code 47l\n"); // restore screen + if (*ptr == 'h') SysLog("code 47h\n"); // save screen + ptr++; // Move past 'l' or 'h' + break; + case 1049: + if (*ptr == 'l') SysLog("code 1049l\n"); // enables the alternative buffer + if (*ptr == 'h') SysLog("code 1049h\n"); // disables the alternative buffer + ptr++; // Move past 'l' or 'h' + break; + default: + ptr++; + break; + } + break; + case 's': + SysLog("SaveCurrentCursorPosition\n"); + ptr++; + break; + case 'u': + SysLog("RestoreCurrentCursorPosition\n"); + ptr++; + break; + case 'r': + // self.restoreCursorPosition(); + SysLog("r case \n"); + ptr++; + break; + case 'h': + case 'l': + // TODO: Handle 'h' (set mode) or 'l' (reset mode) codes + SysLog("h or l case \n"); + ptr++; // Skip 'h' or 'l' + break; + case '=': + SysLog("ScreenMode attempt\n"); + ptr++; + break; + default: + if(!IsDigit(*ptr)) { + SysLog("Unknown code: %c\n", *ptr); + } + ptr++; + break; + } + } + } + else { + // Print the received character + HandleControlCodes(*ptr); + ptr++; + } + } + if (redraw_needed) { + // TerminalDrawIt(term.task, term.task); + // DocClear(term.doc); + + // DCFill(,RED); + // term.cursor_x = 0; + // term.cursor_y = 0; + redraw_needed = FALSE; + } +} + +U0 TerminalTask() { + while (!term.sock_ready) { + Sleep(100); // Avoid busy waiting + } + // This task receives data from the socket and fills the buffer + while (!force_disconnect) + { + term.buffer_len = TCPSocketReceive(term.sock, term.buffer, BUF_SIZE - 1); + if (term.buffer_len > 0) { + redraw_needed = TRUE; + term.buffer[term.buffer_len] = '\0'; + ANSIParse; + } else { + "Error: Connection closed by the remote host.\n"; + break; + } + } +} + +U0 Telnet(U8 *host, U16 port=TELNET_PORT) { + + term.sock_ready = 0; // Initialize the semaphore + term.sock = TelnetOpen(host, port); + term.window_width = 80; + term.window_height = 25; + if (term.sock <= 0) { + return; + } + term.sock_ready = 1; // Signal that the socket is ready + term.dc = DCNew(term.window_width, term.window_height); + term.doc = Fs->put_doc; + + // Spawn a task to receive data from the socket + term.task = Spawn(&TerminalTask, NULL, "Telnet"); + // term.task->win_inhibit = WIG_USER_TASK_DEFAULT; + // LBts(&term.task->display_flags, DISPLAYf_SHOW); + + Fs->draw_it = &TerminalDrawIt; + + StrCopy(Fs->task_title, "TELNET"); + Fs->border_src = BDS_CONST; + Fs->border_attr = LTGREEN << 4 + DriveTextAttrGet(':') & 15; + Fs->text_attr = BLACK << 4 + WHITE; + Fs->title_src = TTS_LOCKED_CONST; + + Fs->win_width = term.window_width; + WinHorz((TEXT_COLS / 2) - (Fs->win_width / 2), + (TEXT_COLS / 2) - (Fs->win_width / 2) + + (Fs->win_width - 1), + Fs); + Fs->win_height = term.window_height; + WinVert((TEXT_ROWS / 2) - (Fs->win_height / 2), + (TEXT_ROWS / 2) - (Fs->win_height / 2) + + (Fs->win_height - 1), + Fs); + DocClear; + + // SplashScreen + term.buffer_len = LoadSplashScreen("Art/TelnetSplash.ans"); + if (term.buffer_len > 0) { + term.buffer[term.buffer_len] = '\0'; + // parse the buffer + ANSIParse; + } + else { + "Error: Could not load splash screen.\n"; + } + + // PopUp if no host is specified + if (host == NULL) { + try + { + while (host == NULL) { + CHostForm form; + TelnetPrompt(&form); + host = form.host; + port = form.port; + DocClear; + break; + } + } + catch + PutExcept; + } + + "$$BG,GREEN$$$$WHITE$$Connected$$FG$$$$BG$$\n"; + Sleep(1000); + DocClear; + + I64 sc; + // https://theasciicode.com.ar/ascii-control-characters/escape-ascii-code-27.html + try + { + while (!force_disconnect) { + U8 key = KeyGet(&sc); + switch (key) + { + case 0: + switch (sc.u8[0]) + { + case SC_CURSOR_LEFT: + TCPSocketSendString(term.sock, "\x1B[D"); + break; + + case SC_CURSOR_RIGHT: + TCPSocketSendString(term.sock, "\x1B[C"); + break; + + case SC_CURSOR_UP: + TCPSocketSendString(term.sock, "\x1B[A"); + break; + + case SC_CURSOR_DOWN: + TCPSocketSendString(term.sock, "\x1B[B"); + break; + default: + break; + } + break; + case 9: + switch (sc.u8[0]) + { + case SC_TAB: + TCPSocketSendString(term.sock, "\x09"); + break; + default: + break; + } + case CH_BACKSPACE: + TCPSocketSendString(term.sock, "\x08"); + break; + case CH_ESC: + TCPSocketSendString(term.sock, "\x1B"); + break; + case CH_SHIFT_ESC: + force_disconnect = TRUE; + break; + // send buffer on enter + case '\n': + TCPSocketSendString(term.sock, "\r\n"); + + break; + default: + if (key >= ' ' && key <= '~') { + // Handle regular keys + U8 input_buf[2]; + input_buf[0] = key; + input_buf[1] = '\0'; + TCPSocketSend(term.sock, input_buf, 1); + } + break; + } + } + } + catch + PutExcept; + + Kill(term.task); + TCPSocketClose(term.sock); + "Telnet connection closed.\n"; +} + +// dev server +Telnet("localhost", 8888); \ No newline at end of file diff --git a/src/Home/Net/Programs/Telnet/Telnet.ZC b/src/Home/Net/Programs/Telnet/Telnet.ZC index ba9e27df..dce4fced 100755 --- a/src/Home/Net/Programs/Telnet/Telnet.ZC +++ b/src/Home/Net/Programs/Telnet/Telnet.ZC @@ -44,6 +44,18 @@ I64 TelnetOpen(U8 *host, U16 port) { return sock; } +U0 TermBottom() +{ + Bool unlock; + CDoc *doc = term.doc; + + unlock = DocLock(doc); + doc->cur_entry = doc; + doc->cur_col = 0; + if (unlock) + DocUnlock(doc); +} + U0 HandleControlCodes(U8 ch) { if (ch < 32) { // ASCII code below 32 (control character) switch (ch) { @@ -772,6 +784,7 @@ show_splash: term.buffer[term.buffer_len] = '\0'; // parse the buffer ANSIParse; + TermBottom; // Free(term.buffer); } else { @@ -962,11 +975,16 @@ init_connection: // dev server // Telnet("localhost", 8888); + +// ZealOS's official BBS zealos +// Telnet("bbs.zealos.net", 4001); + Telnet; /* TODO: +- make all keys send their respective signals (like Ctrl+Key, etc) - refactor ANSIParser's TCPSocketSends to send everything to a "TelnetSend" function - TelnetSend will handle whether there's a connection or not - This will make easier to transform this software into an ANSI viewer / Telnet client/ general purpose Term.