File:  [Synchronet] / sbbs / sbbs2 / con_hi.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 16:38:17 2018 UTC (8 years, 3 months ago) by root
Branches: digitaldynamics, MAIN
CVS tags: v3_00c, HEAD
3.00c Win32

#line 1 "CON_HI.C"

/* Developed 1990-1997 by Rob Swindell; PO Box 501, Yorba Linda, CA 92885 */

#include "sbbs.h"

extern char question[128];

char *mnestr;

/****************************************************************************/
/* Waits for remote or local user to input a CR terminated string. 'length' */
/* is the maximum number of characters that getstr will allow the user to   */
/* input into the string. 'mode' specifies upper case characters are echoed */
/* or wordwrap or if in message input (^A sequences allowed). ^W backspaces */
/* a word, ^X backspaces a line, ^Gs, BSs, TABs are processed, LFs ignored. */
/* ^N non-destructive BS, ^V center line. Valid keys are echoed.            */
/****************************************************************************/
int getstr(char *strout, int maxlen, long mode)
{
    int i,l,x,z;    /* i=current position, l=length, j=printed chars */
                    /* x&z=misc */
    uchar ch,str1[256],str2[256],ins=0,atr;

console&=~CON_UPARROW;
sys_status&=~SS_ABORT;
if(mode&K_LINE && useron.misc&ANSI && !(mode&K_NOECHO)) {
    attr(color[clr_inputline]);
    for(i=0;i<maxlen;i++)
        outchar(SP);
    bprintf("\x1b[%dD",maxlen); }
if(wordwrap[0]) {
    strcpy(str1,wordwrap);
    wordwrap[0]=0; }
else str1[0]=0;
if(mode&K_EDIT)
    strcat(str1,strout);
if(strlen(str1)>maxlen)
    str1[maxlen]=0;
atr=curatr;
if(!(mode&K_NOECHO)) {
	if(mode&K_AUTODEL && str1[0]) {
		i=(color[clr_inputline]&0x77)<<4;
		i|=(color[clr_inputline]&0x77)>>4;
		attr(i); }
	rputs(str1);
	if(mode&K_EDIT && !(mode&(K_LINE|K_AUTODEL)) && useron.misc&ANSI)
		bputs("\x1b[K");  /* destroy to eol */ }

i=l=strlen(str1);
if(mode&K_AUTODEL && str1[0] && !(mode&K_NOECHO)) {
	ch=getkey(mode|K_GETSTR);
    attr(atr);
    if(isprint(ch) || ch==0x7f) {
        for(i=0;i<l;i++)
            bputs("\b \b");
        i=l=0; }
    else {
        for(i=0;i<l;i++)
            outchar(BS);
        rputs(str1);
        i=l; }
	if(ch!=SP && ch!=TAB)
		ungetkey(ch); }

while(!(sys_status&SS_ABORT) && (ch=getkey(mode|K_GETSTR))!=CR && online) {
	if(sys_status&SS_ABORT)
		break;
	if(ch==LF) /* Ctrl-J same as CR */
		break;
	if(ch==TAB && !(mode&K_WRAP))	/* TAB same as CR */
		break;
	if(!i && mode&K_UPRLWR && (ch==SP || ch==TAB))
		continue;	/* ignore beginning white space if upper/lower */
    if(mode&K_E71DETECT && ch==(CR|0x80) && l>1) {
        if(strstr(str1,"��")) {
            bputs("\r\n\r\nYou must set your terminal to NO PARITY, "
                "8 DATA BITS, and 1 STOP BIT (N-8-1).\7\r\n");
            return(0); } }
    switch(ch) {
        case 1: /* Ctrl-A for ANSI */
            if(!(mode&K_MSG) || useron.rest&FLAG('A') || i>maxlen-3)
                break;
            if(ins) {
                if(l<maxlen)
                    l++;
                for(x=l;x>i;x--)
                    str1[x]=str1[x-1];
                rprintf("%.*s",l-i,str1+i);
                rprintf("\x1b[%dD",l-i);
                if(i==maxlen-1)
                    ins=0; }
            outchar(str1[i++]=1);
            break;
        case 2: /* Ctrl-B Beginning of Line */
			if(useron.misc&ANSI && i && !(mode&K_NOECHO)) {
                bprintf("\x1b[%dD",i);
                i=0; }
            break;
        case 4: /* Ctrl-D Delete word right */
            if(i<l) {
                x=i;
                while(x<l && str1[x]!=SP) {
                    outchar(SP);
                    x++; }
                while(x<l && str1[x]==SP) {
                    outchar(SP);
                    x++; }
                bprintf("\x1b[%dD",x-i);   /* move cursor back */
                z=i;
                while(z<l-(x-i))  {             /* move chars in string */
                    outchar(str1[z]=str1[z+(x-i)]);
                    z++; }
                while(z<l) {                    /* write over extra chars */
                    outchar(SP);
                    z++; }
                bprintf("\x1b[%dD",z-i);
                l-=x-i; }                       /* l=new length */
            break;
        case 5: /* Ctrl-E End of line */
			if(useron.misc&ANSI && i<l) {
                bprintf("\x1b[%dC",l-i);  /* move cursor to eol */
                i=l; }
            break;
        case 6: /* Ctrl-F move cursor forewards */
            if(i<l && (useron.misc&ANSI)) {
                bputs("\x1b[C");   /* move cursor right one */
                i++; }
            break;
        case 7:
            if(!(mode&K_MSG))
                break;
            if(useron.rest&FLAG('B')) {
                if (i+6<maxlen) {
                    if(ins) {
                        for(x=l+6;x>i;x--)
                            str1[x]=str1[x-6];
                        if(l+5<maxlen)
                            l+=6;
                        if(i==maxlen-1)
                            ins=0; }
                    str1[i++]='(';
                    str1[i++]='b';
                    str1[i++]='e';
                    str1[i++]='e';
                    str1[i++]='p';
                    str1[i++]=')';
					if(!(mode&K_NOECHO))
						bputs("(beep)"); }
                if(ins)
                    redrwstr(str1,i,l,0);
                break; }
             if(ins) {
                if(l<maxlen)
                    l++;
                for(x=l;x>i;x--)
                    str1[x]=str1[x-1];
                if(i==maxlen-1)
                    ins=0; }
             if(i<maxlen) {
                str1[i++]=7;
				if(!(mode&K_NOECHO))
					outchar(7); }
             break;
        case 14:    /* Ctrl-N Next word */
            if(i<l && (useron.misc&ANSI)) {
                x=i;
                while(str1[i]!=SP && i<l)
                    i++;
                while(str1[i]==SP && i<l)
                    i++;
                bprintf("\x1b[%dC",i-x); }
            break;
        case 0x1c:    /* Ctrl-\ Previous word */
			if(i && (useron.misc&ANSI) && !(mode&K_NOECHO)) {
                x=i;
                while(str1[i-1]==SP && i)
                    i--;
                while(str1[i-1]!=SP && i)
                    i--;
                bprintf("\x1b[%dD",x-i); }
            break;
        case 18:    /* Ctrl-R Redraw Line */
			if(!(mode&K_NOECHO))
				redrwstr(str1,i,l,0);
            break;
        case TAB:
            if(!(i%TABSIZE)) {
                if(ins) {
                    if(l<maxlen)
                        l++;
                    for(x=l;x>i;x--)
                        str1[x]=str1[x-1];
                    if(i==maxlen-1)
                        ins=0; }
                str1[i++]=SP;
				if(!(mode&K_NOECHO))
					outchar(SP); }
            while(i<maxlen && i%TABSIZE) {
                if(ins) {
                    if(l<maxlen)
                        l++;
                    for(x=l;x>i;x--)
                        str1[x]=str1[x-1];
                    if(i==maxlen-1)
                        ins=0; }
                str1[i++]=SP;
				if(!(mode&K_NOECHO))
					outchar(SP); }
			if(ins && !(mode&K_NOECHO))
                redrwstr(str1,i,l,0);
            break;
        case BS:
            if(!i)
                break;
            i--;
            l--;
            if(i!=l) {              /* Deleting char in middle of line */
                outchar(BS);
                z=i;
                while(z<l)  {       /* move the characters in the line */
                    outchar(str1[z]=str1[z+1]);
                    z++; }
                outchar(SP);        /* write over the last char */
                bprintf("\x1b[%dD",(l-i)+1); }
			else if(!(mode&K_NOECHO))
                bputs("\b \b");
            break;
        case 22:    /* Ctrl-V   Center line */
            str1[l]=0;
            l=bstrlen(str1);
            if(!l) break;
            for(x=0;x<(maxlen-l)/2;x++)
                str2[x]=SP;
            str2[x]=0;
            strcat(str2,str1);
            strcpy(strout,str2);
            l=strlen(strout);
			if(mode&K_NOECHO)
				return(l);
            if(mode&K_MSG)
                redrwstr(strout,i,l,K_MSG);
            else {
                while(i--)
                    bputs("\b");
                bputs(strout);
                if(mode&K_LINE)
                    attr(LIGHTGRAY); }
            CRLF;
            return(l);
        case 23:    /* Ctrl-W   Delete word left */
            if(i<l) {
                x=i;                            /* x=original offset */
                while(i && str1[i-1]==SP) {
                    outchar(BS);
                    i--; }
                while(i && str1[i-1]!=SP) {
                    outchar(BS);
                    i--; }
                z=i;                            /* i=z=new offset */
                while(z<l-(x-i))  {             /* move chars in string */
                    outchar(str1[z]=str1[z+(x-i)]);
                    z++; }
                while(z<l) {                    /* write over extra chars */
                    outchar(SP);
                    z++; }
                bprintf("\x1b[%dD",z-i);        /* back to new x corridnant */
                l-=x-i; }                       /* l=new length */
            else {
                while(i && str1[i-1]==SP) {
                    i--;
                    l--;
					if(!(mode&K_NOECHO))
						bputs("\b \b"); }
                while(i && str1[i-1]!=SP) {
                    i--;
                    l--;
					if(!(mode&K_NOECHO))
						bputs("\b \b"); } }
            break;
        case 24:    /* Ctrl-X   Delete entire line */
			if(mode&K_NOECHO)
				l=0;
			else {
				while(i<l) {
					outchar(SP);
					i++; }
				while(l) {
					l--;
					bputs("\b \b"); } }
            i=0;
            break;
        case 25:    /* Ctrl-Y   Delete to end of line */
			if(useron.misc&ANSI && !(mode&K_NOECHO)) {
                bputs("\x1b[K");
                l=i; }
            break;
        case 31:    /* Ctrl-Minus       Toggles Insert/Overwrite */
			if(!(useron.misc&ANSI) || mode&K_NOECHO)
                break;
            if(ins) {
                ins=0;
                redrwstr(str1,i,l,0); }
            else if(i<l) {
                ins=1;
				bprintf("\x1b[s\x1b[%dC",79-lclwx());    /* save pos  */
                z=curatr;                                /* and got to EOL */
                attr(z|BLINK|HIGH);
                outchar('�');
                attr(z);
                bputs("\x1b[u"); }  /* restore pos */
            break;
        case 0x1e:  /* Ctrl-^ */
            if(!(mode&K_EDIT))
                break;
            if(i>l)
                l=i;
            str1[l]=0;
            strcpy(strout,str1);
			if((stripattr(strout) || ins) && !(mode&K_NOECHO))
                redrwstr(strout,i,l,K_MSG);
			if(mode&K_LINE && !(mode&K_NOECHO))
				attr(LIGHTGRAY);
            console|=CON_UPARROW;
            return(l);
        case 0x1d:  /* Ctrl-]  Reverse Cursor Movement */
			if(i && (useron.misc&ANSI) && !(mode&K_NOECHO)) {
                bputs("\x1b[D");   /* move cursor left one */
                i--; }
            break;
        case 0x7f:  /* Ctrl-BkSpc (DEL) Delete current char */
            if(i==l)
                break;
            l--;
            z=i;
            while(z<l)  {       /* move the characters in the line */
                outchar(str1[z]=str1[z+1]);
                z++; }
            outchar(SP);        /* write over the last char */
            bprintf("\x1b[%dD",(l-i)+1);
            break;
        default:
            if(mode&K_WRAP && i==maxlen && ch>=SP && !ins) {
                str1[i]=0;
				if(ch==SP && !(mode&K_CHAT)) { /* don't wrap a space */ 
					strcpy(strout,str1);	   /* as last char */
					if(stripattr(strout) && !(mode&K_NOECHO))
                        redrwstr(strout,i,l,K_MSG);
					if(!(mode&K_NOECHO))
						CRLF;
                    return(i); }
                x=i-1;
                z=1;
                wordwrap[0]=ch;
                while(str1[x]!=SP && x)
                    wordwrap[z++]=str1[x--];
                if(x<(maxlen/2)) {
                    wordwrap[1]=0;  /* only wrap one character */
                    strcpy(strout,str1);
					if(stripattr(strout) && !(mode&K_NOECHO))
                        redrwstr(strout,i,l,K_MSG);
					if(!(mode&K_NOECHO))
						CRLF;
                    return(i); }
                wordwrap[z]=0;
				if(!(mode&K_NOECHO))
					while(z--) {
						bputs("\b \b");
						i--; }
                strrev(wordwrap);
                str1[x]=0;
                strcpy(strout,str1);
				if(stripattr(strout) && !(mode&K_NOECHO))
					redrwstr(strout,i,x,mode);
				if(!(mode&K_NOECHO))
					CRLF;
                return(x); }
            if(i<maxlen && ch>=SP) {
                if(mode&K_UPRLWR)
                    if(!i || (i && (str1[i-1]==SP || str1[i-1]=='-'
                        || str1[i-1]=='.' || str1[i-1]=='_')))
                        ch=toupper(ch);
                    else
                        ch=tolower(ch);
                if(ins) {
                    if(l<maxlen)    /* l<maxlen */
                        l++;
                    for(x=l;x>i;x--)
                        str1[x]=str1[x-1];
					rprintf("%.*s",l-i,str1+i);
					rprintf("\x1b[%dD",l-i);
					if(i==maxlen-1) {
						bputs("  \b\b");
						ins=0; } }
                str1[i++]=ch;
				if(!(mode&K_NOECHO))
					outchar(ch); } }
    if(i>l)
        l=i;
    if(mode&K_CHAT && !l)
        return(0); }
if(!online)
    return(0);
if(i>l)
    l=i;
str1[l]=0;
if(!(sys_status&SS_ABORT)) {
    strcpy(strout,str1);
	if((stripattr(strout) || ins) && !(mode&K_NOECHO))
        redrwstr(strout,i,l,K_MSG); }
else
    l=0;
if(mode&K_LINE && !(mode&K_NOECHO)) attr(LIGHTGRAY);
if(!(mode&(K_NOCRLF|K_NOECHO))) {
    outchar(CR);
    if(!(mode&K_MSG && sys_status&SS_ABORT))
        outchar(LF);
    lncntr=0; }
return(l);
}

