home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 January
/
usenetsourcesnewsgroupsinfomagicjanuary1994.iso
/
sources
/
misc
/
volume7
/
oracle
/
oracle.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-08-05
|
11KB
|
392 lines
/*
* 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');
}