colrconv-0.99.3/0040755000100100010010000000000007675060371011614 5ustar tpmtpmcolrconv-0.99.3/Makefile0100644000100100010010000000027106277334775013262 0ustar tpmtpmBIN_DIR = /usr/local/bin colrconv: colrconv.c gcc -Wall -I/usr/include/ncurses colrconv.c -lncurses -o colrconv install: colrconv install -m 0755 -o root -g bin colrconv $(BIN_DIR) colrconv-0.99.3/Makefile.sound0100644000100100010010000000030106277341374014374 0ustar tpmtpmBIN_DIR = /usr/local/bin colrconv: colrconv.c gcc -Wall -I/usr/include/ncurses colrconv.c -DAUDIO -lncurses -o colrconv install: colrconv install -m 0755 -o root -g bin colrconv $(BIN_DIR) colrconv-0.99.3/README.colrconv0100644000100100010010000000774506277341320014323 0ustar tpmtpm---------------------- begin original README.TTYLINK ---------------------- This is a little program to give a split screen session for ttylink connections. There is a 2 line window at the bottom of the screen for composing outgoing lines, and the rest of the screen is used to display incoming messages. I put this together to make "chatting" with users on NOS convers nodes a little more pleasant. This is public domain software. Dave Perry va3dp dp@hydra.carleton.ca ----------------------- end original README.TTYLINK ----------------------- Colrconv is a modified version of VA3DP's ttylink client (see README.TTYLINK). In addition to the basic split screen session it gives you color and sound support plus some line editing capabilities, a scroll buffer and a status line. Also the default port is changed to 3600 (convers). Here is a list of the special features of colrconv: - Color support. A new color is assigned to each user the first time he sends text and this color is used when displaying any subsequent text from this user. Available colors are green, yellow, cyan, magenta, blue and red. These are used twice, the second time with BOLD attribute on. After the colors run out they are used again. Bold red is reserved for special messages (starting with ***) and for private messages (eg. <*oh2bns*>: Hello!). - Sound support (only if compiled with -DAUDIO flag). Colrconv plays certain files when it receives text from the convers bridge. The files are: callsign.dsp (eg. oh2bns.dsp), unknown.dsp, signedon.dsp, says.dsp and pingpong.dsp. This feature is still a bit experimental and only works with older convers servers. Also you have to make your own dsp files (eg. using a microphone and typing cat < /dev/dsp > oh2bns.dsp) - Line editing. This is somewhat like in emacs: Ctrl-A Goto beginning of line Ctrl-B and Left arrow Go one character backward Ctrl-D Delete character under cursor Ctrl-E Goto end of line Ctrl-F and Right arrow Go one character forward Ctrl-K Kill from cursor to end of line (and store to kill buffer) Ctrl-R Reprint current line Ctrl-U Delete current line in total Ctrl-W Erase last word Ctrl-Y Yank kill buffer - Scroll buffer (default 1000 lines, can be changed when compiled). Scroll buffer can be browsed with arrow keys Up and Down (or Ctrl-P and Ctrl-N), PageUp, PageDown, Home and End keys. - 8 bit support. All legal 8 bit printable characters are displayed as is. Characters 128 - 159 ("illegal" in ISO-8859.1) are converted as if they were IBM codepage 437 characters (this makes some national characters look right even if they were originated from MESSY-DOS applications). Also all "control" characters (0 - 31) are shown in reverse except control-g which is only sounded, not printed. - Status line. Shows some more or less useful information about the current state of the program. - Ctrl-L repaints the whole screen. - By default, colrconv logs in on the convers server, using the user name as the callsign. Use the -n parameter to use some other callsign. - If ~/.conversrc exists, it is sent to the server after logging in. Handy for automatically sending commands like /notify, /who. - Command line arguments: colrconv [-nocolor] [-n [-c ]] [] -nocolor Disables color support even if terminal supports colors. -n Login name (if other than user name). -c Login channel (needs -n option). Hostname to connect to (domain name or ip address). Service to connect to (service name or port number, default 3600). Credits: Original code by VA3DP. Idea and code for color and sound support by Pekka, OH2BBP. This is public domain software. Tomi Manninen OH2BNS tomi.manninen@hut.fi Pekka Lempola OH2BBP pekka.lempola@vtt.fi Small hack by Heikki Hannikainen, OH7LZB colrconv-0.99.3/colrconv.c0100644000100100010010000004317207675061374013615 0ustar tpmtpm#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RXBUFFSIZE 1024 #define TXWINHEIGHT 3 #define RXWINHEIGHT (LINES-TXWINHEIGHT-1) #define SCROLLBUFFSIZE 1000 #define MAXCOLOR 6 #define IPPORT_CONVERS 3600 #define RESOLVING 1 #define ATTEMPTING 2 #define CONNECTED 3 #define CTLA 01 /* goto beginning of line */ #define CTLB 02 /* backward */ #define CTLD 04 /* delete char under cursor */ #define CTLE 05 /* goto end of line */ #define CTLF 06 /* forward */ #define CTLK 11 /* kill line */ #define CTLL 12 /* redraw screen */ #define CTLN 14 /* down */ #define CTLP 16 /* up */ #define CTLR 18 /* reprint current line */ #define CTLU 21 /* delete current line in total */ #define CTLW 23 /* erase last word */ #define CTLY 25 /* yank */ static int getcall(char *); #if AUDIO static void say(char *); #endif static int my_wdelch(WINDOW *); static int my_waddch(WINDOW *, chtype, int); static int my_wmove(WINDOW *, int); static void ins_char(char *, char *, char); static void del_char(char *, char *); static void status(int, const char *, int, int, int); static int my_wclear(WINDOW *); static char *Version = "ColrConv v0.99.3"; static int HasColors = 0; static int NoColor = 0; static char *Channel = NULL; static char *Name = NULL; /* mapping of IBM codepage 437 chars 128-159 to ISO latin1 equivalents * (158 and 159 are mapped to space) */ chtype ibm_map[32] = { 199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 236, 196, 197, 201, 230, 198, 244, 246, 242, 251, 249, 255, 214, 220, 162, 163, 165, 32, 32 }; /* Bugs: */ /* %%%dp don't overflow rxbuff */ /* %%%dp make error handling consistent */ int main(int argc, char *argv[]) { int c = -1; /* Temp char storage */ int recvretval; /* Return value from recv call */ char rxbuff[RXBUFFSIZE]; /* Receive buffer */ char *rxptr = rxbuff; /* Receive buffer pointer */ char *txptr, *txbuff; /* Transmit buffer and pointer */ struct servent *service; /* Used for named service lookup */ struct hostent *hp; /* Used by gethostbyname() */ struct hostent dummy; /* Used if ip no. is given instead of name */ struct timeval timeout; /* Timer structure for select() */ fd_set read_fdset; /* File descriptor set for select() */ struct sockaddr_in serv_addr; /* Required by connect() */ int sockfd; /* Socket for tcp connection */ WINDOW *rxwin,*txwin; /* RX and TX windows structures for ncurses */ int x, y; /* Window cursor position */ int offset = 0; /* Scrollback offset */ int nblines = 0; /* Number of lines in RX buffer */ int rxcolor = 0; /* Current color in RX window */ char *killbuff; /* Last killed line */ char *cp; /* Temp char pointer */ int nextarg; FILE *f; /* ~/.conversrc */ char ch; Name = getlogin(); nextarg = 1; while(argc > 1 && *argv[nextarg] == '-') { if(!strcmp(argv[nextarg], "-n")) { Name = argv[++nextarg]; argc--; } else if(!strcmp(argv[nextarg], "-c")) { Channel = argv[++nextarg]; argc--; } else if(!strcmp(argv[nextarg], "-nocolor")) { NoColor = 1; } argc--; nextarg++; } if(argc < 2) { fprintf(stderr,"Usage: colrconv [service]\n"); exit(1); } initscr(); /* Initialize ncurses data structures */ if(has_colors() == TRUE && !NoColor) { HasColors = 1; start_color(); init_pair(1, COLOR_GREEN, COLOR_BLACK); /* Color for 1st call etc. */ init_pair(2, COLOR_YELLOW, COLOR_BLACK); init_pair(3, COLOR_CYAN, COLOR_BLACK); init_pair(4, COLOR_MAGENTA, COLOR_BLACK); init_pair(5, COLOR_BLUE, COLOR_BLACK); init_pair(6, COLOR_RED, COLOR_BLACK); /* Last color used for alarms also */ init_pair(7, COLOR_WHITE, COLOR_BLUE); /* Color for status line */ init_pair(8, COLOR_WHITE, COLOR_GREEN); /* Color for TX window */ } if((killbuff = (char *) malloc(TXWINHEIGHT * COLS + 2)) == NULL){ endwin(); fprintf(stderr,"colrconv: malloc failed\n"); exit(1); } *killbuff = 0; if((txbuff = (char *) malloc(TXWINHEIGHT * COLS + 2)) == NULL){ endwin(); fprintf(stderr,"colrconv: malloc failed\n"); exit(1); } txptr = txbuff; refresh(); /* This will clear the screen at this point */ cbreak(); /* Char mode input */ noecho(); /* No automatic screen echo */ rxwin = newpad(SCROLLBUFFSIZE,COLS); /* receive window */ txwin = newwin(TXWINHEIGHT,COLS,LINES-TXWINHEIGHT-1,0); /* transmit window */ wattrset(rxwin,0); scrollok(rxwin,TRUE); idlok(rxwin,TRUE); /* immedok(rxwin,TRUE); */ wattrset(txwin,0); if(HasColors) wattron(txwin,A_BOLD | COLOR_PAIR(8)); else wattron(txwin,A_BOLD); my_wclear(txwin); wrefresh(txwin); /* Fill in network address structure with address of server we want to connect to (colrconv) */ bzero((char *) &serv_addr, sizeof(serv_addr)); /* first zero it out */ serv_addr.sin_family = AF_INET; if(isdigit(argv[nextarg][0])) { serv_addr.sin_addr.s_addr = inet_addr(argv[nextarg]); hp = &dummy; hp->h_name = ""; } else { status(RESOLVING,argv[nextarg],0,0,0); doupdate(); if ((hp = gethostbyname(argv[nextarg])) == NULL) { echo(); nocbreak(); endwin(); fprintf(stderr,"\ncolrconv: Unable to resolve name %s\n",argv[nextarg]); free(txbuff); free(killbuff); exit(1); } memcpy(&serv_addr.sin_addr.s_addr,hp->h_addr_list[0],4); } nextarg++; if(argc <= 2) serv_addr.sin_port = htons(IPPORT_CONVERS); else { if(isdigit(argv[nextarg][0])) serv_addr.sin_port = htons(atoi(argv[nextarg])); else { if((service = getservbyname(argv[nextarg],"tcp")) != NULL) serv_addr.sin_port = service->s_port; else { echo(); nocbreak(); endwin(); fprintf(stderr,"\ncolrconv: Unknown service %s\n",argv[nextarg]); free(txbuff); free(killbuff); exit(1); } } } status(ATTEMPTING,hp->h_name,ntohs(serv_addr.sin_port),0,0); doupdate(); /* Open a TCP socket */ if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { echo(); nocbreak(); endwin(); perror("colrconv: Can't open socket"); free(txbuff); free(killbuff); exit(1); } /* Attempt to connect */ if(connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))<0){ echo(); nocbreak(); endwin(); perror("colrconv: Can't connect"); free(txbuff); free(killbuff); exit(1); } status(CONNECTED,hp->h_name,ntohs(serv_addr.sin_port),0,0); doupdate(); if (Name != NULL) { sprintf(txbuff, "/n %s %s\n", Name, Channel != NULL ? Channel : ""); cp = txbuff; while(*cp) { my_waddch(rxwin,(*cp & 0xff) | A_BOLD | COLOR_PAIR(0),0); cp++; } send(sockfd,txbuff,strlen(txbuff),0); *txbuff = 0; } /* Read ~/.conversrc */ sprintf(txbuff, "%s/.conversrc", getenv("HOME")); if (!access(txbuff, R_OK)) { if ((f = fopen(txbuff, "r"))) { *txbuff = 0; for ( fgets(txbuff, TXWINHEIGHT * COLS, f); !feof(f); fgets(txbuff, TXWINHEIGHT * COLS, f) ) { cp = txbuff; while(*cp) { my_waddch(rxwin,(*cp & 0xff) | A_BOLD | COLOR_PAIR(0),0); cp++; } send(sockfd, txbuff, strlen(txbuff), 0); *txbuff = 0; } fclose(f); } } *txbuff = 0; /* Make receive on sockfd non-blocking */ if(fcntl(sockfd,F_SETFL,O_NONBLOCK) == -1) { echo(); nocbreak(); endwin(); perror("colrconv: fcntl error - sockfd"); free(txbuff); free(killbuff); exit(1); } keypad(stdscr,TRUE); /* Return single codes for function keys */ meta(stdscr,TRUE); /* force 8-bit input */ for(;;) { /* Now initialize file descriptor sets for select() */ FD_ZERO(&read_fdset); FD_SET(sockfd,&read_fdset); FD_SET(STDIN_FILENO,&read_fdset); timeout.tv_sec = 1; /* paranoid timer */ timeout.tv_usec = 0; /* Wake me if something happens */ if(select(sockfd+1,&read_fdset,0,0,&timeout) == -1) { if(errno != EINTR) { echo(); nocbreak(); endwin(); perror("colrconv: Select error"); close(sockfd); free(txbuff); free(killbuff); exit(1); } } /* Check for receive data */ while((recvretval = recv(sockfd,&ch,1,0)) != -1){ c = ch; /* one byte at a time so we can strip chars */ if(recvretval == 0) { /* Socket disconnected */ echo(); nocbreak(); endwin(); fprintf(stderr,"\ncolrconv: Connection closed by foreign host\n"); close(sockfd); free(txbuff); free(killbuff); exit(0); } switch(c & 0xff){ case '\r': /* Don't send these to ncurses */ break; case '\n': /* Complete line, display it */ rxptr = rxbuff; if(!strncmp(rxptr,"*** ", 4) || !strncmp(rxptr,"<*", 2)) { if(HasColors) { rxcolor = MAXCOLOR; wattron(rxwin,A_BOLD); } #if AUDIO /* "*** oh2bbp signed on" -message */ if(!strncmp(rxptr,"*** ", 4) && ((cp = strstr(rxptr, " signed on")) != NULL)) { *cp = 0; /* cut the line for a while */ say(&rxptr[4]); say("signedon"); *cp = ' '; } #endif } else if(*rxptr == '<' && HasColors) { rxcolor = getcall(rxptr); if(rxcolor > MAXCOLOR) { rxcolor -= MAXCOLOR; wattron(rxwin,A_BOLD); } else wattroff(rxwin,A_BOLD); } else if(strncmp(rxptr," ", 3)) { rxcolor = 0; wattroff(rxwin,A_BOLD); } while(*rxptr) { my_waddch(rxwin,(*rxptr & 0xff) | COLOR_PAIR(rxcolor),0); rxptr++; } waddch(rxwin,'\n'); rxptr = rxbuff; *rxptr = 0; /* wrefresh(rxwin); This looks nicer on slow terminals (not for pads...) */ break; case '\007': /* Sound it, don't print it */ #if AUDIO say("pingpong"); #else beep(); #endif break; default: /* Save in rxbuff */ c &= 0xff; *rxptr++ = (c > 127 && c < 160) ? ibm_map[c-128] : c; *rxptr = 0; break; } /* switch */ } if(errno != EAGAIN) { echo(); nocbreak(); close(sockfd); endwin(); perror("\ncolrconv"); free(txbuff); free(killbuff); exit(1); } nodelay(stdscr,TRUE); /* Make getch non-blocking */ while((c = getch()) != -1) { switch(c) { case CTLL: wrefresh(curscr); break; case CTLA: while(txptr > txbuff) { txptr--; my_wmove(txwin,-1); } break; case CTLE: while(*txptr != 0) { txptr++; my_wmove(txwin,1); } break; case CTLB: case KEY_LEFT: if(txptr > txbuff) { txptr--; my_wmove(txwin,-1); } break; case CTLF: case KEY_RIGHT: if(*txptr != 0) { txptr++; my_wmove(txwin,1); } break; case CTLP: case KEY_UP: getyx(rxwin,y,x); if(y-offset-(RXWINHEIGHT-1) > 0) offset++; else beep(); break; case CTLN: case KEY_DOWN: if(offset > 0) offset--; else beep(); break; case KEY_PPAGE: offset += RXWINHEIGHT-1; offset = (y-offset-(RXWINHEIGHT-1) < 0) ? y-(RXWINHEIGHT-1) : offset; break; case KEY_NPAGE: offset -= RXWINHEIGHT-1; offset = (offset < 0) ? 0 : offset; break; case KEY_HOME: getyx(rxwin,y,x); offset = y-(RXWINHEIGHT-1); break; case KEY_END: offset = 0; break; case CTLR: my_wclear(txwin); txptr = txbuff; while(*txptr) { my_waddch(txwin,(*txptr & 0xff),0); txptr++; } break; case CTLW: while(txptr != txbuff && *(txptr-1) != ' ') { txptr--; del_char(txbuff,txptr); my_wdelch(txwin); } while(txptr != txbuff && *(txptr-1) == ' ') { txptr--; del_char(txbuff,txptr); my_wdelch(txwin); } break; case CTLU: txptr = txbuff; *txptr = 0; my_wclear(txwin); break; case CTLK: strcpy(killbuff,txptr); while(*txptr != 0) { del_char(txbuff,txptr); my_wmove(txwin,1); my_wdelch(txwin); } break; case CTLY: cp = killbuff; while(*cp != 0 && strlen(txbuff) < (TXWINHEIGHT * COLS - 1)) { my_waddch(txwin,(*cp & 0xff),1); ins_char(txbuff,txptr,(*cp & 0xff)); txptr++; cp++; } break; case '\n': /* New line */ /* strcpy(lastline,txbuff); */ while(*txptr != 0) txptr++; ins_char(txbuff,txptr,(c & 0xff)); cp = txbuff; while(*cp) { my_waddch(rxwin,(*cp & 0xff) | A_BOLD | COLOR_PAIR(0),0); cp++; } send(sockfd,txbuff,strlen(txbuff),0); my_wclear(txwin); txptr = txbuff; *txptr = 0; nblines++; nblines = (nblines > SCROLLBUFFSIZE) ? SCROLLBUFFSIZE : nblines; break; case CTLD: if(*txptr != 0) { my_wmove(txwin,1); my_wdelch(txwin); del_char(txbuff,txptr); } break; case '\177': case KEY_BACKSPACE: if(txptr != txbuff) { txptr--; /* delete it from the buffer */ my_wdelch(txwin); del_char(txbuff,txptr); } break; default: if(!(c & 0xff00)){ /* ignore fn keys */ if(strlen(txbuff) < (TXWINHEIGHT * COLS - 1)) { if(*txptr != 0) my_waddch(txwin,(c & 0xff),1); else my_waddch(txwin,(c & 0xff),0); ins_char(txbuff,txptr,(c & 0xff)); txptr++; } else beep(); } break; } } nodelay(stdscr,FALSE); /* Make getch blocking */ getyx(rxwin,y,x); if(pnoutrefresh(rxwin,y-offset-(RXWINHEIGHT-1),0, 1,0,RXWINHEIGHT-1,COLS-1) == ERR) printf("***prefresh failed***\n"); status(CONNECTED,hp->h_name,ntohs(serv_addr.sin_port),offset,y); if(wnoutrefresh(txwin) == ERR) printf("***wrefresh failed***\n"); doupdate(); } } #if AUDIO static void say(char *str) { FILE *fpin, *fpout; char infile[40]; register int c; strncpy(infile, str, 35); strcat(infile, ".dsp"); if((fpin = fopen(infile, "r")) == NULL) fpin = fopen("unknown.dsp", "r"); if(fpin != NULL) { if((fpout = fopen("/dev/dsp", "w")) != NULL) { while((c = fgetc(fpin)) != EOF) fputc(c, fpout); fclose(fpout); } fclose(fpin); } } #endif static int getcall(char *rxptr) { static int nextcall = 0; static char callsign[100][30]; /* list of known callsigns */ int i; char *endofcall; if((endofcall = strchr(rxptr, '>')) == NULL) return 0; *endofcall = 0; /* cut the line for a while */ for(i=0; i MAXCOLOR means bold color */ /* Last bold color (bright red) reserved for alarm messages */ } static int my_wdelch(WINDOW *win) { chtype c; int x,y,savex,savey; my_wmove(win,-1); wdelch(win); getyx(win,y,x); savex = x; savey = y; while(y < win->_maxy) { y++; wmove(win,y,0); c = winch(win); wdelch(win); y--; wmove(win,y,win->_maxx); winsch(win,c); y++; } wmove(win,savey,savex); return OK; } static int my_waddch(WINDOW *win, chtype c, int ins) { chtype ch; int x,y,savex,savey; if((c & 0xff) != '\n') c = ((c & 0xff) < 32) ? ((c + 64) | A_REVERSE) : c; if(!ins) return waddch(win,c); else { getyx(win,y,x); savey = y; savex = x; y = win->_maxy; while(y > savey) { y--; wmove(win,y,win->_maxx); ch = winch(win); wdelch(win); y++; wmove(win,y,0); winsch(win,ch); y--; } wmove(win,savey,savex); winsch(win,c); my_wmove(win,1); } return OK; } static int my_wmove(WINDOW *win, int dir) { int x,y; getyx(win,y,x); if(dir > 0) { /* forward */ if(++x > (COLS-1)) { x = 0; y++; } } else { /* bacward */ if(--x < 0) { x = COLS-1; y--; } } return wmove(win,y,x); } static void ins_char(char *buf, char *ptr, char c) { char *cp; cp = buf+strlen(buf); while(cp >= ptr) { *(cp+1) = *cp; cp--; } *ptr = c; } static void del_char(char *buf, char *ptr) { char *cp; cp = ptr; while((*cp = *(cp+1)) != 0) cp++; } static void status(int stat, const char *host, int port, int offset, int lines) { char *cp,pos[10],line[80]; static WINDOW *win = NULL; struct tm *t; time_t tim; int x,y; if(!win) { win = newwin(1,COLS,0,0); wattrset(win,0); if(HasColors) wattron(win,A_BOLD | COLOR_PAIR(7)); else wattron(win,A_REVERSE); } switch(stat) { case RESOLVING: cp = "Resolving"; break; case ATTEMPTING: cp = "Connecting to"; break; case CONNECTED: cp = "Connected to"; break; } if(lines < RXWINHEIGHT) strcpy(pos,"All"); else { if(offset == 0) strcpy(pos,"Bot"); else { if(lines-offset-(RXWINHEIGHT-1) < 1) strcpy(pos,"Top"); else sprintf(pos,"%d",lines-offset); } } time(&tim); t = localtime(&tim); sprintf(line,"%s --- %s/%d --- %02d:%02d --- %s", Version,pos,lines,t->tm_hour,t->tm_min,cp); cp = strdup(host); sprintf(pos,"%d",port); x = COLS - strlen(line) - strlen(pos) - 4; if(x < strlen(host)) { cp[x] = 0; cp[--x] = '.'; cp[--x] = '.'; } wmove(win,0,0); wprintw(win,"%s %s:%s ",line,cp,pos); getyx(win,y,x); while(x++ <= win->_maxx) waddch(win,'-'); wnoutrefresh(win); } static int my_wclear(WINDOW *win) { int i; wmove(win,0,0); i = (win->_maxx+1)*(win->_maxy+1); while(i-- >= 0) waddch(win,' '); wmove(win,0,0); #if 0 wbkgd(win,' ' | COLOR_PAIR(8)); #endif return OK; }