/****************************************************************************/
/* Hot keyed number input routine.                                          */
/* Returns a valid number between 1 and max, 0 if no number entered, or -1  */
/* if the user hit 'Q' or ctrl-c                                            */
/****************************************************************************/
long getnum(ulong max)
{
    uchar ch,n=0;
	long i=0;

while(online) {
	ch=getkey(K_UPPER);
    if(ch>0x7f)
        continue;
	if(ch=='Q') {
		outchar('Q');
		if(useron.misc&COLDKEYS)
			ch=getkey(K_UPPER);
		if(ch==BS) {
			bputs("\b \b");
			continue; }
        CRLF;
        lncntr=0;
        return(-1); }
    else if(sys_status&SS_ABORT) {
        CRLF;
        lncntr=0;
        return(-1); }
    else if(ch==CR) {
        CRLF;
        lncntr=0;
        return(i); }
    else if(ch==BS && n) {
        bputs("\b \b");
        i/=10;
        n--; }
	else if(isdigit(ch) && (i*10L)+(ch&0xf)<=max && (ch!='0' || n)) {
		i*=10L;
        n++;
        i+=ch&0xf;
        outchar(ch);
		if(i*10L>max && !(useron.misc&COLDKEYS)) {
            CRLF;
            lncntr=0;
            return(i); } } }
return(0);
}

