home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright 1989 Lars Huttar
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose and without fee is hereby granted, provided
- * that the above copyright notice appear in all copies and that both that
- * copyright notice and this permission notice appear in supporting
- * documentation, and that the name of Lars Huttar not be used in advertising
- * or publicity pertaining to distribution of the software without specific,
- * written prior permission. Lars Huttar makes no representations about the
- * suitability of this software for any purpose. It is provided "as is"
- * without express or implied warranty.
- *
- * LARS HUTTAR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
- * SHALL LARS HUTTAR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
- * THIS SOFTWARE.
- *
- * Author: Lars Huttar, Oberlin College
- * huttar@occs.oberlin.edu
- * slh6559@oberlin.bitnet
- */
-
- #include <stdio.h>
- #include <sys/param.h>
- /* ^^ defines MAXHOSTNAMELEN */
- #include <sys/time.h>
- #include <errno.h>
- #include <pwd.h>
-
- #ifndef L_cuserid
- #define L_cuserid 50
- #endif
- int errno;
-
- #define MAXPNLEN 50 /* That's being generous. */
- static char progname[MAXPNLEN], loginname[L_cuserid];
- /* L_cuserid is defined in stdio.h on some systems,
- and is the max length of login names. */
- int lognamlen;
-
-
- usage()
- { fprintf(stderr, "Usage: %s\n", progname);
- exit(1);
- }
-
- main(argc, argv)
- int argc;
- char **argv;
- { char *ks, *getlogin();
- struct passwd *kp;
-
- #ifdef DEBUG
- kp=getpwuid(geteuid());
- printf("euid: %d %s\n", geteuid(), kp->pw_name);
- #endif
-
- if (chdir(ORACLEDIR)) /* Try to go to the directory. */
- my_error("error in main(): Couldn't chdir to ", ORACLEDIR);
-
- if (!(ks = getlogin()))
- { if (!(kp = getpwuid(getuid())))
- my_error("error in main(): Couldn't get username.", "");
- else strcpy(loginname, kp->pw_name);
- }
- else strcpy(loginname, ks);
- lognamlen=strlen(loginname);
-
- strncpy(progname, argv[0], MAXPNLEN-1);
- progname[MAXPNLEN-1]=NULL;
-
- if (argc>1) usage();
-
- get_question();
- get_answer();
- update_records();
- farewell();
-
-
- }
-
-
- #define MAXDATESIZE 30
- #define MAXFNSIZE (MAXHOSTNAMELEN+L_cuserid+MAXDATESIZE+6)
- static char q_filename[MAXFNSIZE], a_filename[MAXFNSIZE];
-
- #define MAXLINELEN 141
-
- /** This function, get_question(), asks the user to type in a question.
- ** The question is written line by line to a temporary file x_blahblahblah.
- */
-
- get_question()
- { char line[MAXLINELEN];
- FILE *outfp;
- register int lines=0;
-
- make_filename();
- q_filename[0]='x'; /* This means it's a temporary question file. */
-
- if (!(outfp=fopen(q_filename, "w")))
- my_error("error in get_question(): Couldn't open file for writing: ",
- q_filename);
-
- puts("I am the oracle. I can answer any question");
- puts("in roughly constant time. Please type in");
- puts("your question. Enter a blank line when");
- puts("you are finished (don't use ^D!).\n");
-
- do
- { putchar('>'); putchar(' ');
- if (!fgets(line, MAXLINELEN-1, stdin) || line[0]=='\n') break;
- lines=1;
- line[MAXLINELEN-1]=0;
- fputs(line, outfp);
- } while (!feof(stdin));
-
- clearerr(stdin);
- fclose(outfp);
-
- if (!lines)
- { puts("Ask a null question, get a null answer.");
- if (unlink(q_filename))
- my_error("error in get_question: Couldn't unlink ", q_filename);
- exit(1);
- }
- }
-
-
- int got_answer=0;
-
- get_answer()
- { FILE *recfp, *quesfp, *ansfp;
- char line[MAXLINELEN];
- register int lines=0, num;
-
- puts("\nHmmm. I'll have to think about that one for a while.");
-
- /** Look for a suitable question file. */
-
- if (lock_directory()) return;
- /* Couldn't lock the directory, so don't require an answer. */
-
- system("ls -rt q_* > record 2>&-"); /* stderr goes to /dev/null */
- if (!(recfp=fopen("record", "r"))) /* Open the file containing list of
- * question files */
- my_error("in get_answer(): fopen(\"record\", \"r\") gave error.", "");
-
- do
- {
- fgets(a_filename, MAXFNSIZE, recfp);
- if (feof(recfp)) break;
-
- if (strncmp(a_filename+2, loginname, lognamlen))
- break; /* found a question from a different user */
- } while (!feof(recfp));
-
- if (feof(recfp)) /* Didn't find a suitable question file. */
- { fclose(recfp);
- unlock_directory();
- return;
- }
- fclose(recfp);
-
- a_filename[strlen(a_filename)-1]=(char)NULL; /* Take away final newline */
-
- { char nufilename[MAXFNSIZE];
-
- strcpy(nufilename, a_filename);
- nufilename[0]='t'; /* Rename it so other oracles will not answer it. */
- if (rename(a_filename, nufilename))
- my_error("Error in get_answer() renaming file ", a_filename);
- }
-
-
- unlock_directory();
-
-
- /** Ask user to answer question. */
-
- a_filename[0]='t';
- if (!(quesfp=fopen(a_filename, "r")))
- my_error("Error in get_answer() opening file for input: ", a_filename);
-
- a_filename[0]='a';
- if (!(ansfp=fopen(a_filename, "w")))
- my_error("Error in get_answer() opening file for output: ", a_filename);
-
- fputs("The oracle has pondered your question deeply.\n", ansfp);
- fputs("Your question was:\n\n", ansfp);
-
- puts("Meanwhile, I'd like you to answer a question for me:\n");
-
- while (!feof(quesfp))
- { static int i=0;
-
- fgets(line, MAXLINELEN, quesfp);
- if (!feof(quesfp))
- { fputs("> ", stdout); fputs(line, stdout);
- fputs("> ", ansfp); fputs(line, ansfp);
- }
- if (++i % 15 == 0) get_return("Press RETURN for More--");
- }
- fclose(quesfp);
- puts("\nWhat would you say?\n");
- fputs("\nAnd in response, thus spake the oracle:\n\n", ansfp);
-
- do
- { putchar(')'); putchar(' ');
- fgets(line, MAXLINELEN-1, stdin);
- if (line[0]=='\n' && lines)
- break;
- lines++;
- line[MAXLINELEN-1]=0;
- fputs(lines%2 ? "} " : "{ ", ansfp); fputs(line, ansfp);
- } while (!feof(stdin));
-
- { static char *objects[]=
- {"cents", "of your children", "dollars", "big kisses",
- "years of slavery", "minutes of life", "newt's eyes",
- "quarts of soy sauce", "cases of root beer"};
-
- num=rand()+getpid()+getuid()+getppid();
- fprintf(ansfp, "\n\nYou owe the oracle %d %s.\n",
- num%4+2, objects[num%9]);
- }
-
- fclose(ansfp);
-
- switch(num%5)
- { case 1: puts("\nHa! What kind of answer is that?"); break;
- case 2: puts("\nNow why couldn't I think of that?"); break;
- case 3: puts("\nOk, good enough. You pass."); break;
- default: puts("\nThank you! That one's been troubling me.");
- }
-
- got_answer=1;
- }
-
-
- #define WAITLIMIT 5
-
- /** This function, lock_directory, tries to lock the current directory
- ** by creating a file "lockfile",
- ** and returns 0 on success, 1 on failure. */
- lock_directory()
- { register int lockfd, i=0;
-
- for (i=0; i<WAITLIMIT; i++)
-
- { if ((lockfd=creat("lockfile", 0)) == -1)
- { if (errno != EACCES)
- my_error("Error in lock_directory(): creat(\"lockfile\");", "");
- }
- else
- { if (close(lockfd) == -1)
- my_error("Error in lock_directory(): closing lockfile","");
- else return(0);
- }
-
- #ifdef DEBUG
- if (i==0) printf("Waiting for lock... 1");
- else printf(", %d", i+1);
- fflush(stdout);
- #endif
- sleep(1); /* Is this too long? usleep() may be more appropriate. */
- }
-
- return(1);
- }
-
-
- /** This function unlocks the current directory. */
- unlock_directory()
- {
- #ifdef DEBUG
- get_return("Press RETURN to unlock the directory.");
- #endif
-
- if (unlink("lockfile") == -1)
- my_error("Error in unlink() while trying to unlock directory.", "");
- }
-
-
- update_records()
- { char command[L_cuserid+MAXFNSIZE+10], olduser[L_cuserid], newqfn[MAXFNSIZE];
- register int i;
-
- strcpy(newqfn, q_filename);
- newqfn[0]='q';
- if (rename(q_filename, newqfn))
- my_error("update_records(): Error renaming file ", newqfn);
-
- if (!got_answer) return;
-
- for (i=2; a_filename[i]!='_'; i++)
- olduser[i-2]=a_filename[i];
- olduser[i-2]=0;
-
- if (setreuid(geteuid(), -1)) /* Make message be From the oracle owner. */
- my_error("Error in update_records(), setreuid();", "");
- sprintf(command, "mail %s < %s", olduser, a_filename);
- if (i=system(command))
- { static char exit_code[5];
- sprintf(exit_code, "%d", i);
- my_error("Trouble mailing reply. Exit code: ", exit_code);
- }
-
-
- #ifndef LOG
- if (unlink(a_filename)) /* Remove the answer file (which includes
- a copy of the question)*/
- my_error("update_records(): Error unlinking ", a_filename);
- #endif
- a_filename[0]='t';
- if (unlink(a_filename)) /* Remove the old question file */
- my_error("update_records(): Error unlinking ", a_filename);
-
- }
-
-
- /** The function make_filename constructs a string of the form
- ** q_user_date_host_processid and writes it the global string "q_filename". */
- make_filename()
- { char hostname[MAXHOSTNAMELEN], date[MAXDATESIZE];
- long time; /* no see. */
-
- int pid;
- struct timeval tv;
- struct timezone tz;
-
- if (gethostname(hostname, MAXHOSTNAMELEN))
- my_error("make_filename(): Had trouble getting hostname;", "");
-
-
- pid=getpid(); /* Get the process id. */
-
- time=gettimeofday(&tv, &tz); /* Get the number of seconds since
- that unforgettable New Year's party */
- if (time == -1) tv.tv_sec=rand(); /* Sure this isn't error-proof... */
- strncpy(date, ctime(&(tv.tv_sec))+4, 20); /* +4 to avoid day of the week. */
- date[20]=NULL; space_to__(date); /* Change spaces to _. */
-
- sprintf(q_filename, "q_%s_%s_%s_%d", loginname, date, hostname, pid);
- /* Just trying to make a unique filename. */
- }
-
- space_to__(string)
- char *string;
- { register char *p;
-
- for (p=string; *p; p++)
- if (*p==' ') *p='_';
- }
-
-
-
- /** The following function says goodbye and advertises
- ** the program. */
- farewell()
- {
- puts("\nYour answer (and your bill) will be mailed to you shortly.");
- puts("Tell all your friends to Ask the Oracle!");
- }
-
-
- my_error(mess1, mess2)
- char *mess1, *mess2;
- { printf("%s%s\n", mess1, mess2);
-
- #ifdef DEBUG
- printf("errno: %d\n", errno);
- #endif
-
- /* printf("EACCES %d EBADF %d EAGAIN %d EINTR %d ENOLCK %d\n",/**deleteme*/
- /* EACCES, EBADF, EAGAIN, EINTR, ENOLCK);/**deleteme*/
- perror(progname);
- exit(1);
- }
-
-
- get_return(prompt)
- char *prompt;
- { fputs(prompt, stdout);
- while (getchar()!='\n');
- }
-
-