/****************************************************************************** * * METAL/Z-Msg library functions * * FILE: MELIB.C * * * Z-Msg, Metal and Metal Message System are Trademarked and * Copyright (c) 1984,1985,1986 Tim Gary * All rights reserved. * * * Common utility finctions used in METAL/Z-MSG * ****************************************************************************** * * 1.50xx 06/12/86 Added ^K enable/disable in TYPE funciton.. * 1.50xx ??/??/86 Added ! options for typing files.. user type only displays * 1.50xx 04/12/86 Put SQ stuff in.. works ok now.. * 1.50xx 04/06/86 Generalized library stuff.. * 1.50xx 03/10/86 Echo toggle fixed for middle of lines, users stuff * cut down in size.. * 1.40xx 02/13/86 Time on system at command prompt fixed.. * 02/11/86 Time on system today based on current+total today... * 02/10/86 Added library type function (cheapie!) * 02/04/86 Fix time stuff (again), add user file routines.. * 01/26/86 timefix moved here, same with new dateasc/timeasc functions. * 01/25/86 Changed and added user-type, and time/date stuff.. *------ * 1.31a 10/13/85 Release version. Includes the following changes: * link_prog(name), go_os(f), and hangup() routines. * New method of reading the clock (use asm insert routines) * Real tabs for typed text (8 char stops). * ^E echo surpress. ^O abort in typing files. * Run com file on exit from bbs (bye or cpm). * General code cleanup. * 1.30xx 7/07/85 QX10 routine fixed... * 7/01/85 Echo toggle with ^E implemented.. * T 6/23/85 Sysop ^P thing for list output added, new qx10 clock stuff. * E 6/19/85 Findbye put back.. * S 6/12/85 Modified new date routine to check if ;y was entered earlier * T 6/09/85 Enf of line bell every other char after linelen-8 * I 5/02/85 Modified Date question to only accept y/n answers. * N 3/07/85 LinkBye added for use in cpm 8/16 systems for hanging up. * G 3/04/85 LF's converted to spaces in getl.. * 2/26/85 ZCPR3 Support added, and old RESETDRIVE stuff deleted. *------ * 1.20b 01/13/85 MM58167A Kenmore Computer Tech. clock board support added. * Time compare added. Set getl to ignore linefeeds... * Fix type to strip bit 8 (for WS files), getchar also strips. * New clock routines added.. CCS and QX10. * * 1.20a 11/04/84 Counters routines made less dependant upon right info. *------ * 1.10e 11/02/84 Added to pcounters routine, now wil write to specified file * if arg!=0. Put writestat routine here, for easy access. * Parsing routine fixed to zap trailing blanks in ask func. * Future note: CAN'T change in getline!!!!!!!! * * 1.10c 10/0x/84 Hayes clock date order changed to reasonable mm/dd/yy form. * * 1.10b (testing only version) * 1.10a 8/31/84 Overlay stuff started. *------ * 1.01a 6/27/84 Multi user stuff inserted. Setup for Aztec C 1.06. * CCS clock stuff added. * * 1.0c 5/17/84 Added Hayes clock support. * * 1.0b 4/20/84 Added usindex() (index ignoring case), distext() * Findbye routine setup.. * Added Upper/lower case routines. Added Novice user help. * Putchar returns char typed at [More] prompt for checking. * Removed several MENTER only functions (putcaller, writestat) * External height variable used for screen height now. * * 1.0a 1/25/84 Added terminal height code. Now pauses with [more] prompt * * 1.0 1/17/84 Version 3.0a new format of files. * *****************************************************************************/ #include "xpm.h" /* CPMIO header file for i/o operations.*/ #include "megen.h" /* general defines */ #include "meglob.h" /* global variable definitions */ #include "meovfn.h" /* overlay function numbers */ #include "mefiles.h" /* file names */ #include "ctype.h" static char line=1; /* current line (relative to last input) */ static int col_pos=0; /* position on line */ static int *squeezed=NULL; /* sqeezed flag.. if != 0, then ptr to alloc */ static int break_disable; /* flag to check for break or not for file */ /* return pointer to combined location+filename, based on passed offset */ char *makefn(n) int n; { static char filename[18]; rst_sq(); /* Reset this most of the time */ sprintf(filename,"%s%s",O.file_locs[n],files[n]); return filename; /* return pointer to it */ } /******************************** * Return pointer to user type * ********************************/ u_types *get_tp(t_char) char t_char; { register int i; for (i=0; O.user_type[i].type!='\0'; i++) if (O.user_type[i].type==t_char) break; if (O.user_type[i].type=='\0') --i; /* return last real type */ return &(O.user_type[i]); /* return pointer */ } getw(fil) FILE *fil; { register int n; n=getrawc(fil); n|=(getrawc(fil)<<8); return n; } getrawc(fil) FILE *fil; { register int i; i=getc(fil); if (i!=EOF) return i; if (eobuf(fil)) return EOF; return 0x1a; /* if EOF flag, but not physicl eof, return logical eof */ } /******************************** * type a file, stop on a break * ********************************/ type(filename) char *filename; { FILE *fil; if ((fil=open(filename,F_RD | F_UNLOCK))==NULL) return ERROR; /* open text file for read */ type_from(fil); /* type file from current location */ close(fil); return NULL; } type_from(fil) FILE *fil; { register int cfast; char u_stats[15]; *u_stats='\0'; /* clear this */ /* first initialize buffer, and check for squeezed file */ if (read(fil,1)!=128) return ERROR; if (sq_init(fil)==ERROR) return ERROR; novhelp(); /* print abort help */ while ( ( cfast=tgetc(fil) )!=EOF ) { cfast&=0x7f; /* strip high bit so doc mode ws files work */ if (!col_pos && cfast=='!') /* if column 0 and '!' */ { register int i,n; for (i=0; ((n=tgetc(fil))!=EOF) && index("+snxXabcdefKk",(char)n); i++) { if (tolower((char) n) == 'k') break_disable ^= TRUE; else u_stats[i]=n; } u_stats[i]='\0'; /* terminate string */ if (n==EOF) break; /* abort if end of file here */ do cfast=tgetc(fil); while (!isspace(cfast)); /* until we hit white space */ } if (cfast!='\r') { if ( !(*u_stats) || index(u_stats,user.status)) if ( bcputchar(cfast)==ERROR || (globalchar&0x1f)==0x0f) { if (!break_disable) { globalchar='\0'; break; } } } } rst_sq(); /* Reset squeezed flags if any */ break_disable=FALSE; return NULL; } tgetc(fil) FILE *fil; { if (squeezed) return sq_getc(fil); else return getc(fil); } /* Library version of type routines.. */ typedef struct { char file_stat; /* 00=file here.. ff=empty */ char file_name[11]; /* filename (like fcb setup) */ int seek; /* seek location in file */ int length; /* number of records in file */ char pad[16]; /* crc/pad.. */ } lib_fcb; static FILE *l_fd=0; /* init to point to nothing.. */ static char l_name[18]; /* name of last library file */ FILE *lopen(libname,mname) char *libname; /* library name */ char *mname; /* member name */ { char name[12]; /* member name after fix */ lib_fcb *l_fcb; int ndrecs; /* number of directory records to scan */ register int i,n,flag; if (!(*l_name) || strcmp(libname,l_name)) /* no match.. close old, open new */ { if (l_fd) close(l_fd); if ( !(l_fd=open(libname,0)) ) /* error on open */ { *l_name='\0'; /* clear for next */ return NULL; } strcpy(l_name,libname); /* make new name */ } /* library open.. scan for member.. and display (like type) */ if (index(mname,'.') || strlen(mname)!=11) { fcbinit(mname,buffer); strncpy(name,buffer+1,11); } else strncpy(name,mname,11); name[11]='\0'; setarec(l_fd,0); /* make sure we are at begining */ l_fcb=bufloc(l_fd); read(l_fd,0); ndrecs=l_fcb->length; flag=TRUE; for (i=0; ifile_name); */ if ( !(l_fcb->file_stat) && !strncmp(l_fcb->file_name,name,11)) { flag=FALSE; break; } } } if (flag) return NULL; /* member not found */ setarec(l_fd,l_fcb->seek); /* seek to start of member text */ return l_fd; } lclose(f) FILE *f; { if (*l_name) close(l_fd); rst_sq(); *l_name='\0'; } ltype(libname,mname) char *libname; /* library name */ char *mname; /* member name */ { if (!lopen(libname,mname)) return ERROR; /* not found */ type_from(l_fd); /* type from here on... (til EOF) */ /* we leave library files open until next time */ } /******************************************************* * Stuff for typing squeezed files.. *******************************************************/ static int rept_count,l_sqchar,c_sqchar,bit_pos; sq_init(fil) /* check for sq, setup tables if so */ FILE *fil; { register unsigned u,t; u=getw(fil); if (u!=0xff76) /* magic squeezed file number? */ { setbuf(fil,0); /* position back to start of getc buffer */ return; /* and return */ } setbuf(fil,4); /* skip to filename */ while (getc(fil)!=0); /* skip filename */ u=getw(fil); /* get number of node entries */ if (!( squeezed=malloc(u*sizeof(int)*2) ) ) return ERROR; /* can't alloc tbl */ /* printf("\nNumber of table entries: %d\n",u); */ for (t=0; t0) { rept_count--; ret_ch=l_sqchar; } else { c=sq_func(fil); if (c==0x90) { if (rept_count=sq_func(fil)) { rept_count-=2; ret_ch=l_sqchar; } else ret_ch=0x90; } else ret_ch=l_sqchar=c; } return ret_ch==0x1a ? EOF : ret_ch; } sq_func(fil) /* decode current or next char */ FILE *fil; { register int i=0; do { if (++bit_pos>7) { c_sqchar=getrawc(fil); bit_pos=0; } else c_sqchar>>=1; i=squeezed[(i<<1)+(c_sqchar & 1)]; } while (i>=0); i=0-(i+1); return i ? i : 0x1a; } rst_sq() /* clear squeezed flag, and dealloc ptr to alloc area */ { if (squeezed>(char *)1) free(squeezed); /* 1 is correct number! */ squeezed=NULL; } /******************************************************* * display text from pointer to array of char pointers * * (eg static char *it_hlp[] = { "1","2","3",0 }; )* *******************************************************/ dis_text(sp) register char **sp; { while (*sp && (send(*sp++)!=ERROR)); } /* A one liner to help novices out on the system */ novhelp() { if (!(user.flags&EXPERT)) /* if not expert, print msg */ send("\n ** Control-K to abort, Control-S to pause **\n"); } /* Regular old putchar, but checks for break (^K, etc..) chars. Returns ERROR if one hit, else it returns the char */ bcputchar(ch) int ch; { register int a; if (!breakkey()) { if ( ( (a=toupper(putchar(ch))) & 0x1f)==0xb ) a=ERROR; } else a=ERROR; return a; } send(s) /* send string while checking for break */ register char *s; { while (*s) if (bcputchar(*s++)==ERROR) return ERROR;; return 0; /* else ok */ } /**************************************** * Print string to printer (LST:) * ****************************************/ print(str) register char *str; { while (*str) bdos(5,*str++); } /******************************** * check for ^k ^x, ^s, etc.. * ********************************/ breakkey() /* check for break */ { register int key; if (key=bdos(6,0xff)) { int tc; tc=key&0x1f; if ( tc==0x0b || tc==0x18) { putchar('\n'); return ERROR; } if (tc==0x13) /* pause if ^S */ getd(); globalchar=key; /* save the char for later reference */ } return 0; /* none pressed */ } /****************************************************************************** * ask: get string after prompting user. * * question= prompt string (will NOT be printed if ';' used * to get mult. commands/options.. * str = place to put string * maxchars= max # of chars to be accepted from terminal * options = following BITs that may be combined: * UPLOW : accept upper/lower case input * UP : upper case conversion is made * NOECHO : turn off display of chars as they are entered * NUMBER : a 'position' number is output instead of chars typed * MSGLINE : ONLY FOR getl(*).. for it to accept MAXMSGLINE chars * NOTOS : don't print time on system at prompt * *****************************************************************************/ ask(question,str,maxchars,options) char *question,*str; int maxchars; register char options; { int mins; static char sepchars[3]={ ';', '\0', '\0' }; /* string of seperator chars */ char *cp; char *sepcp=0; sepchars[1]=sepstr; /* make string of 1 or 2 seperator chars */ if (!(options&NOTOS)) mins=check_tos(); /* get time on sys today */ if ((strloc==0) || (glbstr[strloc]=='\0')) { if (O.RTC && !(options&NOTOS)) { if (*question=='\n') printf("\n[%d mins] %s",mins+user.minutes,question+1); else printf("[%d mins] %s",mins+user.minutes,question); } else send(question); getl(glbstr,options); /* get the line */ strloc=0; /* make sure of this */ } /* see if seperator in string */ for (cp=&glbstr[strloc]; *cp && !(sepcp=index(sepchars,*cp)); cp++); if (sepcp) { /* change sepcp into ptr to actual text line */ sepcp=index(&glbstr[strloc],*sepcp); *sepcp='\0'; /* terminate at seperator character */ } if (strlen(&glbstr[strloc])>=maxchars) glbstr[strloc+maxchars]='\0'; if (options & UP) upcase(&glbstr[strloc]); /* all upper case if spec'd */ strcpy(str,&glbstr[strloc]); /* copy it to where it's wanted */ if (sepcp) strloc=(sepcp-glbstr)+1; /* updates strloc if ';' */ else strloc=0; /* else start over */ } /* ask */ /* read a line of input from console */ getl(s,options) char *s,options; { register int c; register int llen; int loop_flag; char *temp; register int count; if (options&MSGLINE) llen=O.MAXMSGLINE; else llen=MAXLINE; temp=s; count=0; if (user.flags&BELL) putchar(7); /* ring bell */ loop_flag=TRUE; while ((c=getd()) != '\r' ) { if (!echo_flag) options|=NOECHO; /* force no echo if this set */ if (c=='\n') c=' '; /* convert a linefeed to a space.. */ if (c=='\b' || c==0x7f) { /* backspace or DEL */ if (!count) continue; --count; --s; if (!(options & NOECHO) || (options & NUMBER)) send("\b \b"); continue; } if (c>=' ') /* tab goes in directly */ { *s++=c; if (count=llen-9 && (isspace(c) || (ispunct(c) && c!='\'')) ) loop_flag=FALSE; /* if (count>=llen-8 && (count&1)) */ /* bell every other char */ /* putchar(7); */ } if (c==9) { if (count+16=' ') putchar(c); } else if ((options & NUMBER) != 0) putchar( (count % 10)+'0'); if (!loop_flag) break; } /* while */ *s='\0'; /* end of string */ putchar('\n'); return temp; } /************** * New putchar that counts columns *************/ putchar(ch) register int ch; { register int i,c; c=NULL; /* returned character for MORE prompt */ if (ch < ' ') { switch (ch) { case '\t': for (i = 0; i <= (col_pos+8)-((col_pos+8) & 0xf8); i++) putchar(' '); return c; /* quicky return */ case '\n': col_pos=0; line++; outc('\r'); break; case '\r': col_pos=0; break; case '\b': --col_pos; break; } /* switch */ } /* control char check */ else col_pos++; outc(ch); /* output character.. */ if (height && line>=height) { new_page(); /* MUST BE HERE OR RECURSIVE PUTCHAR WILL CROKE */ if (user.flags&EXPERT) { /* don't print much if expert */ printf(" "); c=getd(); putchar('\r'); } else { printf("[Press RETURN to continue]"); c=getd(); /* get char which clears lines */ putchar('\n'); } } return c; } /* new putchar */ outc(ch) register int ch; { bdos(6,ch); if (print_flag==ON) bdos(5,ch); /* if printer flag on, write to it */ } /* new getchar to clear line variable, etc.. */ getchar() { register int cfast; cfast=getd(); if (echo_flag) putchar(cfast); return cfast; } getd() { register int cfast; new_page(); /* reset line counter */ cfast=c_wait(); if (user.status==SYSOP) if (cfast==0x10) /* ^P */ { if (print_flag) print_flag=OFF; else print_flag=ON; return c_wait(); } if (cfast==5) /* ^E */ { if (echo_flag) echo_flag=OFF; else echo_flag=ON; return c_wait(); } return cfast; } c_wait() { register int cfast; while(!(cfast = (bdos(6,0xff) & 0x7f) ) ); return cfast; } new_page() { line=1; } /* New readclock routine... */ /* this one requires the config program to select a clock routine out of several supplied files.. the user may create their own clock code, etc.. */ readclock() { int (*clock_fn)()=0x0212; /* location of actual routine */ usr **u_p=0x0207; /* pointer to pointer of user structure */ char *rtcbuf=0x0209; /* buffer returning hr,mi,se,yr1,yr2,mo,da */ *u_p=&user; /* pass pointer to user variable */ if (*(rtcbuf-5)) (*clock_fn)(); /* call it if installed */ else O.RTC=NOCLOCK; /* otherwise set this for later */ movmem(rtcbuf,time,2); *date=rtcbuf[5]; *(date+1)=rtcbuf[6]; *(date+2)=rtcbuf[4]; } char *asctime(t) /* convert internal format time ascii */ char *t; { static char timestr[9]; /* watch out!!! don't call routine twice in one func */ int c; c=*t; if (c>=0x12) { c=( (*t & 0xf0) >> 4 ) * 10 + (*t & 0xf); if (c!=12) c-=12; sprintf(timestr,"%2d:%02x pm",c,(int)t[1]); } else { if (c==0) c=0x12; sprintf(timestr,"%2x:%02x am",c,(int)t[1]); } return timestr; } char *ascdate(d) char *d; { static char datestr[9]; sprintf(datestr,"%02x/%02x/%02x",(int)d[0],(int)d[1],(int)d[2]); return datestr; } timeasc(at,t) char *at,*t; { *t=xtou(at); /* get first two hex digits */ if (*t==0x12) *t=0; if (index(at,'p')) *t+=0x12; /* if it's pm, at 12 to hour */ t[1]=xtou(at+3); /* get minutes */ } dateasc(ad,d) char *ad,*d; { *d=xtou(ad); d[1]=xtou(ad+3); d[2]=xtou(ad+6); } /* convert ascii hex into unsigned */ unsigned xtou(s) char *s; { unsigned u=0; while(*s && isspace(*s)) ++s; /* ignore leading blanks */ while (isxdigit(*s)) { u<<=4; /* shift previous result by one hex digit (*16) */ if (tolower(*s)>='a' && tolower(*s)<='f') u+=(tolower(*s++)-'a')+10; else u+=(*s++)-'0'; } return u; } getdate() { if (O.RTC!=NOCLOCK) readclock(); return date; } /****************************************************************** * compare times, return minutes difference. Works for 47:59 hours. * passed: current time, compare time, current date, compare date ******************************************************************/ timecomp(t1,t2,d1,d2) char *t1,*t2,*d1,*d2; { register int mins1,mins2; mins1=bcdtodec(t1[1])+(bcdtodec(*t1)*60); mins2=bcdtodec(t2[1])+(bcdtodec(*t2)*60); /* if dates have changed. Add a day */ if (strncmp(d1,d2,3)) mins1+=1440; /* this works since no 0 vals in date */ return mins1-mins2; } bcdtodec(c) char c; { return (int)((c&0x0f)+((c&0xf0)>>4)*10); } check_tos() { int mins=0; int tm; if (O.RTC) { readclock(); /* get time */ mins=timecomp(time,user.time,date,user.date)+user.minutes; tm=user.type_ptr->minutes; if (tm) { if (mins>tm) { send("\n[Time Limit Expired]\n"); hangup(YES); /* hangup after printing copyright/etc */ } else if (mins>(tm-3)) send("\n[Your time on the system is almost up]\n"); } /* if tm */ } return mins-user.minutes; } /************************************************************************ * String functions... */ /* convert string to all upper case */ upcase(str) register char *str; { for (;*str; str++) *str=toupper(*str); } /* check for a space or control char in a string returns TRUE if space or control, else returns FALSE */ isspc(str) register char *str; { while(*str) if (*str++<=' ') return TRUE; /* return TRUE if space/cntrl */ return FALSE; /* else FALSE returned */ } /* capitalize a string (all words in the string */ capstr(str) char *str; { register char *tp; *str=toupper(*str); /* first char is easy */ for ( tp=str; tp0); s1++, s2++, n--) if (*s1=='\0') return (toupper(*s1)-toupper(*s2)); /* if at end of first string, test for=length/return */ return (n ? (toupper(*s1)-toupper(*s2)) : 0); } /***************************** * Same as index(s,c), but with two strings *****************************/ sindex(s1,s2) register char *s1,*s2; { register char *sp; for (sp=s2; strlen(s1)<=strlen(sp); sp++) if (!strncmp(s1,sp,strlen(s1))) return sp; return 0; /* no match */ } /***************************** * Same as sindex(s1,s2), but IGNORE CASE in comparison *****************************/ usindex(s1,s2) char *s1,*s2; { register char *sp; for (sp=s2; strlen(s1)<=strlen(sp); sp++) if (!ustrncmp(s1,sp,strlen(s1))) return sp; return 0; /* no match */ } /* Hangup the phone (exit system..) if value passed!=0 print signoff msg */ hangup(flag) int flag; { char *zero=0; if (flag) printf("\n%s (TM)\nCopyright (c) 1984,1985,1986 Tim Gary\nAll rights reserved.\n\n",O.BBSNAME); if (strcmp(BYE,BYECOM)) link_prog(BYE); /* run prog if name changed */ if (bye!=nothing) *zero=0xcd; exit(0); } /* link_prog... This routine loads and runs a com file.. */ link_prog(name) char *name; { setmem(0x5c,0x24,0); setusr(fcbinit(name,0x5c)); #asm ; ; This is taken from mentr.asm routine.. ; ; The routine moves itself into the buffer at 80h, and ; procedes to load the program that is called for... ; ext ostack ; place where old stack is kept ; lxi h,ostack sphl pop h lxi h,0 push h ; make sure top of stack is zero mvi c,0fh ; open code.. lxi d,005ch ; fcb call 0005 ; bdos cpi 0ffh ; error? jz 0 ; yes boot... ; lxi d,loader lxi h,0080h ; dma.. mvi c,07fh movloop: ldax d mov m,a inx d inx h dcr c jnz movloop jmp 080h ; jump to loader... ; loader: lxi d,100h ; tpa load1: push d mvi c,1ah ; set dma.. call 0005 ; bdos lxi d,005ch ; fcb mvi c,14h ; read seq. call 0005 ; bdos pop d ora a ; error? jnz 80h+(done-loader) ; if read error, finish up.. ; bump the dma counts by 80h lxi h,0080h dad d ; xchg jmp 80h+(load1-loader) ; jr loader ; ; the file has presumably been read.. so run it.. ; done: mvi c,1ah ; set dma lxi d,0080h ; reset default dma address.. call 0005 ; bdos ; mvi c,10h ; close lxi d,05ch call 0005 ; jmp 100h ; tpa.. (program just loaded) ; lend: db 0 ; #endasm } /****************************************************************** * initialize the bye variable to point to location if BYE active */ findbye() { register char *p; bye=(*(char *)2)*256+*(char *)1-2; bye=(*(char *)(bye+1))*256+*(char *)bye+6; /* bye keeps mucking everyone up by changing the locations of strings */ for (p=bye; p<=bye+35; p++) if (!ustrncmp("bye",p,3)) break; if (p>=bye+25) bye=nothing; /* now for the values that we use the bye variable for.. */ maxuser=bye; maxdrive=bye+1; tout=bye+2; nulls=bye+3; } /******************************************************* * Users file utility routines... With hash file lookup * * Returns <0 value if error, else users seek loc *******************************************************/ find_user(name) char *name; { int record,del_rec=0; usr *up; record=read_hash(name); /* get record pointed to by hash file */ if (record==ERROR) return HASHEMPTY; /* nobody in hash table */ up=bufloc(users); /* point to user structure being read */ do { setarec(users,record); read(users,0); record=up->number; /* point to NEXT record in hash list.. */ if ( (up->file_info)&DEADUSER ) del_rec=getrec(users); } while ( ustrcmp(up->name,name) && (up->file_info&MOREHASH)); /* We are now ALWAYS at the end of the hashed name list, or deleted user... */ if ( ustrcmp(up->name,name) ) { if (del_rec) setarec(users,del_rec); return ERROR; /* not found in hash list */ } return getrec(users); /* return seek location if found */ } read_hash(name) char *name; { FILE *hashfile; unsigned hashval; int *ip; if ( !(hashfile=open_hash()) ) return ERROR; hashval=(hash_fn(name)%HFSIZE); setarec(hashfile,(hashval/64)); /* seek to record determined by hash_fn() */ read(hashfile,0); /* read the record */ ip=bufloc(hashfile); hashval=ip[hashval%64]; /* reuse hashval for record returned */ close(hashfile); return hashval; } /* Return a value computed from the string passed.. */ unsigned hash_fn(s) char *s; { register unsigned val=0; register unsigned i; for (i=0; *s; i++,s++) val=val*(i*toupper(*s))+i; /* new */ return val; } /* Open hash file.. if it doesn't exist, create one.. (slow) */ FILE *open_hash() { FILE *fp; int *ibuf,lrec; register unsigned i; usr *up; #if FALSE /* testing only.. */ int goodcount=0; int badcount=0; #endif if (fp=open(HASHFILE,F_RD)) return fp; /* exists already.. */ if ( !(fp=open(HASHFILE,F_RW)) ) return NULL; /* couldn't open at all */ send("\n[Please wait, creating hash file]\n"); ibuf=malloc((HFSIZE<<1)+16); /* Allocate table space to build hash file */ for (i=0; iname)%HFSIZE; if (ibuf[i]==ERROR) #if FALSE { ++goodcount; putchar('+'); /* indicate nice one */ #endif ibuf[i]=lrec; /* not in table.. put it there.. */ #if FALSE } #endif else { /* find end of hash trail */ int record; record=ibuf[i]; #if FALSE ++badcount; putchar('#'); /* indicate bad guy */ #endif do { setarec(users,record); read(users,0); record=up->number; } while (up->file_info&MOREHASH); up->file_info|=MOREHASH; /* set this */ up->number=lrec; /* and pointer to next */ write(users,0); /* resave */ setarec(users,lrec); read(users,0); } /* ERROR else */ up->number=0; /* clear next hash ptr */ up->file_info&=(char)(~MOREHASH); write(users,0); /* write it back */ } /* users file for loop */ /* we are now finished building tables, and updating users file */ for (i=0; i<(HFSIZE>>6); i++) { movmem(&ibuf[i<<6],bufloc(fp),128); write(fp,1); /* write hash record */ } setarec(fp,0); /* reset this to first */ free(ibuf); #if FALSE printf("\nOptimum hash values=%d, collisions=%d.\n",goodcount,badcount); #endif return fp; } /* EOF MELIB.C */