/*****************************************************************************/
/* Displays or erases [WAIT] message                                         */
/*****************************************************************************/
void waitforsysop(char on)
{
    static saveatr;
    int i,j;

if(on) {
	saveatr=curatr;
    bputs(text[Wait]);
    lclatr(LIGHTGRAY);
    return; }
j=bstrlen(text[Wait]);
attr(saveatr);
for(i=0;i<j;i++)
    bputs("\b \b");
}

/****************************************************************************/
/* Returns 1 if a is a valid ctrl-a code, 0 if it isn't.                    */
/****************************************************************************/
char validattr(char a)
{

switch(toupper(a)) {
    case '-':   /* clear        */
    case '_':   /* clear        */
    case 'B':   /* blue     fg  */
    case 'C':   /* cyan     fg  */
    case 'G':   /* green    fg  */
    case 'H':   /* high     fg  */
    case 'I':   /* blink        */
    case 'K':   /* black    fg  */
    case 'L':   /* cls          */
    case 'M':   /* magenta  fg  */
    case 'N':   /* normal       */
    case 'P':   /* pause        */
    case 'R':   /* red      fg  */
    case 'W':   /* white    fg  */
    case 'Y':   /* yellow   fg  */
    case '0':   /* black    bg  */
    case '1':   /* red      bg  */
    case '2':   /* green    bg  */
    case '3':   /* brown    bg  */
    case '4':   /* blue     bg  */
    case '5':   /* magenta  bg  */
    case '6':   /* cyan     bg  */
    case '7':   /* white    bg  */
		return(1); }
return(0);
}

