/* * The WizPort * * This software is Copyright 1993, David Ljung. * Please read the file "docs/0.COPYRIGHT" * This version of this software is free for distribution * Do not distribute any parts of this package without the documentation. * Read the docs before attempting to use this software! * * David Ljung * ljung@cae.wisc.edu (until 8/94) * * Permanent Address: Current (till 8/94): * 2257 Clover Lane 310 South Bassett * Geneva, IL. 60134 Madison, WI. 53703 * (708) 232-4987 (608) 257-COWS * */ #include "wizshell.h" /* all should use putenv now #if defined(NeXT) /* some machines don't have putenv()! yeesh. */ /* procedure is declared in wizshell.h */ /* #include "putenv.c" #endif */ /* GLOBAL VARS */ envir_t envir; acc_t *read_acc, *write_acc; acc_t *wd_default, *can_read, *non_file_args; alias_t *aliases; int DEBUGON=0; /* set_prompt */ void set_prompt(str) char *str; { char *tmp,*pathat; tmp=str; pathat=NULL; while(*tmp!='\0') if(*(tmp++)=='%' && *tmp=='/') { pathat=tmp-1; break; } FREE(envir.prompt); if(envir.prompt2) FREE(envir.prompt2); envir.prompt2=NULL; if(pathat) { MK_STR(envir.prompt,pathat-str+1) strncpy(envir.prompt,str,pathat-str); envir.prompt[pathat-str]='\0'; if(envir.prompt2) FREE(envir.prompt2); pathat+=2; MK_STR(envir.prompt2,strlen(pathat)+1) strcpy(envir.prompt2,pathat); return; } MK_STR(envir.prompt,strlen(str)+1) strcpy(envir.prompt,str); } /* init_envir * sets up environment for start of shell */ void init_envir(interactive) int interactive; { int i; char *tmp; FILE *rcfp; set_prompt("WizShell] "); strcpy(WD,envir.home); /* start in home */ /* do real environment stuff */ wshputenv("MAIL="); MK_STR(tmp,6+BIN_DIR_LEN) sprintf(tmp,"PATH=%s",BIN_DIR); wshputenv(tmp); MK_STR(tmp,12+BIN_DIR_LEN); sprintf(tmp,"EDITOR=%s/vi",BIN_DIR); wshputenv(tmp); MK_STR(tmp,6+strlen(WD)) sprintf(tmp,"HOME=%s",WD+1); wshputenv(tmp); MK_STR(tmp,6+strlen(envir.login)) sprintf(tmp,"USER=%s",envir.login); wshputenv(tmp); MK_STR(tmp,9+strlen(envir.login)) sprintf(tmp,"LOGNAME=%s",envir.login); wshputenv(tmp); MK_STR(tmp,11+strlen(envir.login)) sprintf(tmp,"WSHLOGIN=%s",envir.login); wshputenv(tmp); MK_STR(tmp,8+strlen(WSH_FNAME)) sprintf(tmp,"SHELL=%s",WSH_FNAME); wshputenv(tmp); if(!cWD(0,interactive)) /* cd to home */ FATAL("Could not change to home directory") /* do history stuff */ envir.command_num=0; envir.current_hist=0; envir.history_size=0; envir.hist=NULL; set_history_size(START_HIST_SIZE); /* alias stuff */ aliases=NULL; /* read access files and exceptions */ get_acc(&write_acc,WRITE_ACC_FILE,envir.access); get_acc(&read_acc,READ_ACC_FILE,envir.access); get_acc(&wd_default,EXCEPTIONS_FILE,"WD_DEFAULT"); get_acc(&can_read,EXCEPTIONS_FILE,"CAN_READ"); get_acc(&non_file_args,EXCEPTIONS_FILE,"NON_FILE_ARGS"); /* now that we have access, source .wshrc */ source("/.wshrc.global",0); source(".wshrc",1); } int cWD(arg,dopwd) char *arg; int dopwd; { char *tmp,*tmp2; struct stat status; int result; if (!arg) { /* cd to home */ if(!(tmp2=getenv("HOME"))) { fprintf(stderr,"cd: Can't change to home directory (not set).\n"); return 0; } else { MK_STR(tmp,ROOT_DIR_LEN+strlen(tmp2)+3) strcpy(tmp,ROOT_DIR); *(tmp+ROOT_DIR_LEN-1)='/'; strcpy(tmp+ROOT_DIR_LEN,tmp2); } } else tmp=mk_path(arg); if(arg) tmp2=arg; else tmp2=tmp+ROOT_DIR_LEN-1; if((result=lstat(tmp,&status))==-1) { if(errno==ENOTDIR) fprintf(stderr,"Directory doesn't exist\n"); else if(errno==ENOENT) fprintf(stderr,"%s: No such file or directory\n",tmp2);else if(errno==EACCES) fprintf(stderr,"%s unreadable\n",tmp2);else if(errno==ELOOP) fprintf(stderr,"Too many symbolic links\n"); else if(errno==ENAMETOOLONG) fprintf(stderr,"Filename too long\n"); else fprintf(stderr,"Error in reading path: %s\n",tmp2); FREE(tmp); return 0; } if(status.st_mode & S_IFDIR) { strcpy(WD,(tmp+ROOT_DIR_LEN-1)); if(*WD=='/' && *(WD+1)=='\0') *WD='\0'; if(dopwd) if(*WD) fprintf(stderr,"%s\n",WD); else fprintf(stderr,"/\n"); #ifndef FROM_ROOT chdir(tmp); #endif return 1; } fprintf(stderr,"%s: Not a directory\n",arg); FREE(tmp); return 0; } /* sources a file. returns 0 if ok, else -1 if permission denied, 1 if no file */ int source(what,ck_acc) char *what; int ck_acc; /* check access? */ { char *tmp,*tmp2; FILE *fp; MK_STR(tmp,strlen(what)+1) strcpy(tmp,what); if(!(tmp=mk_path(tmp))) return; /* mk_path needs to be able to free its arg */ tmp2=ref_from(tmp); FREE(tmp); if(ck_acc && !valid_path(write_acc,tmp2) && !valid_path(read_acc,tmp2)) return -1; if(!(fp=fopen(tmp2,"r"))) return 1; while(parse_line(0,fp,0)); fclose(fp); return 0; } /* justfile * looks for '..' and '/' (for /bin/command name and -arglists) */ int justfile(com) char *com; { while(*com!='\0') { if(*com=='/' || (*com=='.' && *(com+1)=='.')) return 0; com++; } return 1; } /* str_add * copy "add" to end of str up to '\0' * CHANGE TO MACRO?? */ void str_add(str,add) char *str,*add; { str+=strlen(str); while(*str++=*add++); } /* eat_white * return pointer to next non-white space */ char *eat_white(str) char *str; { while(*str==' ' || *str=='\t') str++; return str; } /* do_comm * forks and executes the command */ int do_comm(comm) char *comm[MAX_ARGS]; { int pid,result,stat_loc[1]; struct stat status; if((result=lstat(comm[0],&status))==-1) { ERROR(comm[0]+BIN_DIR_LEN,"Command not found") return; } pid=fork(); if (pid==-1) FATAL("Error when forking") if (pid) { errno=0; while(wait(stat_loc)==-1 && errno==EINTR) ; /* if interrupt, wait again */ /* fprintf(stderr,"%d",*stat_loc); * * if(errno==EINTR) fprintf(stderr,"Interrupted\n"); * FATAL("Error waiting for child process") * } */ } else { if(DEBUGON)fprintf(stderr,"command to exec [%s]\n",comm[0]); execv(comm[0],comm); comm[0]+=BIN_DIR_LEN; switch(errno) { case EACCES: ERROR(comm[0],"Permission denied") break; case ELOOP: ERROR(comm[0],"Too many levels of symbolic links") break; case ENOEXEC: ERROR(comm[0],"Not an executable") break; default: ERROR(comm[0],"Could not execute") break; } exit(1); /* FATAL */ } } /* mk_comm * takes a string of tokens with whitespace and makes an array of tokens */ int mk_comm(buf,commh) char buf[]; char ***commh; { char **comm; char *tmp=buf,*start,*end,ch; int i=0,len=0,args=0; if(*buf==EOF) return 0; /* estimate (max) number of args and kill \r,\n and # */ while(*tmp!='\0') { tmp=eat_white(tmp); if(*tmp!='\r' && *tmp!='\n' && *tmp!='#' && *tmp!='\0') { args++; while(*tmp!=' ' && *tmp!='\t' && *tmp!='\r' && *tmp!='\n' && *tmp!='#' && *tmp!='\0') tmp++; } if(*tmp=='\r' || *tmp=='\n' || *tmp=='#') *tmp='\0'; } if((comm=(char **) CALLOC(args+2,sizeof(char *)))==NULL) FATAL("mk_comm: Ran out of memory") tmp=buf; i=0; /* Build command array */ tmp=eat_white(tmp); while(*tmp!='\0') { start=tmp; if(*tmp!='"' && *tmp!='\'' && *tmp!='`' && *tmp!='(') { while(*tmp!=' ' && *tmp!='\t' && *tmp!='\0') tmp++; end=tmp; } else { ch=*tmp++; if(ch=='(') ch=')'; start++; while(*tmp!=ch && *tmp++!='\0'); if(*tmp!=ch) { fprintf(stderr,"Unmatched %c.\n",ch); FREE(comm); return 0; } end=tmp++; } /* parsed quoted string */ len=end-start+1; MK_STR(comm[i],len) strncpy(comm[i],start,len-1); *(comm[i]+len)='\0'; i++; tmp=eat_white(tmp); } /* end while */ comm[i]=NULL; *commh=comm; return 1; } /* parse_line * returns 0 at "quit" */ int parse_line(interactive,fd,thisline) int interactive; FILE *fd; char *thisline; { char buffer[MAX_BUF+1],num[10],*line; char **comm,**commuse; int i,result=0,numargs,numfileargs=0,needtofree=0,*file_args; acc_t *acc; /* this is where the interrupt gets noticed: if(envir.gotintr!=-1) fprintf(stderr,"Got a user interrupt!\n"); */ if(interactive) if(envir.prompt2) if(WD[0]!='\0') fprintf(stdout,"%s%s%s",envir.prompt,WD,envir.prompt2); else fprintf(stdout,"%s/%s",envir.prompt,envir.prompt2); else fprintf(stdout,"%s",envir.prompt); if(thisline) if(line=replaces(thisline)) needtofree=1; else line=thisline; else { /* not thisline */ clearerr(fd); /* reset ferror() & feof() */ errno=0; while(fgets(buffer,MAX_BUF,fd)==NULL && errno==EINTR) ; if(feof(fd)) { if (!interactive) return 0; if(envir.loopy_eofs++>0) return 0; fprintf(stdout,"\nUse \"logout\" to logout\n"); clearerr(fd); /* reset feof() */ if(feof(fd)) { fprintf(stderr,"Agh! Your clearerr() library call doesn't work!\n"); fprintf(stderr,"Sorry, but I gotta die.\nLog in again.\n"); return 0; } return 1; } else envir.loopy_eofs=0; if(line=replaces(buffer)) needtofree=1; else line=buffer; } if(catchline(line)) return 1; /* catchline adds to history */ i=mk_comm(line,&comm); if(needtofree) FREE(line); if(!i || !comm[0]) return 1; i=1; if(DEBUGON) while(comm[i]) fprintf(stderr,"%d: [%s]\n",i,comm[i++]); result=catchcomm(comm,interactive); if(result==-1) return 0; /* shell is done */ if(result==2) return 1; /* caught - don't add to history */ if(envir.history_size) add_history(comm); if(result==1) return 1; /* caught */ numargs=1; i=0; while(comm[i++]) numargs++; if((commuse=(char **) CALLOC(numargs+3,sizeof(char *)))==NULL) FATAL("couldn't make commuse array") if((file_args=(int *) CALLOC(numargs+2,sizeof(int)))==NULL) FATAL("couldn't make file_args array") MK_STR(commuse[0],BIN_DIR_LEN+3+strlen(comm[0])) strcpy(commuse[0],BIN_DIR); *(commuse[0]+BIN_DIR_LEN-1)='/'; strcpy(commuse[0]+BIN_DIR_LEN,comm[0]); if(!justfile(commuse[0]+BIN_DIR_LEN)) { ERROR(commuse[0]+BIN_DIR_LEN,"Cannot have '..' or '/' in command name"); FREE(commuse); FREE(comm); return 1; } if(DEBUGON)fprintf(stderr,"COMMAND: %s\n",commuse[0]); i=1; while(comm[i]) { if(*comm[i]=='-') { MK_STR(commuse[i],strlen(comm[i])+1) strcpy(commuse[i],comm[i]); file_args[i]=0; if(DEBUGON)fprintf(stderr,"FLAGS: %d [%s]\n",i+1,commuse[i]); i++; continue; } if(acc=find_token(non_file_args,comm[0])) { sprintf(num,"%d",i); if(!acc->down || find_token(acc,num)) { MK_STR(commuse[i],strlen(comm[i])+1) strcpy(commuse[i],comm[i]); file_args[i]=0; if(DEBUGON)fprintf(stderr,"USING(not file): %d [%s]\n",i+1,commuse[i]); i++; continue; } } if(!(comm[i]=mk_path(comm[i]))) return 1; /* bad path */ commuse[i]=ref_from(comm[i]); file_args[i]=1; numfileargs++; if(DEBUGON)fprintf(stderr,"USING: %d [%s]\n",i+1,commuse[i]); i++; } numargs--; if(!numfileargs && find_token(wd_default,comm[0])) { if(DEBUGON)fprintf(stderr,"No file args. Using WD\n"); MK_STR(comm[numargs],ROOT_DIR_LEN+strlen(WD)+2) strcpy(comm[numargs],ROOT_DIR); if(*WD) str_add(comm[numargs],WD); MK_STR(commuse[numargs],strlen(WD)+2); if(*WD) strcpy(commuse[numargs],WD+1); else strcpy(commuse[numargs],"."); file_args[numargs]=1; commuse[++numargs]=0; comm[numargs]=0; } if(ck_access(comm,file_args)) do_comm(commuse); i=0; while(comm[i]) FREE(comm[i++]); i=0; while(commuse[i]) FREE(commuse[i++]); FREE(comm); FREE(commuse); FREE(file_args); return 1; } /* void handlebreak(sig) int sig; { signal(sig,SIG_DFL); signal(sig,SIG_IGN); fprintf(stderr,"got ctrl-c\n"); signal(SIGINT,handlebreak); parse_line(1,stdin,0); } */ /* This will be used in the future (as well as SIGUSR2) */ void usersig(sig) int sig; { signal(sig,SIG_DFL); /* reset */ signal(sig,SIG_IGN); /* ignore until done */ /* you could set a flag here and then check at the beginning of parse_line */ signal(sig,usersig); } /* Print the usage error message and exit */ void usage() { fprintf(stderr,"usage: wsh [-i|-l] [sourcefile]\n"); fprintf(stderr," where options are:\n"); fprintf(stderr," -i interactive shell (default)\n"); fprintf(stderr," -c (single) following argument is command\n"); fprintf(stderr," -l login "); fprintf(stderr,"(otherwise use WSHLOGIN environment var\n"); fprintf(stderr," sourcefile file to take commands from "); fprintf(stderr,"(must be logged in)\n\n"); exit(-1); /* fatal error */ } /* Parse the options and arguments as seen in usage() */ void parse_args(argc,argv,envp,interactive,getlogin,fh,line) int argc; char **argv, **envp; int *interactive,*getlogin; FILE **fh; char **line; { int flag,algpos; *interactive=1; /* default to interactive */ *getlogin=0; *fh=stdin; /* default to stdin */ if(argc==1) return; if(argc>3) usage(); if(argv[1][0]=='-') { /*flags*/ if(argv[1][1]=='i') *interactive=1; /* default anyways :-) */ else if(argv[1][1]=='l') *getlogin=1; else if(argv[1][1]=='c') { if(argc!=3) usage(); *line=argv[2]; *interactive=0; } else usage(); /* no other flags */ } else { /* if not flag, assume source file */ *interactive=0; *getlogin=0; if (!(*fh=fopen(argv[1],"r"))) { fprintf(stderr,"Could not open file: %s\n",argv[2]); usage(); } } } /* main * do it. */ int main(argc, argv, envp) int argc; char **argv, **envp; { acc_t *tmp; int interactive,getlogin,u; FILE *fp; char *line=0; /* for shell escapes */ #ifdef FROM_ROOT if(chdir(ROOT_DIR)) FATAL("Could not find mud root directory"); #endif parse_args(argc,argv,envp,&interactive,&getlogin,&fp,&line); #ifdef MAX_USERS if((u=count_users())>=MAX_USERS) { fprintf(stdout,"Too many logins. Try again later. Current users:\n",u); free_who(print_utmp(read_utmp(0),0)); return; } if(u) fprintf(stdout,"Number of users: %d/%d\n",u,MAX_USERS); else fprintf(stdout,"No users. (Maximum: %d)\n",MAX_USERS); #endif if(!login(getlogin)) return; if(getlogin) add_uwtmp(); init_envir(interactive); if(interactive) { source("/.login.global",0); source(".login",1); } signal(SIGINT, SIG_IGN); /* This will be used later, as well as SIGUSR2 */ signal(SIGUSR1, usersig); if(line) parse_line(interactive,0,line); else while(parse_line(interactive,fp,0)); fclose(fp); signal(SIGINT, SIG_DFL); if(getlogin) source("~/.logout",1); if(interactive) fprintf(stderr,"Exiting WizShell.\n"); }