home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume8 / hint / part01 / main.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-08-24  |  10.6 KB  |  503 lines

  1. /* main.c -- argument parser and control loops
  2.    Copyright (C) 1989 David MacKenzie
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 1, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
  17.  
  18. /* hint - send a one-line message to other users
  19.  
  20.    Usage: hint [-fp] [-d sec] user|tty[,user|tty...] [message]
  21.       hint -y [-bs] [-T terminal]
  22.       hint -n
  23.       hint -V
  24.       hint
  25.  
  26.    Options:
  27.    -f    make hint last forever, until overwritten
  28.    -p    put sender's name at beginning of hint instead of end
  29.    -d    duration in seconds (5 <= sec <= 60) before hint is erased (default 10)
  30.  
  31.    -y    enable receipt of hints
  32.    -b    don't use a visual bell (can still use audible bell)
  33.    -s    don't use an audible bell (can still use visual bell)
  34.    -T    specify terminal type, overriding $TERM
  35.  
  36.    -n    disable receipt of hints
  37.  
  38.    -V    display program version number
  39.  
  40.    With no arguments, displays the current hint status.
  41.  
  42.    David MacKenzie <edf@rocky2.rockefeller.edu>
  43.    Latest revision: 08/24/89 */
  44.  
  45. #define ALLOCATE        /* Allocate global variables in hint.h. */
  46.  
  47. #include "hint.h"
  48.  
  49. /* Hint status to set if 'y' or 'n'. */
  50. static int new_stats;
  51.  
  52. /* The hint message can be no longer than this. */
  53. static int length_limit;
  54.  
  55. /* Head of linked list of ttys to hint. */
  56. static struct ttylist *tty_list;
  57.  
  58. int
  59. main (argc, argv)
  60.      int argc;
  61.      char **argv;
  62. {
  63.   extern int optind;
  64.   struct ttylist *tp;        /* A ttylist entry. */
  65.   char temp_message[MAX_MESSAGE];    /* Hint read from tty. */
  66.   int i;            /* To reset `optind' between uses. */
  67.  
  68.   program_name = argv[0] = basename (argv[0]);
  69.   new_stats = 0;
  70.   length_limit = MAX_MESSAGE - 1;
  71.  
  72.   if (argc == 1)
  73.     {
  74.       show_status ();
  75.       exit (0);
  76.     }
  77.  
  78.   if (chdir ("/dev") == -1)
  79.     pfatal ("Cannot change directory to /dev");
  80.  
  81.   term = getenv ("TERM");
  82.   /* Some implementations of getopt seem to start out with `optind' == 0,
  83.      while others start out with `optind' == 1.  Whatever it starts out as,
  84.      we save it and then restore it.  */
  85.   i = optind;
  86.   parse_environment ();
  87.   optind = i;
  88.   parse_options (argc, argv, 1);
  89.  
  90.   if (new_stats)
  91.     {
  92.       if (argc < optind)
  93.     usage ();
  94.       set_status (new_stats);
  95.       exit (0);
  96.     }
  97.  
  98.   /* Check for user|tty[,user|tty...]. */
  99.   if (optind < argc)
  100.       add_recipients (argv[optind++]);
  101.   if (tty_list == NULL)
  102.     {
  103.       fprintf (stderr, "%s: No ttys to hint\n", program_name);
  104.       exit (1);
  105.     }
  106.   endhtent ();
  107.  
  108.   length_limit -= 11;        /* 8 for username + 3 for punctuation. */
  109.   if (length_limit < 11)
  110.     length_limit = 11;        /* Let's be reasonable. */
  111.  
  112.   /* Check for message. */
  113.   if (optind < argc)
  114.     for (; optind < argc; ++optind)
  115.       {
  116.     append_word (argv[optind]);
  117.     if (optind < argc - 1)
  118.       append_word (" ");
  119.       }
  120.   else
  121.     {
  122.       prompt ();
  123.       fgets (temp_message, MAX_MESSAGE, stdin);
  124.       append_word (temp_message);
  125.     }
  126.  
  127.   set_signals ();
  128.   /* Send the hint to all relevant ttys. */
  129.   while (tp = next_tty ())
  130.     sendhint (tp);
  131.  
  132.   if (forever)
  133.     exit (0);
  134.   switch (fork ())
  135.     {
  136.     case -1:
  137.       pfatal ("Cannot fork");
  138.     case 0:            /* Child. */
  139.       sleep (duration);
  140.       while (tp = next_tty ())
  141.     erasehint (tp);
  142.     }
  143.   exit (0);
  144.   /* NOTREACHED */
  145. }
  146.  
  147. void
  148. parse_environment ()
  149. {
  150.   char *enval;
  151.   struct args *argsp;
  152.   char *s;            /* program_name + enval. */
  153.  
  154.   enval = getenv (ENVAR);
  155.   if (enval && *enval)
  156.     {
  157.       s = xmalloc ((unsigned) (strlen (enval) + strlen (program_name) + 2));
  158.       strcpy (s, program_name);
  159.       strcat (s, " ");
  160.       strcat (s, enval);
  161.       argsp = stov (s);
  162.       parse_options (argsp->argc, argsp->argv, 0);
  163.       free (s);
  164.     }
  165. }
  166.  
  167. /* If `cmdline' is nonzero, this function was called with command line
  168.    arguments; otherwise they were taken from the environment. */
  169.  
  170. void
  171. parse_options (argc, argv, cmdline)
  172.      int argc;
  173.      char **argv;
  174.      int cmdline;
  175. {
  176.   extern int optind;
  177.   extern char *optarg;
  178.   int c;
  179.  
  180.   while ((c = getopt (argc, argv, "ynbfpsd:T:V")) != EOF)
  181.     {
  182.       switch (c)
  183.     {
  184.     case 'y':
  185.     case 'n':
  186.       if (cmdline)
  187.         new_stats = c;
  188.       else
  189.         ignored (c);
  190.       break;
  191.     case 'b':
  192.       use_vbell = 0;
  193.       break;
  194.     case 'f':
  195.       forever = 1;
  196.       break;
  197.     case 'p':
  198.       name_at_end = 0;
  199.       break;
  200.     case 's':
  201.       use_bell = 0;
  202.       break;
  203.     case 'd':
  204.       duration = atoi (optarg);
  205.       if (duration < MIN_SEC || duration > MAX_SEC)
  206.         {
  207.           fprintf (stderr,
  208.                "%s: %d: Duration out of range (%d-%d seconds)\n",
  209.                program_name, duration, MIN_SEC, MAX_SEC);
  210.           exit (1);
  211.         }
  212.       break;
  213.     case 'T':
  214.       term = optarg;
  215.       break;
  216.     case 'V':
  217.       if (cmdline)
  218.         {
  219.           fprintf (stderr, "hint, version 3.5\n");
  220.           exit (0);
  221.         }
  222.       else
  223.         ignored (c);
  224.       break;
  225.     default:
  226.       if (cmdline)
  227.         usage ();
  228.       else
  229.         fprintf (stderr, "%s: Invalid option in environment ignored\n",
  230.              program_name);
  231.       break;
  232.     }
  233.     }
  234. }
  235.  
  236. /* Display a prompt that attempts to be as long as `length_limit'. */
  237.  
  238. void
  239. prompt ()
  240. {
  241.   static char *prompt_string = "[ Enter message (max. %d characters):";
  242.   int prompt_length;
  243.  
  244.   printf (prompt_string, length_limit);
  245.   /* Set prompt_length to length of the prompt except for trailing spaces. */
  246.   prompt_length = strlen (prompt_string) + 1;    /* The 1 is the ']'. */
  247.   if (length_limit > 99)
  248.     ++prompt_length;        /* Adjust for the extra digit. */
  249.   if (prompt_length > length_limit)
  250.     prompt_length = length_limit;
  251.   printf ("%*s]\n", length_limit - prompt_length, " ");
  252.   fflush (stdout);
  253. }
  254.  
  255. /* Print the program name, `s', and a system error message, and die.  */
  256.  
  257. void
  258. pfatal (s)
  259.      char *s;
  260. {
  261.   fprintf (stderr, "%s: ", program_name);
  262.   perror (s);
  263.   exit (1);
  264. }
  265.  
  266. /* Same but stay alive. */
  267.  
  268. void
  269. pnonfatal (s)
  270.      char *s;
  271. {
  272.   fprintf (stderr, "%s: ", program_name);
  273.   perror (s);
  274. }
  275.  
  276. void
  277. show_status ()
  278. {
  279.   char *tty;
  280.  
  281.   tty = ttyname (2);        /* Stderr is least likely to be redirected. */
  282.   if (tty == NULL)
  283.     pfatal ("Cannot get ttyname");
  284.   printf ("is %c\n", hintable (tty) ? 'y' : 'n');
  285. }
  286.  
  287. /* Set hint status.
  288.    `newstat' can be 'y' or 'n'.  */
  289.  
  290. void
  291. set_status (newstat)
  292.      char newstat;
  293. {
  294.   struct stat tty_stats;
  295.   char *tty;
  296.  
  297.   tty = ttyname (2);
  298.   if (tty == NULL)
  299.     pfatal ("Cannot get ttyname");
  300.   if (stat (tty, &tty_stats) < 0)
  301.     pfatal (tty);
  302.  
  303.   if (newstat == 'y')
  304.     {
  305.       tty_stats.st_mode |= HINTBIT;
  306.       updatetab (tty);
  307.     }
  308.   else
  309.     tty_stats.st_mode &= ~HINTBIT;
  310.   if (chmod (tty, tty_stats.st_mode) < 0)
  311.     pfatal (tty);
  312. }
  313.  
  314. /* Return true if `tty' is hint y, false otherwise.  */
  315.  
  316. int
  317. hintable (tty)
  318.      char *tty;
  319. {
  320.   struct stat tty_stats;
  321.  
  322.   if (stat (tty, &tty_stats) < 0)
  323.     {
  324.       pnonfatal (tty);
  325.       return 0;            /* Cannot stat; assume they're hint n. */
  326.     }
  327.   return tty_stats.st_mode & HINTBIT;
  328. }
  329.  
  330. /* Return true if `name' is a tty device in /dev. */
  331.  
  332. int
  333. istty (name)
  334.      char *name;
  335. {
  336.   struct stat stats;
  337.   char *path;
  338.   int i;
  339.  
  340.   path = xmalloc (strlen (name) + 3);
  341.   strcpy (path, "./");
  342.   strcat (path, name);
  343.   i = stat (path, &stats);
  344.   free (path);
  345.   if (i == -1 || (stats.st_mode & S_IFMT) != S_IFCHR)
  346.     return 0;
  347.   else
  348.     return 1;
  349. }
  350.  
  351. void
  352. add_recipients (names)
  353.      char *names;
  354. {
  355.   char *name;
  356.  
  357.   for (name = strtok (names, ","); name; name = strtok ((char *) NULL, ","))
  358.     if (istty (name))
  359.       {
  360.     addtty ("user", name);
  361.       }
  362.     else
  363.       {
  364.     /* Add any ttys where user `name' is logged on to `tty_list'. */
  365.     if (adduser (name) == 0)
  366.       fprintf (stderr, "%s: %s is not logged on\n", program_name, name);
  367.       }
  368. }
  369.  
  370. /* Add `user' at `tty' (which should have no leading path) to the list of
  371.    ttys to send the hint to.  */
  372.  
  373. void
  374. addtty (user, tty)
  375.      char *user;
  376.      char *tty;
  377. {
  378.   struct ttylist *tp;
  379.   struct hinttab *ht;
  380.   char *ttypath;
  381.  
  382.   /* Make sure they haven't already been added. */
  383.   for (tp = tty_list; tp; tp = tp->tt_next)
  384.     if (!strcmp (tp->tt_tty, tty))
  385.       return;
  386.  
  387.   if (!hintable (tty))
  388.     {
  389.       /* They're hint n. */
  390.       fprintf (stderr, "%s: %s on %s is refusing hints\n",
  391.            program_name, user, tty);
  392.       return;
  393.     }
  394.  
  395.   sethtent ();            /* Rewind hinttab file. */
  396.  
  397.   ttypath = xmalloc ((unsigned) (strlen (tty) + 6));
  398.   strcpy (ttypath, "/dev/");
  399.   strcat (ttypath, tty);
  400.   ht = gethttty (ttypath);
  401.   free (ttypath);
  402.  
  403.   if (ht == NULL)
  404.     {
  405.       fprintf (stderr, "%s: No hinttab entry for %s\n", program_name, tty);
  406.       return;
  407.     }
  408.  
  409.   tp = (struct ttylist *) xmalloc (sizeof (struct ttylist));
  410.   tp->tt_user = strdup (user);
  411.   tp->tt_tty = strdup (tty);
  412.   tp->tt_ht = *ht;
  413.  
  414.   tp->tt_next = tty_list;
  415.   tty_list = tp;
  416.  
  417.   /* Keep track of the shortest status line we'll be hinting. */
  418.   if (length_limit > tp->tt_ht.h_ws)
  419.     length_limit = tp->tt_ht.h_ws;
  420. }
  421.  
  422. /* Return the next tty in the list.
  423.    Return NULL if the list is empty.  */
  424.  
  425. struct ttylist *
  426. next_tty ()
  427. {
  428.   static struct ttylist *tp = NULL;
  429.  
  430.   tp = tp ? tp->tt_next : tty_list;
  431.  
  432.   return tp;
  433. }
  434.  
  435. void
  436. set_signals ()
  437. {
  438. #ifdef SIGTTOU
  439.   signal (SIGTTOU, SIG_IGN);
  440. #endif
  441.   /* If the sender logs out right after hinting, stick around to erase the
  442.      hint. */
  443.   signal (SIGHUP, SIG_IGN);
  444.   signal (SIGINT, SIG_IGN);
  445. }
  446.  
  447. /* Concatenate `word' onto the end of the hint message, silently truncating
  448.    if it would overflow `length_limit', and removing any control
  449.    characters.  */
  450.  
  451. void
  452. append_word (word)
  453.      char *word;
  454. {
  455.   static int message_length = 0;/* Length of message. */
  456.  
  457.   for (; *word && message_length < length_limit; ++word)
  458.     if (isascii (*word) && isprint (*word))
  459.       message[message_length++] = *word;
  460.   message[message_length] = 0;
  461. }
  462.  
  463. /* Return a malloc'd duplicate of string `s'. */
  464.  
  465. char *
  466. strdup (s)
  467.      char *s;
  468. {
  469.   return strcpy (xmalloc ((unsigned) (strlen (s) + 1)), s);
  470. }
  471.  
  472. /* Return `name' with any leading path stripped off.  */
  473.  
  474. char *
  475. basename (name)
  476.      char *name;
  477. {
  478.   char *base;
  479.  
  480.   base = strrchr (name, '/');
  481.   return base ? base + 1 : name;
  482. }
  483.  
  484. void
  485. ignored (c)
  486.      char c;
  487. {
  488.   fprintf (stderr, "%s: Option ignored in environment: -%c\n", program_name, c);
  489. }
  490.  
  491. void
  492. usage ()
  493. {
  494.   fprintf (stderr,
  495.        "Usage: %s [-fp] [-d sec] user|tty[,user|tty...] [message]\n",
  496.        program_name);
  497.   fprintf (stderr, "       %s -y [-bs] [-T termtype]\n", program_name);
  498.   fprintf (stderr, "       %s -n\n", program_name);
  499.   fprintf (stderr, "       %s -V\n", program_name);
  500.   fprintf (stderr, "       %s\n", program_name);
  501.   exit (1);
  502. }
  503.