/****************************************************************************/
/* Strips invalid Ctrl-Ax sequences from str                                */
/* Returns number of ^A's in line                                           */
/****************************************************************************/
char stripattr(char *strin)
{
    uchar str[256];
    uchar a,c,d,e;

e=strlen(strin);
for(a=c=d=0;c<e;c++) {
    if(strin[c]==1) {
        a++;
        if(!validattr(strin[c+1])) {
            c++;
            continue; } }
    str[d++]=strin[c]; }
str[d]=0;
strcpy(strin,str);
return(a);
}

/****************************************************************************/
/* Redraws str using i as current cursor position and l as length           */
/****************************************************************************/
void redrwstr(char *strin, int i, int l, char mode)
{
    char str[256],c;

sprintf(str,"%-*.*s",l,l,strin);
c=i;
while(c--)
    outchar(BS);
if(mode&K_MSG)
    bputs(str);
else
    rputs(str);
if(useron.misc&ANSI) {
    bputs("\x1b[K");
    if(i<l)
        bprintf("\x1b[%dD",l-i); }
else {
    while(c<79) { /* clear to end of line */
        outchar(SP);
        c++; }
    while(c>l) { /* back space to end of string */
        outchar(BS);
        c--; } }
}

/****************************************************************************/
/* Outputs a string highlighting characters preceeded by a tilde            */
/****************************************************************************/
void mnemonics(char *str)
{
    char *ctrl_a_codes;
    long l;

if(!strchr(str,'~')) {
	mnestr=str;
	bputs(str);
	return; }
ctrl_a_codes=strchr(str,1);
if(!ctrl_a_codes) {
	if(str[0]=='@' && str[strlen(str)-1]=='@' && !strchr(str,SP)) {
		mnestr=str;
		bputs(str);
		return; }
	attr(color[clr_mnelow]); }
l=0L;
while(str[l]) {
    if(str[l]=='~' && str[l+1]) {
        if(!(useron.misc&ANSI))
            outchar('(');
        l++;
        if(!ctrl_a_codes)
            attr(color[clr_mnehigh]);
        outchar(str[l]);
        l++;
        if(!(useron.misc&ANSI))
            outchar(')');
        if(!ctrl_a_codes)
            attr(color[clr_mnelow]); }
    else {
        if(str[l]==1) {             /* ctrl-a */
            ctrl_a(str[++l]);       /* skip the ctrl-a */
            l++; }                  /* skip the attribute code */
        else
            outchar(str[l++]); } }
if(!ctrl_a_codes)
    attr(color[clr_mnecmd]);
}

