/* * C U 1 . C * * This file contains the core routines and the AST service * handlers for cux. * * In use, the program is functionally similar to Unix cu. The original design * of cux was based on the Decus library program TALK, written by * N. K. Bewtra, and modified by J. Yambor and Bob Turkelson. * * Besides providing adequate inter-system communication and file transfer * capabilities, the program amply illustrates the convenient run-time * interface to the RSX-11M Executive's services, in particular AST support. * * Kindly forward copies of any modifications, etc... to * * Bob Donovan * Graef-Anhalt-Schloemer & Associates * Milwaukee, WI 53216 * 414-461-6900 ex 225 */ #include #include "cu.h" int $$narg = 1; /* turn off csi */ /* * flags */ char exit_flag = OFF; /* exit main loop if true */ char pt_overflow = OFF; /* true if queue over flow */ char tt_overflow = OFF; /* true if queue over flow */ char suspend = OFF; /* true if host auto-suspeneded */ char echo_flag = ON; /* echo transmission */ char recv_file_open = OFF; /* on for redirected input */ char cmd_flag = OFF; /* on if building a command */ char host_off = FALSE; /* on if host manually suspended*/ int x_file_open = OFF; /* true if transfer documet open*/ int abort_x = OFF; /* on to abort transmission */ int cmd_sent = OFF; /* on if command enroute */ int port_data = OFF; /* on if port_queue receiving */ #ifndef GENERIC char auto_recv = OFF; /* on if auto receive from host */ char auto_x = OFF; /* on if auto transfer to host */ #endif int halted = OFF; /* to support xon-xoff protocol */ /* * system specific character set */ #ifdef UNIX char eofchar = 'D'-0100; /* EOT char on host system */ char wait_for_me_char = 012; /* host has record */ #endif #ifdef VAXVMS char eofchar = 'Z'-0100; /* Control-Z */ char wait_for_me_char = 012; /* host has record */ #endif #ifdef CYBERNET char wait_for_me_char = '?'; /* host requesting record */ #endif char xon = 'Q'-0100; /* start character */ char xoff= 'S'-0100; /* stop character */ char cmdchar = 'W'-0100; /* command flag */ /* * port data structures and parameters. The port buffer is * implemented as a circular queue. */ char pt_queue[QUEUE_SIZE]; /* character queue */ char *pt_char; /* address of port character */ char *pt_front = &pt_queue[0];/* front of queue */ char *pt_rear = &pt_queue[0];/* rear of queue */ char *pt_end = &pt_queue[QUEUE_SIZE - 1]; /* end of queue */ int pt_count = QUEUE_SIZE; /* queue free count */ int devpar[6]; /* for qio */ /* * tty data structures and parameters. The tty buffer is also * maintained as a circular queue. */ char tt_queue[TT_SIZE]; /* character queue */ char *tt_char; /* tty character */ char *tt_front = &tt_queue[0];/* front of queue */ char *tt_rear = &tt_queue[0];/* rear of queue */ char *tt_end = &tt_queue[TT_SIZE - 1]; /* end of queue */ int tt_count = TT_SIZE; /* queue free count */ int ttypar[6]; /* for tty qio's */ /* * file stuff and buffers */ char cmd_buf[80]; /* general purpose buffer */ char x_buff[BUFF_SIZE + 1]; /* transmit file buffer */ char buff[BUFF_SIZE + 1]; /* receive file buffer */ char *buff_ptr = &buff[0]; /* buff pointer */ char *buff_end = &buff[BUFF_SIZE]; /* end of buffer address */ FILE *rcvfd; /* receive file iov */ FILE *xfd; /* transmit file iov */ /* * diagnostics */ char *mesg1 = "\rcux: port queue overflow - character lost\n"; char *mesg2 = "\rcux: terminal queue overflow - character lost\n"; main () /* * Attach devices, initiate communication, * and cleanup. */ { /* * verbosity */ #ifdef UNIX printf("cux: UNIX version\n"); #endif #ifdef VAXVMS printf("cux: VAX/VMS version\n"); #endif #ifdef GENERIC #ifdef CYBERNET printf("cux: Cybernet NOS version\n"); #else printf("cux: Generic version\n"); #endif #endif attach_ports(); /* assign and attach tty and host ports */ printf("ready\n"); /* verbosity */ control(); /* main loop to manage conversation */ detach_ports(); /* detach devices from program */ printf("\rdisconnected\n"); } pt_enqueue () /* * AST service handler. Trap unsolicited input from the remote system * and enqueue it into the port's character buffer, respresented as a * circular queue. */ { astset(); /* required ast setup routine */ if (pt_count == 0) /* if port full signal overflow */ pt_overflow = ON; else { *pt_rear++ = (char) gtdp(0); /* get trapped input */ if (pt_rear > pt_end) /* wrap around pointer to rear */ pt_rear = pt_queue; pt_count--; /* reduce free count by one */ #ifndef CYBERNET /* * test if port queue is getting full. If so, * suspend host transmission to allow program * catch up. */ if (pt_count <= LOW_SIZE){ put_to_sleep(); /* transmit 'stop' character */ suspend = ON; /* set flag */ } #endif } port_data = ON; /* signal data received */ astx (1); /* required ast exit routine */ } tt_enqueue () /* * AST service handler. Take unsolicited input from tty and * enqueue it into terminal's character buffer, represented * as a circular queue. */ { astset(); /* ast setup routine */ if (tt_count == 0) /* signal queue is full */ tt_overflow = ON; else { *tt_rear++ = (char) gtdp(0); /* remove trapped character */ if (tt_rear > tt_end) tt_rear = tt_queue; /* wrap around */ tt_count--; /* reduce free count by one */ } astx (1); /* ast exit routine */ } pt_dequeue () /* * dequeue a character from the port attached to the remote system. */ { pt_char = pt_front++; /* get character address */ if (pt_front > pt_end) pt_front = pt_queue; /* wrap around pointer */ *pt_char = (*pt_char & ~AST_MASK); /* remove parity bit */ pt_count++; /* step queue free count */ port_data = OFF; /* signal port data taken */ } tt_dequeue () /* * dequeue a character from terminal queue. */ { tt_char = tt_front++; if (tt_front > tt_end) tt_front = tt_queue; /* wrap around pointer */ *tt_char = (*tt_char & ~AST_MASK); /* keep 7 bit character */ tt_count++; /* step queue free count */ } control() /* * Manage an interactive conversation between two systems. * control is an infinite loop that constantly monitors and * maintains the character buffers attached to the modem port * and tty. */ { for (;;){ /* * this loop constantly monitors the character queues * maintained by the ast service handlers */ if (pt_count != QUEUE_SIZE){ /* * process port character */ if (pt_overflow){ /* * report queue overflow and reset flag */ printf("%s", mesg1); pt_overflow = OFF; } else{ pt_dequeue(); pt_process(); } } while (tt_count != TT_SIZE){ /* * process terminal queue until empty */ if (tt_overflow){ /* * report queue overflow and reset flag */ printf("%s", mesg2); tt_overflow = OFF; } tt_dequeue(); tt_process(); if (exit_flag) /* exit loop */ goto end_loop; } /* * continue to process port queue while not empty. */ if (pt_count != QUEUE_SIZE) continue; #ifndef CYBERNET /* * if host transmission was temporarily suspended due * to low port free count, send xon character to resume * host transmission. */ if (suspend){ wake_up(); suspend = OFF; } else #endif if (x_file_open && !halted) /* * if a file is open for transmission to host, * and we haven't been stopped by the remote * system, send the next record. */ send_next_record(); } /* end loop */ /* * Perform some general cleanup before exiting. * Process any remaining characters from the port queue and * close any opened files. */ end_loop: if (suspend || host_off) wake_up(); while (pt_count != QUEUE_SIZE){ /* * process any remaining port characters */ pt_dequeue(); pt_process(); } /* * close any opened files */ if (recv_file_open) fclose(rcvfd); if (x_file_open) fclose(xfd); } wake_up() /* * send 'start' character to host to resume transmission */ { devpar[0] = &xon; devpar[1] = 1; qio(IO_WAL,LUN,3,NULL,NULL,devpar); } put_to_sleep() /* * send x-off character to suspend host transmission */ { devpar[0] = &xoff; devpar[1] = 1; qio(IO_WAL,LUN,3,NULL,NULL,devpar); } #ifdef GENERIC #ifndef CYBERNET #define PURELY_GENERIC 1 #endif #endif pt_process () /* * Process characters received from the remote system. */ { register char ch; if ((ch = *pt_char) == 0) /* ignore NULLs */ goto end_process; #ifdef PROTOCOL /* * the following recognizes xon-xoff protocol from host. */ if (ch == xon || ch == xoff){ halted ^= ON; /* toggle flag */ goto end_process; /* premature exit */ } #endif /* * echo character to tty */ if (echo_flag){ ttypar[0] = pt_char; /* address of char */ ttypar[1] = 1; qiow(IO_WAL, TTYLUN, 4, 0, 0, ttypar); } /* * test if a receive file is opened for redirected output * or 'take'. */ if (recv_file_open){ /* * ignore DEL, LF, and CR when dumping input to file. */ if (ch != 0177 && ch != '\n' && ch != '\r') *buff_ptr++ = ch; /* put to file buffer */ /* * write buffer when received or when buffer becomes * full. */ if (ch == '\r' || buff_ptr > buff_end) { *buff_ptr = '\0'; /* append null */ fputss (buff, rcvfd); /* write buffer */ buff_ptr = buff; /* reset buff pointer */ } #ifndef GENERIC /* * if auto-receive and port queue is empty, assume * eof. */ if (auto_recv && pt_count == QUEUE_SIZE && !suspend && !host_off) end_receive(); #endif } #ifndef PURELY_GENERIC /* * To prevent a locally initiated command from being * written (echoed) to the receive file, or to allow the host * time to * set up to receive a document, wait until the command has been * sent back before signaling to program that the receive or * transmit file is open. */ else if (cmd_sent && ch == wait_for_me_char){ cmd_sent = OFF; #ifdef CYBERNET x_file_open = ON; /* send next record to NOS */ #else if (auto_recv){ /* 'take' in effect */ recv_file_open = ON; /* signal file open */ while (!port_data); /* wait for port data */ } else if (auto_x){ /* 'put' in effect */ x_file_open = ON; /* signal local file open */ sleep(2); /* sleep a second or two */ } #endif } #endif end_process: ; } #ifdef HELP static char *cuhelp = "\r\n\n cux - communications utility\r\n\n\ . exit cux\r\n\ %take from [to] copy file 'from' (on the remote system) to\r\n\ file 'to' on the local system.\r\n\ %put from [to] copy file 'from' (on local system) to file 'to'\r\n\ on remote system.\r\n\ a abort document transmission\r\n\ >[>][:]file redirect host output to the named file.\r\n\ > terminate ouptut redirection.\r\n\ on */ if (host_off) wake_up(); /* send x-on to host */ else put_to_sleep(); /* send x-off */ host_off ^= ON; /* toggle flag */ } else if (ch == '<' && !x_file_open) dvt_from(); /* divert input */ else if (ch == '>' && (pt_count == QUEUE_SIZE || host_off)) divert_to(); /* redirect output */ #ifdef HELP else if (ch == 'h' && (pt_count == QUEUE_SIZE || host_off)) printf("%s", cuhelp); #endif else if (ch == 'a') abort_x = ON; #ifndef GENERIC else if (ch == '%' && pt_count == QUEUE_SIZE && !host_off) take_or_put(); #endif else if (ch == '!' && (pt_count == QUEUE_SIZE || host_off)) /* * escape to local shell */ escape(); else if (ch == cmdchar){ /* send command character */ devpar[0] = tt_char; devpar[1] = 1; qio(IO_WAL, LUN, 3, 0, 0, devpar); } } #ifndef GENERIC send_cmd () /* * transmit the given command string to the host * */ { printf("\r"); /* line feed to clear line */ devpar[0] = cmd_buf; /* character address */ devpar[1] = strlen(cmd_buf); /* string length */ qiow(IO_WAL,LUN,3,NULL,NULL,devpar); } #endif send_next_record() /* * Read the next record from the opened transmit file and * send it to the host. If eof, close file and reset flags. */ { register int recsiz; /* record size */ register char *s; #ifdef CYBERNET register char ch; #endif if (!abort_x && (s = fgets(x_buff, BUFF_SIZE, xfd)) != NULL){ /* * Transmit local record to host. */ recsiz = strlen(s); *(s + recsiz -1) = '\r'; /* map newline into */ #ifdef CYBERNET /* * The NOS cybernet system runs half duplex and has no * typeahead buffering. * After each record is sent, we set the 'cmd_sent' flag. * This forces us to wait for xedit's '?' prompt * before sending the next record. */ while (*s != '\0'){ /* * transmit a single character at a time. */ devpar[0] = s++; devpar[1] = 1; qiow(IO_WAL, LUN, 3, NULL, NULL, devpar); } ch = getc(xfd); /* test for EOF */ if (feof(xfd)) x_file_open = ON; /* catch EOF on next pass */ else{ ungetc(ch, xfd); /* return char to stream */ cmd_sent = ON; /* force wait for '?' prompt */ x_file_open = OFF; /* before sending next record*/ } #else devpar[0] = s; /* address of gotten line */ devpar[1] = recsiz; /* length */ qiow(IO_WAL, LUN, 3, NULL, NULL, devpar); #endif } else { /* eof or error */ /* * Either eof or 'abort' requested. * Close file and reset flags */ fclose (xfd); x_file_open = OFF; #ifdef CYBERNET if (!echo_flag) printf("\rcux: file transfer complete\n"); #endif echo_flag = ON; abort_x = OFF; #ifndef GENERIC /* * if auto transfer of local file to remote system, * transmit eof character to close remote document. */ if (auto_x) { devpar[0] = &eofchar; devpar[1] = 1; qio(IO_WAL, LUN, 3, 0, 0, devpar); auto_x = OFF; } #endif } } #ifndef GENERIC end_receive () /* * Terminate auto-receive diversion of host file. */ { if (recv_file_open) { fclose (rcvfd); /* close input file */ recv_file_open = OFF; /* reset flag */ auto_recv = OFF; /* end of auto-transfer */ echo_flag = ON; /* in case it was off */ /* * print any partially filled file buffer * to terminal. It should be the remote * host monitor's prompt. */ if (buff_ptr != buff) { buff_ptr = '\0'; ttypar[0] = buff; ttypar[1] = (buff_ptr - buff); qiow(IO_WAL,TTYLUN,4,NULL,NULL,ttypar); } } } #endif