/* * $Id: opt.c,v 1.5 2002/08/23 13:38:14 howardjp Exp $ * * Copyright (c) 1990 * Jan Wolter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jan Wolter * and his contributors. * 4. Neither the name of Jan Wolter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JAN WOLTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL JAN WOLTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* PARTY PROGRAM -- OPTION MAINTAINANCE ROUTINES -- Jan Wolter */ #include "party.h" #include "opt.h" #define VRSZ 1024 /* More than the total size of all options */ char var_buf[VRSZ]; /* Storage for string values of options */ int var_end= 0; /* Index of first unused byte of var_buf */ struct optent dflt_copt[NCOPT]; FILE *openchn(); /* The partytab contains any number of lines of the form: * * progname default-option-list * * progname is the name of the program, as in argv[0]. The options are in the * usual format. This lets you set up different versions of party by making a * link to the party program with a different name and entering a line setting * the options for that name in partytab. */ readtab() { FILE *fp; register int pnl; char *partytab= PARTYTAB; if ((fp= fopen(partytab,"r")) == NULL) { err("cannot open %s -- using default options\n",partytab); return; } pnl= strlen(progname); while (fgets(txbuf,BFSZ,fp) != NULL) if (!strncmp(txbuf,progname,pnl)) { if (txbuf[pnl] != ' ' && txbuf[pnl] != '\t') continue; parseopts(txbuf+pnl+1,1); } fclose(fp); return; } /* This sets the channel options from the chantab. The chantab contains * any number of lines of the form: * * chn_name_pat channel-option-list * * chn_name_pat is an text pattern that is matched against the channel name. * It may include the usual ?, *, \ and [] constructs, but not {}. The * options are in the usual format. Normally only channel options should be * specified here because only those are reset when you leave a channel. The * "file=" option should be specified on just about all channels. * * The first line with a pattern matching the named channel will be used. If * no line matches, then no options are read and the function returns 1. */ int chnopt(newchn) char *newchn; { int i; FILE *fp; char *endpat; /* Open the Channel Table */ if ((fp= openchn()) == NULL) return(1); /* Check if the Channel Table includes a line for this name */ for (;;) { if (fgets(txbuf,BFSZ,fp) == NULL) return(1); *(endpat= firstin(txbuf," \t\n"))= '\0'; if (patmatch(newchn,txbuf)) break; } /* First, reset defaults values of all channel options */ for (i= 0; i VRSZ) { err("Out of variable space\n"); return; } strcpy(var_buf+var_end,opt[i].str); opt[i].str= var_buf + var_end; var_end+= len + 1; } } /* * Set the list of options given by the string c. * source == 1 if this comes from partytab or chantab. * source == 2 if this comes from command line. * source == 0 otherwise. */ parseopts(c,source) register char *c; int source; { char *optbeg,*keybeg,*keyend,*strbeg,*strend,*optend,*p; register int i; int prefix,quote; for (;;) { /* Skip white leading space and punctuation */ c= firstout(c," \t,;:"); /* Is this the end of the line? */ if (*c == '\0' || *c == '\n') return; /* Find end of keyword */ optbeg= c++; optend= c= firstin(c," ,;:\t=\n"); /* Is it a channel selector? */ if (source == 2 && *optbeg == '#') { setstr(OPT_START,optbeg+1,optend-optbeg-1); continue; } /* Is there a prefix? */ keybeg= optbeg; prefix= 1; /* Assume no prefix */ if (keybeg[0] == 'n' && keybeg[1] == 'o') { prefix= 0; keybeg+= 2; } if (keybeg[0]=='s' && keybeg[1]=='e' && keybeg[2]=='e') { prefix= (prefix == 1) ? 2 : 1; keybeg+= 3; } /* Is there a string value? */ if (*c == '=') { strbeg= ++c; if (source == 2) { strend= c= firstin(c,"\n"); } else { if (*c == '\042') { /* Delimit string in double-quotes */ strbeg= ++c; strend= c= firstin(c,"\n\042"); } else if (*c == '\047') { /* Delimit string in single-quotes */ strbeg= ++c; strend= c= firstin(c,"\n\047"); } else /* Delimit unquoted string */ strend= c= firstin(c," ,:;\t\n"); if (*c=='\042' || *c=='\047') c++; } } else strbeg= NULL; optend= c; /* Look for name in table */ for (i=0; i < NOPT; i++) { if (!strncmp(keybeg,opt[i].name,strlen(opt[i].name))) { /* Am I allowed to change this option? */ if ((opt[i].pf & (PF_SYS|PF_CHN)) && source != 1) { err("%s is not a user settable option\n",opt[i].name); break; } /* Do string valued options */ if (strbeg) { /* Check that number is assigned to numeric option */ if (opt[i].pf & PF_NUM) { for (p=strbeg; p < strend; p++) if (*p < '0' || *p > '9') { err("Nonnumeric value assigned to %s\n", opt[i].name); goto next; } } /* Save the new value */ if (opt[i].pf & PF_STR) setstr(i,strbeg,strend-strbeg); else { err("%s does not take a string value\n",opt[i].name); break; } if (i == OPT_FILTER && opt[OPT_FILTER].yes) start_filter(); if (i == OPT_MAILDIR) setmailfile(); if (i == OPT_WRAP) stashname(); } /* Do "See" options */ if (prefix == 2) { if (opt[i].pf & PF_SEE) opt[i].yes= prefix; else err("Invalid option: see%s\n", opt[i].name); } else /* Do boolean options */ { if (!(opt[i].pf & PF_NO)) { if (prefix == 0) { err("%s does not take a boolean value\n", opt[i].name); } } else { opt[i].yes= prefix; if (i == OPT_FILTER) { if (prefix) start_filter(); else stop_filter(); } if (i == OPT_DEBUG) set_debug(); } } break; } } if (i == NOPT) err("Invalid option: %.*s\n",optend-optbeg,optbeg); next:; } } setnum(nopt,new) int nopt; int new; { char anew[12]; sprintf(anew,"%d",new); setstr(nopt,anew,strlen(anew)); } /* Set the string valued option number nopt to the value new which has a * length of newlen. */ setstr(nopt,new,newlen) int nopt; char *new; int newlen; { register char *src, *dst; register int i; char *stop; int oldlen= strlen(opt[nopt].str); int inc; if (oldlen != newlen) { if ((inc= newlen - oldlen) > 0) { /* String lengthened */ if (var_end + inc >= VRSZ) { err("Out of variable space\n"); return; } src= var_buf+var_end; dst= src+inc; stop= opt[nopt].str + oldlen; while (src > stop) *(--dst)= *(--src); } else { /* String shortened */ src= opt[nopt].str + oldlen; dst= src + inc; stop= var_buf+var_end; while (src < stop) *(dst++)= *(src++); } /* Fix affected pointers */ var_end+= inc; for (i=nopt+1; i= NCOPT || hasdefval(i)) && (!list || !inlist(opt[i].name,list)) && (list || !(opt[i].pf & PF_SHOW))) continue; didprint=1; if (opt[i].pf & (PF_NO|PF_SEE)) { pref= (opt[i].yes == 1) ? "" : ((opt[i].yes == 0) ? "no" : "see"); printed+= (a= strlen(opt[i].name) + strlen(pref) + 2); if (printed >= cols) { fputc('\n',fp); printed= a; } fprintf(fp,"%c %s%s", (printed==a) ? prefix : ' ', pref, opt[i].name); } if (opt[i].pf & PF_NUM) { printed+= (a= strlen(opt[i].name) + strlen(opt[i].str) + 3); if (printed >= cols) { fputc('\n',fp); printed= a; } fprintf(fp,"%c %s=%s", (printed==a) ? prefix : ' ', opt[i].name, opt[i].str); } else if (opt[i].pf & PF_STR) { printed+= (a= strlen(opt[i].name) + strlen(opt[i].str) + 5); if (printed >= cols) { fputc('\n',fp); printed= a; } fprintf(fp,"%c %s=\042%s\042", (printed==a) ? prefix : ' ', opt[i].name, opt[i].str); } } if (printed != 0) fputc('\n',fp); return(!didprint); } int inlist(name,list) char *name,*list; { int nmlen; char *p; nmlen= strlen(name); p= list-1; while ((p= strstr(p+1,name)) != NULL) { if ((p == list || p[-1] == ' ' || p[-1] == '\t') && strchr(" \t\n",p[nmlen])) return(1); } return(0); } /* Firstin() returns a pointer to the first character in s that is in l. * \0 is always considered to be in the string l. * * Firstout() returns a pointer to the first character s that is not in l. * \0 is always considered to be not in the string l. * * Note that unlike strpbrk() these never return NULL. They always return * a valid pointer into string s, if only a pointer to it's terminating * \0. They are amazingly useful for simple tokenizing. */ char *firstin(s,l) register char *s; char *l; { for (;*s;s++) if (strchr(l,*s)) break; return(s); } char *firstout(s,l) register char *s; char *l; { for (;*s;s++) if (!strchr(l,*s)) break; return(s); } /* STRSTR() find a string in a string. This is the dumb algorithm, meant only * for short strings. It is only here for those Unixes who don't have one in * the system library. */ #ifdef NOSTRSTR char *strstr(s,p) char *s, *p; { register char *sp, *pp; for(sp= s, pp= p; *sp && *pp; ) { if (*sp == *pp) { sp++; pp++; } else { sp= sp - (pp - p) + 1; pp= p; } } return(*pp ? NULL : sp-(pp-p)); } #endif /*NOSTRSTR*/ /* LISTCHN: prints a list of channels. This will include any channel named * uniquely in the chantab, plus any volatile channels with at least one user. */ int listchn() { FILE *fp; char *o,*n; struct chnname *head=NULL, *ch, *chn; int ln,clen; /* Open the Channel Table */ if ((fp= openchn()) == NULL) return(1); /* Put names from the chantab into the list */ while(fgets(txbuf,BFSZ,fp) != NULL) { *firstin(txbuf," \t\n")= '\0'; /* Scan for wild cards, and eliminate backslashes */ for (o= n= txbuf; *o != '\0'; o++,n++) { if (*o == '*' || *o == '?' || *o == '[') goto skipit; if (*o == '\\') o++; *n= *o; } *n= '\0'; head= addchn(head,txbuf,0); skipit: ; } /* Count up the users and find any volatile channels */ head= who_clist(head); /* Print out the list */ fputs(" Channel Users | Channel Users | Channel Users", stderr); ln= 0; for (ch= head; ch != NULL; ch= ch->next) { if ((ln++)%3 == 0) fputc('\n',stderr); else fputs(" | ",stderr); fputc(strncmp(ch->name,channel,CHN_LEN) ? ' ' : '>', stderr); fputs(ch->name,stderr); clen= strlen(ch->name); #ifndef NOCLOSE if (check_open(ch->name) != 1) { fputs("(closed)",stderr); clen+= 8; } #endif NOCLOSE for ( ; clen < 20; clen++) fputc(' ', stderr); fprintf(stderr,"%3d",ch->users); } fputc('\n',stderr); if (ln == 0) fputs("No Channels\n",stderr); /* Free the list */ for (ch= head; ch != NULL; ch= chn) { chn= ch->next; free(ch); } } /* ADDCHN: Add an entry to the channel list in alphabetical order. */ struct chnname *addchn(head,name,users) struct chnname *head; char *name; int users; { struct chnname *c, *p, *new; for (c= head, p= NULL; c != NULL; c= (p= c)->next) if (strncmp(name,c->name,CHN_LEN) < 0) break; new= (struct chnname *)malloc((mtype)sizeof(struct chnname)); strncpy(new->name,name,CHN_LEN); new->name[CHN_LEN]= '\0'; new->users= users; new->next= c; if (p == NULL) head= new; else p->next= new; return(head); } /* OPENCHN: Open the chantab file for reading and returns the file descriptor. * (actually it only opens it on the first call. Later it just rewinds.) */ FILE *openchn() { static FILE *fp= NULL; /* Open the Channel Table */ if (fp == NULL) { if ((fp= fopen(opt[OPT_CHANTAB].str,"r")) == NULL) { err("cannot open %s\n", opt[OPT_CHANTAB].str); return(NULL); } } else rewind(fp); return(fp); } /* This returns true if the string s matches the pattern p. The pattern syntax * is similar to the shell's globbing mechanism, with ? matching any character, * [...] matching any character in the brackets, and * matching any sequence * of characters. Backslashes escape any of these special characters. This * version doesn't support {...} patterns, mainly because the kludgy way I * wrote it makes those a bit difficult. */ int patmatch(s,p) char *s, *p; { int found; char *q; while (*s != '\0') { switch (*p) { case '\0': /* End of pattern but not end of text -- fail */ return(0); case '?': /* '?' matches any one character */ break; case '*': /* '*' matches any sequence of characters */ if (*(p+1) == '\0') return(1); /* speeds common case */ return(patmatch(s,p+1) || patmatch(s+1,p)); case '[': /* [...] matches any character in the brackets */ found= 0; while (*(++p) != ']') { if (*p == '\\') p++; if (*p == '\0') return(0); if (*p == *s) {found= 1; break;} if (*(p+1) == '-' && *(q= p+2) != ']') { if (*q == '\\') q++; if (*q == '\0') return(0); if (*p < *s && *s <= *q) {found= 1; p= q; break;} p= q; } } if (!found) return(0); while (*(++p) != ']') { if (*p == '\\') p++; if (*p == '\0') return(0); } break; case '\\': /* '\x' matches character x */ p++; default: if (*s != *p) return(0); break; } p++; s++; } return(*p == '\0'); } set_debug() { long tm; if (debug) fclose(debug); if (opt[OPT_DEBUG].str[0] == '\0') opt[OPT_DEBUG].yes= 0; if (!opt[OPT_DEBUG].yes) { debug= NULL; err("debugging off\n"); return; } if ((debug= fopen(opt[OPT_DEBUG].str,"a")) == NULL) { fprintf(stderr,"cannot open debug file %s\n", opt[OPT_DEBUG].str); return; } tm= time((long *)0); fprintf(debug,"debug started %s",ctime(&tm)); printopts(debug,0,' ',"all"); fflush(debug); fprintf(stderr,"debugging output in %s\n",opt[OPT_DEBUG].str); }