/****************************************************************************/
/* Prompts user for Y or N (yes or no) and CR is interpreted as a Y         */
/* Returns 1 for Y or 0 for N                                               */
/* Called from quite a few places                                           */
/****************************************************************************/
char yesno(char *str)
{
    char ch;

strcpy(question,str);
SYNC;
if(useron.misc&WIP) {
	strip_ctrl(question);
	menu("YESNO"); }
else
	bprintf(text[YesNoQuestion],str);
while(online) {
	if(sys_status&SS_ABORT)
		ch=text[YN][1];
	else
		ch=getkey(K_UPPER|K_COLD);
	if(ch==text[YN][0] || ch==CR) {
		if(bputs(text[Yes]))
			CRLF;
        lncntr=0;
        return(1); }
	if(ch==text[YN][1]) {
		if(bputs(text[No]))
			CRLF;
        lncntr=0;
        return(0); } }
return(1);
}

/****************************************************************************/
/* Prompts user for N or Y (no or yes) and CR is interpreted as a N         */
/* Returns 1 for N or 0 for Y                                               */
/* Called from quite a few places                                           */
/****************************************************************************/
char noyes(char *str)
{
    char ch;

strcpy(question,str);
SYNC;
if(useron.misc&WIP) {
	strip_ctrl(question);
	menu("NOYES"); }
else
	bprintf(text[NoYesQuestion],str);
while(online) {
	if(sys_status&SS_ABORT)
		ch=text[YN][1];
	else
		ch=getkey(K_UPPER|K_COLD);
	if(ch==text[YN][1] || ch==CR) {
		if(bputs(text[No]))
			CRLF;
        lncntr=0;
        return(1); }
	if(ch==text[YN][0]) {
		if(bputs(text[Yes]))
			CRLF;
        lncntr=0;
        return(0); } }
return(1);
}

