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

  1. /*
  2.  * Copyright 1989 Lars Huttar
  3.  *
  4.  * Permission to use, copy, modify, and distribute this software and its
  5.  * documentation for any purpose and without fee is hereby granted, provided
  6.  * that the above copyright notice appear in all copies and that both that
  7.  * copyright notice and this permission notice appear in supporting
  8.  * documentation, and that the name of Lars Huttar not be used in advertising
  9.  * or publicity pertaining to distribution of the software without specific,
  10.  * written prior permission.  Lars Huttar makes no representations about the
  11.  * suitability of this software for any purpose.  It is provided "as is"
  12.  * without express or implied warranty.
  13.  *
  14.  * LARS HUTTAR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  15.  * INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
  16.  * SHALL LARS HUTTAR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
  17.  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  18.  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  19.  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
  20.  * THIS SOFTWARE.
  21.  *
  22.  * Author:  Lars Huttar, Oberlin College
  23.  *          huttar@occs.oberlin.edu
  24.  *          slh6559@oberlin.bitnet
  25.  */
  26.  
  27. #include <stdio.h>
  28. #include <sys/param.h>
  29.    /*  ^^ defines MAXHOSTNAMELEN */
  30. #include <sys/time.h>
  31. #include <errno.h>
  32. #include <pwd.h>
  33.  
  34. #ifndef L_cuserid
  35. #define L_cuserid 50
  36. #endif
  37. int errno;
  38.  
  39. #define MAXPNLEN 50  /* That's being generous. */
  40. static char progname[MAXPNLEN], loginname[L_cuserid];
  41.    /* L_cuserid is defined in stdio.h on some systems,
  42.       and is the max length of login names. */
  43. int lognamlen;
  44.  
  45.  
  46. usage()
  47. {  fprintf(stderr, "Usage: %s\n", progname);
  48.    exit(1);
  49. }
  50.  
  51. main(argc, argv)
  52. int argc;
  53. char **argv;
  54. {  char *ks, *getlogin();
  55.    struct passwd *kp;
  56.  
  57. #ifdef DEBUG
  58.    kp=getpwuid(geteuid());
  59.    printf("euid: %d %s\n", geteuid(), kp->pw_name);
  60. #endif
  61.  
  62.    if (chdir(ORACLEDIR))   /* Try to go to the directory. */
  63.       my_error("error in main(): Couldn't chdir to ", ORACLEDIR);
  64.  
  65.    if (!(ks = getlogin()))
  66.    {  if (!(kp = getpwuid(getuid())))
  67.          my_error("error in main(): Couldn't get username.", "");
  68.       else strcpy(loginname, kp->pw_name);
  69.    }
  70.    else strcpy(loginname, ks);
  71.    lognamlen=strlen(loginname);
  72.  
  73.    strncpy(progname, argv[0], MAXPNLEN-1);
  74.    progname[MAXPNLEN-1]=NULL;
  75.  
  76.    if (argc>1) usage();
  77.  
  78.    get_question();
  79.    get_answer();
  80.    update_records();
  81.    farewell();
  82.  
  83.  
  84. }
  85.  
  86.  
  87. #define MAXDATESIZE 30
  88. #define MAXFNSIZE (MAXHOSTNAMELEN+L_cuserid+MAXDATESIZE+6)
  89. static char q_filename[MAXFNSIZE], a_filename[MAXFNSIZE];
  90.  
  91. #define MAXLINELEN 141
  92.  
  93. /** This function, get_question(), asks the user to type in a question.
  94.  ** The question is written line by line to a temporary file x_blahblahblah.
  95.  */
  96.  
  97. get_question()
  98. {  char line[MAXLINELEN];
  99.    FILE *outfp;
  100.    register int lines=0;
  101.  
  102.    make_filename();
  103.    q_filename[0]='x';   /* This means it's a temporary question file. */
  104.  
  105.    if (!(outfp=fopen(q_filename, "w")))
  106.       my_error("error in get_question(): Couldn't open file for writing: ",
  107.         q_filename);
  108.  
  109.    puts("I am the oracle.  I can answer any question");
  110.    puts("in roughly constant time.  Please type in");
  111.    puts("your question.  Enter a blank line when");
  112.    puts("you are finished (don't use ^D!).\n");
  113.  
  114.    do
  115.    {  putchar('>'); putchar(' ');
  116.       if (!fgets(line, MAXLINELEN-1, stdin) || line[0]=='\n') break;
  117.       lines=1;
  118.       line[MAXLINELEN-1]=0;
  119.       fputs(line, outfp);
  120.    } while (!feof(stdin));
  121.  
  122.    clearerr(stdin);
  123.    fclose(outfp);
  124.  
  125.    if (!lines)
  126.    {  puts("Ask a null question, get a null answer.");
  127.       if (unlink(q_filename))
  128.          my_error("error in get_question: Couldn't unlink ", q_filename);
  129.       exit(1);
  130.    }
  131. }
  132.  
  133.  
  134. int got_answer=0;
  135.  
  136. get_answer()
  137. {  FILE *recfp, *quesfp, *ansfp;
  138.    char line[MAXLINELEN];
  139.    register int lines=0, num;
  140.  
  141.    puts("\nHmmm.  I'll have to think about that one for a while.");
  142.  
  143.   /** Look for a suitable question file. */
  144.  
  145.    if (lock_directory()) return;
  146.       /* Couldn't lock the directory, so don't require an answer. */
  147.  
  148.    system("ls -rt q_* > record 2>&-"); /* stderr goes to /dev/null */
  149.    if (!(recfp=fopen("record", "r")))  /* Open the file containing list of
  150.                                         * question files */
  151.       my_error("in get_answer(): fopen(\"record\", \"r\") gave error.", "");
  152.  
  153.    do
  154.    {  
  155.       fgets(a_filename, MAXFNSIZE, recfp);
  156.       if (feof(recfp)) break;
  157.  
  158.       if (strncmp(a_filename+2, loginname, lognamlen))
  159.          break;   /* found a question from a different user */
  160.    } while (!feof(recfp));
  161.  
  162.    if (feof(recfp))  /* Didn't find a suitable question file. */
  163.    {  fclose(recfp);
  164.       unlock_directory();
  165.       return;
  166.    }
  167.    fclose(recfp);
  168.  
  169.    a_filename[strlen(a_filename)-1]=(char)NULL; /* Take away final newline */
  170.  
  171.    {  char nufilename[MAXFNSIZE];
  172.  
  173.       strcpy(nufilename, a_filename);
  174.       nufilename[0]='t';   /* Rename it so other oracles will not answer it. */
  175.       if (rename(a_filename, nufilename))
  176.          my_error("Error in get_answer() renaming file ", a_filename);
  177.    }
  178.  
  179.  
  180.    unlock_directory();
  181.  
  182.  
  183.   /** Ask user to answer question. */
  184.  
  185.    a_filename[0]='t';
  186.    if (!(quesfp=fopen(a_filename, "r")))
  187.       my_error("Error in get_answer() opening file for input: ", a_filename);
  188.  
  189.    a_filename[0]='a';
  190.    if (!(ansfp=fopen(a_filename, "w")))
  191.       my_error("Error in get_answer() opening file for output: ", a_filename);
  192.  
  193.    fputs("The oracle has pondered your question deeply.\n", ansfp);
  194.    fputs("Your question was:\n\n", ansfp);
  195.  
  196.    puts("Meanwhile, I'd like you to answer a question for me:\n");
  197.  
  198.    while (!feof(quesfp))
  199.    {  static int i=0;
  200.  
  201.       fgets(line, MAXLINELEN, quesfp);
  202.       if (!feof(quesfp))
  203.       {  fputs("> ", stdout); fputs(line, stdout);
  204.          fputs("> ", ansfp); fputs(line, ansfp);
  205.       }
  206.       if (++i % 15 == 0) get_return("Press RETURN for More--");
  207.    }
  208.    fclose(quesfp);
  209.    puts("\nWhat would you say?\n");
  210.    fputs("\nAnd in response, thus spake the oracle:\n\n", ansfp);
  211.  
  212.    do
  213.    {  putchar(')'); putchar(' ');
  214.       fgets(line, MAXLINELEN-1, stdin);
  215.       if (line[0]=='\n' && lines)
  216.          break;
  217.       lines++;
  218.       line[MAXLINELEN-1]=0;
  219.       fputs(lines%2 ? "} " : "{ ", ansfp); fputs(line, ansfp);
  220.    } while (!feof(stdin));
  221.  
  222.    {  static char *objects[]=
  223.          {"cents", "of your children", "dollars", "big kisses",
  224.           "years of slavery", "minutes of life", "newt's eyes",
  225.           "quarts of soy sauce", "cases of root beer"};
  226.       
  227.       num=rand()+getpid()+getuid()+getppid();
  228.       fprintf(ansfp, "\n\nYou owe the oracle %d %s.\n",
  229.            num%4+2, objects[num%9]);
  230.    }
  231.  
  232.    fclose(ansfp);
  233.  
  234.    switch(num%5)
  235.    {  case 1: puts("\nHa!  What kind of answer is that?"); break;
  236.       case 2: puts("\nNow why couldn't I think of that?"); break;
  237.       case 3: puts("\nOk, good enough.  You pass."); break;
  238.       default: puts("\nThank you!  That one's been troubling me.");
  239.    }
  240.  
  241.    got_answer=1;
  242. }
  243.  
  244.  
  245. #define WAITLIMIT 5
  246.  
  247. /** This function, lock_directory, tries to lock the current directory
  248.  ** by creating a file "lockfile",
  249.  ** and returns 0 on success, 1 on failure. */
  250. lock_directory()
  251. {  register int lockfd, i=0;
  252.  
  253.    for (i=0; i<WAITLIMIT; i++)
  254.  
  255.    {  if ((lockfd=creat("lockfile", 0)) == -1)
  256.       {  if (errno != EACCES)
  257.             my_error("Error in lock_directory(): creat(\"lockfile\");", "");
  258.       }
  259.       else
  260.       {  if (close(lockfd) == -1)
  261.             my_error("Error in lock_directory(): closing lockfile","");
  262.          else return(0);
  263.       }
  264.  
  265. #ifdef DEBUG
  266.       if (i==0) printf("Waiting for lock... 1");
  267.       else printf(", %d", i+1);
  268.       fflush(stdout);
  269. #endif
  270.       sleep(1);   /* Is this too long?  usleep() may be more appropriate. */
  271.    }
  272.  
  273.    return(1);
  274. }
  275.  
  276.  
  277. /** This function unlocks the current directory. */
  278. unlock_directory()
  279. {
  280. #ifdef DEBUG
  281.    get_return("Press RETURN to unlock the directory.");
  282. #endif
  283.  
  284.    if (unlink("lockfile") == -1)
  285.       my_error("Error in unlink() while trying to unlock directory.", "");
  286. }
  287.  
  288.  
  289. update_records()
  290. {  char command[L_cuserid+MAXFNSIZE+10], olduser[L_cuserid], newqfn[MAXFNSIZE];
  291.    register int i;
  292.  
  293.    strcpy(newqfn, q_filename);
  294.    newqfn[0]='q';
  295.    if (rename(q_filename, newqfn))
  296.       my_error("update_records(): Error renaming file ", newqfn);
  297.  
  298.    if (!got_answer) return;
  299.    
  300.    for (i=2; a_filename[i]!='_'; i++)
  301.       olduser[i-2]=a_filename[i];
  302.    olduser[i-2]=0;
  303.  
  304.    if (setreuid(geteuid(), -1))  /* Make message be From the oracle owner. */
  305.       my_error("Error in update_records(), setreuid();", "");
  306.    sprintf(command, "mail %s < %s", olduser, a_filename);
  307.    if (i=system(command))
  308.    {  static char exit_code[5];
  309.       sprintf(exit_code, "%d", i);
  310.       my_error("Trouble mailing reply.  Exit code: ", exit_code);
  311.    }
  312.  
  313.  
  314. #ifndef LOG
  315.    if (unlink(a_filename))    /* Remove the answer file (which includes
  316.              a copy of the question)*/
  317.       my_error("update_records(): Error unlinking ", a_filename);
  318. #endif
  319.    a_filename[0]='t';
  320.    if (unlink(a_filename))    /* Remove the old question file */
  321.       my_error("update_records(): Error unlinking ", a_filename);
  322.  
  323. }
  324.  
  325.  
  326. /** The function make_filename constructs a string of the form
  327.  ** q_user_date_host_processid and writes it the global string "q_filename". */
  328. make_filename()
  329. {  char hostname[MAXHOSTNAMELEN], date[MAXDATESIZE];
  330.    long time; /* no see. */
  331.    
  332.    int pid;
  333.    struct timeval tv;
  334.    struct timezone tz;
  335.  
  336.    if (gethostname(hostname, MAXHOSTNAMELEN)) 
  337.       my_error("make_filename(): Had trouble getting hostname;", "");
  338.    
  339.  
  340.    pid=getpid();  /* Get the process id. */
  341.    
  342.    time=gettimeofday(&tv, &tz);  /* Get the number of seconds since
  343.                                     that unforgettable New Year's party */
  344.    if (time == -1) tv.tv_sec=rand();   /* Sure this isn't error-proof... */
  345.    strncpy(date, ctime(&(tv.tv_sec))+4, 20); /* +4 to avoid day of the week. */
  346.    date[20]=NULL; space_to__(date); /* Change spaces to _. */
  347.  
  348.    sprintf(q_filename, "q_%s_%s_%s_%d", loginname, date, hostname, pid);
  349.       /* Just trying to make a unique filename. */
  350. }
  351.  
  352. space_to__(string)
  353. char *string;
  354. {  register char *p;
  355.  
  356.    for (p=string; *p; p++)
  357.       if (*p==' ') *p='_';
  358. }
  359.  
  360.  
  361.  
  362. /** The following function says goodbye and advertises
  363.  ** the program. */
  364. farewell()
  365. {
  366.    puts("\nYour answer (and your bill) will be mailed to you shortly.");
  367.    puts("Tell all your friends to Ask the Oracle!");
  368. }
  369.  
  370.  
  371. my_error(mess1, mess2)
  372. char *mess1, *mess2;
  373. {  printf("%s%s\n", mess1, mess2);
  374.  
  375. #ifdef DEBUG
  376.    printf("errno: %d\n", errno);
  377. #endif
  378.  
  379. /* printf("EACCES %d  EBADF %d  EAGAIN %d  EINTR %d  ENOLCK %d\n",/**deleteme*/
  380. /*    EACCES, EBADF, EAGAIN, EINTR, ENOLCK);/**deleteme*/
  381.    perror(progname);
  382.    exit(1);
  383. }
  384.  
  385.  
  386. get_return(prompt)
  387. char *prompt;
  388. {  fputs(prompt, stdout);
  389.    while (getchar()!='\n');
  390. }
  391.  
  392.