/****************************************************************************/
/* Waits for remote or local user to hit a key that is contained inside str.*/
/* 'str' should contain uppercase characters only. When a valid key is hit, */
/* it is echoed (upper case) and is the return value.                       */
/* Called from quite a few functions                                        */
/****************************************************************************/
long getkeys(char *str, ulong max)
{
	uchar ch,n=0,c;
	ulong i=0;

strupr(str);
while(online) {
	ch=getkey(K_UPPER);
    if(max && ch>0x7f)  /* extended ascii chars are digits to isdigit() */
        continue;
    if(sys_status&SS_ABORT) {   /* return -1 if Ctrl-C hit */
        attr(LIGHTGRAY);
        CRLF;
        lncntr=0;
        return(-1); }
    if(ch && !n && (strchr(str,ch))) {  /* return character if in string */
		outchar(ch);
		if(useron.misc&COLDKEYS && ch>SP) {
			while(online && !(sys_status&SS_ABORT)) {
				c=getkey(0);
				if(c==CR || c==BS)
					break; }
			if(sys_status&SS_ABORT) {
				CRLF;
				return(-1); }
			if(c==BS) {
				bputs("\b \b");
				continue; } }
		attr(LIGHTGRAY);
        CRLF;
        lncntr=0;
        return(ch); }
    if(ch==CR && max) {             /* return 0 if no number */
        attr(LIGHTGRAY);
        CRLF;
        lncntr=0;
        if(n)
			return(i|0x80000000L);		 /* return number plus high bit */
        return(0); }
    if(ch==BS && n) {
        bputs("\b \b");
        i/=10;
        n--; }
    else if(max && isdigit(ch) && (i*10)+(ch&0xf)<=max && (ch!='0' || n)) {
        i*=10;
        n++;
        i+=ch&0xf;
        outchar(ch);
		if(i*10>max && !(useron.misc&COLDKEYS)) {
            attr(LIGHTGRAY);
            CRLF;
            lncntr=0;
			return(i|0x80000000L); } } }
return(-1);
}

void center(char *str)
{
	 int i,j;

j=bstrlen(str);
for(i=0;i<(80-j)/2;i++)
	outchar(SP);
bputs(str);
CRLF;
}


int uselect(int add, int n, char *title, char *item, char *ar)
{
	static uint total,num[500];
	char str[128];
	int i,t;

if(add) {
	if(ar && !chk_ar(ar,useron))
		return(0);
	if(!total)
		bprintf(text[SelectItemHdr],title);
	num[total++]=n;
	bprintf(text[SelectItemFmt],total,item);
	return(0); }

if(!total)
	return(-1);

for(i=0;i<total;i++)
	if(num[i]==n)
		break;
if(i==total)
	i=0;
sprintf(str,text[SelectItemWhich],i+1);
mnemonics(str);
i=getnum(total);
t=total;
total=0;
if(i<0)
	return(-1);
if(!i) {					/* User hit ENTER, use default */
	for(i=0;i<t;i++)
		if(num[i]==n)
			return(num[i]);
	if(n<t)
		return(num[n]);
	return(-1); }
return(num[i-1]);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.