source: trunk/athena/bin/bugme/bugme.c @ 12244

Revision 12244, 15.2 KB checked in by ghudson, 26 years ago (diff)
Move bugme sources here from athena/bin/dash/src/bugme, and autoconfiscate.
Line 
1/* Copyright 1997 by the Massachusetts Institute of Technology.
2 *
3 * Permission to use, copy, modify, and distribute this
4 * software and its documentation for any purpose and without
5 * fee is hereby granted, provided that the above copyright
6 * notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting
8 * documentation, and that the name of M.I.T. not be used in
9 * advertising or publicity pertaining to distribution of the
10 * software without specific, written prior permission.
11 * M.I.T. makes no representations about the suitability of
12 * this software for any purpose.  It is provided "as is"
13 * without express or implied warranty.
14 */
15
16static const char rcsid[] = "$Id: bugme.c,v 1.1 1998-12-17 16:27:12 ghudson Exp $";
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <unistd.h>
21#include <signal.h>
22#include <string.h>
23#include <ctype.h>
24#include <netdb.h>
25#include <sys/param.h>
26#include <errno.h>
27#include <sys/types.h>
28#include <sys/wait.h>
29#include <X11/Xos.h>
30#include <Jets/Jets.h>
31#include <Jets/Window.h>
32#include <Jets/Button.h>
33#include <Jets/Label.h>
34#include <Jets/DClock.h>
35#include <Jets/Form.h>
36#include <Jets/Tree.h>
37#include "Snoop.h"
38#include "bugme.h"
39
40#define QUICK_FILE DATADIR "/quickstations"
41
42int ok_x, ok_y; /* most recent acceptable position for the counter window */
43
44/* Constants computed in main() */
45Jet root;
46int proper_width, proper_height, display_width, display_height;
47pid_t pid;
48
49/* These definitions are needed by the tree jet */
50JetClass *jetClasses[] =
51{ &treeJetClass, &windowJetClass, &buttonJetClass, &labelJetClass,
52    &formJetClass };
53
54int numJetClasses = XjNumber(jetClasses);
55
56static XrmOptionDescRec op_table[] = {
57{"+rv",         "*reverseVideo", XrmoptionNoArg,        (void *) "off"},
58{"-background", "*background",  XrmoptionSepArg,        (void *) NULL},
59{"-bd",         "*borderColor", XrmoptionSepArg,        (void *) NULL},
60{"-bg",         "*background",  XrmoptionSepArg,        (void *) NULL},
61{"-bordercolor","*borderColor", XrmoptionSepArg,        (void *) NULL},
62{"-borderwidth",".borderWidth", XrmoptionSepArg,        (void *) NULL},
63{"-bw",         ".borderWidth", XrmoptionSepArg,        (void *) NULL},
64{"-display",    ".display",     XrmoptionSepArg,        (void *) NULL},
65{"-fg",         "*foreground",  XrmoptionSepArg,        (void *) NULL},
66{"-fn",         "*font",        XrmoptionSepArg,        (void *) NULL},
67{"-font",       "*font",        XrmoptionSepArg,        (void *) NULL},
68{"-foreground", "*foreground",  XrmoptionSepArg,        (void *) NULL},
69{"-geometry",   "*menuTree.window.geometry",    XrmoptionSepArg,
70   (void *) NULL},
71{"-reverse",    "*reverseVideo", XrmoptionNoArg,        (void *) "on"},
72{"-rv",         "*reverseVideo", XrmoptionNoArg,        (void *) "on"},
73{"-xrm",        NULL,           XrmoptionResArg,        (void *) NULL},
74{"-name",       ".name",        XrmoptionSepArg,        (void *) NULL},
75{"-appdefs",    ".appDefs",     XrmoptionSepArg,        (void *) NULL},
76{"-f",          ".appDefs",     XrmoptionSepArg,        (void *) NULL},
77{"-userdefs",   ".userDefs",    XrmoptionSepArg,        (void *) NULL},
78{"-bugnames",   ".bugnames",    XrmoptionSepArg,        (void *) NULL},
79{"-exec",       ".exec",        XrmoptionSepArg,        (void *) NULL},
80};
81
82My_resources parms;
83
84#define offset(field) XjOffset(My_resources *,field)
85
86static XjResource app_resources[] =
87{
88  { "bugnames", "Bugnames", XjRString, sizeof(char *),
89      offset(bugnames), XjRString, (void *) NULL },
90  { "exec", "Exec", XjRString, sizeof(char *),
91      offset(command), XjRString, (void *) NULL },
92};
93
94#undef offset
95
96/* Tree callbacks... */
97
98int print_tree(Jet from_jet, char *what, void *data)
99{
100  Jet j;
101
102  for (j = root->core.child; j != NULL; j = j->core.sibling)
103    fprintf(stdout, "%s\n", j->core.name);
104  return 0;
105}
106
107int map_tree(Jet from_jet, char *what, void *data)
108{
109  Jet w;
110
111  w = XjFindJet(what, root);
112
113  if (w != NULL)
114    {
115      w = w->core.child;
116      while (w)
117        {
118          MapWindow(w, True);
119          w = w->core.sibling;
120        }
121      return 0;
122    }
123  else
124    {
125      char errtext[100];
126
127      sprintf(errtext, "couldn't find %s to map it", what);
128      XjWarning(errtext);
129      return 1;
130    }
131}
132
133int unmap_tree(Jet from_jet, char *what, void *data)
134{
135  Jet w;
136
137  w = XjFindJet(what, root);
138
139  if (w != NULL)
140    {
141      w = w->core.child;
142      while (w)
143        {
144          UnmapWindow(w);
145          w = w->core.sibling;
146        }
147      return 0;
148    }
149  else
150    {
151      char errtext[100];
152
153      sprintf(errtext, "couldn't find %s to unmap it", what);
154      XjWarning(errtext);
155      return 1;
156    }
157}
158
159int create_tree(Jet from_jet, char *what, void *data)
160{
161  if (NULL == XjFindJet(what, root))
162    XjRealizeJet(XjVaCreateJet(what, treeJetClass, root, NULL, NULL));
163  return 0;
164}
165
166int create_map_tree(Jet from_jet, char *what, void *data)
167{
168  if (NULL == XjFindJet(what, root))
169    XjRealizeJet(XjVaCreateJet(what, treeJetClass, root, NULL, NULL));
170  else
171    map_tree(from_jet, what, data);
172
173  return 0;
174}
175
176int destroy_tree(Jet from_jet, char *what, void *data)
177{
178  Jet w;
179
180  w = XjFindJet(what, root);
181  if (w != NULL)
182    {
183      XjDestroyJet(w);
184      return 0;
185    }
186  else
187    {
188      char errtext[100];
189
190      sprintf(errtext, "couldn't find %s to destroy it", what);
191      XjWarning(errtext);
192      return 1;
193    }
194}
195
196int warp_tree(Jet from_jet, char *what, void *data)
197{
198  Jet w;
199
200  w = XjFindJet(what, root);
201
202  if (w != NULL)
203    {
204      w = w->core.child;
205      if (w != NULL)
206        XWarpPointer(w->core.display,
207                     None,
208                     w->core.window,
209                     0, 0, 0, 0,
210                     w->core.width / 2,
211                     w->core.height / 2);
212      return 0;
213    }
214  else
215    {
216      char errtext[100];
217
218      sprintf(errtext, "couldn't find %s to warp there", what);
219      XjWarning(errtext);
220      return 1;
221    }
222}
223
224fatal(Display *display)
225{
226  XjExit(-1);
227}
228
229static int (*def_handler)();
230
231static int handler(Display *display, XErrorEvent *error)
232{
233  if (error->error_code == BadWindow  ||  error->error_code == BadAtom)
234    return 0;
235
236  def_handler(display, error);
237  return 0;                     /* it'll never get this far anyway... */
238}
239
240int delete(Jet from_jet, char *what, void *data)
241{
242  XjDestroyJet(from_jet);
243  return 0;
244}
245
246int delete_parent(Jet from_jet, char *what, void *data)
247{
248  XjDestroyJet(XjParent(from_jet));
249  return 0;
250}
251
252/* This kind of operation isn't actually safe in principle,
253 * but is in practice. XjDestroyJet needs to be modified so
254 * that this can be done safely.
255 */
256int delete_ancestor(Jet from_jet, int generationGap, void *data)
257{
258  Jet who = from_jet;
259
260  for (; generationGap--; generationGap >= 0)
261    who = XjParent(who);
262
263  XjDestroyJet(who);
264  return 0;
265}
266
267Boolean event(Jet from_jet, char *what, XEvent *event)
268{
269  int broken = 0;
270  static struct timeval last;
271  static int howmany;
272  struct timeval now;
273  static int clickx, clicky, movex, movey;
274
275  switch(event->type)
276    {
277    case VisibilityNotify:
278      /* If someone obscures us, come back. */
279      if (event->xvisibility.window == from_jet->core.window)
280        if (event->xvisibility.state != VisibilityUnobscured)
281          {
282            /* don't fight with ourselves. */
283            gettimeofday(&now, NULL);
284            if (now.tv_sec - last.tv_sec > 2)
285              {
286                last = now;
287                howmany = 0;
288              }
289            else
290              {
291                howmany++;
292                if (howmany > 20)
293                  {
294                    ok_x = rand() % (display_width - proper_width);
295                    ok_y = rand() % (display_height - proper_height);
296                    XMoveResizeWindow(root->core.display,
297                                      from_jet->core.window,
298                                      ok_x, ok_y, proper_width, proper_height);
299                    howmany = 0;
300                    last = now;
301                  }
302              }
303
304            MapWindow(XjParent(from_jet), True);
305          }
306      break;
307
308    case UnmapNotify:
309      /* If someone unmaps us, come back. */
310      if (event->xunmap.window == from_jet->core.window)
311        MapWindow(XjParent(from_jet), True);
312      break;
313
314    case ConfigureNotify:
315      if (event->xconfigure.window == from_jet->core.window)
316        {
317          /* If someone resizes us, go back to the size we like. */
318          if (event->xconfigure.width != proper_width ||
319              event->xconfigure.height != proper_height)
320            broken = 1;
321
322          /* If someone moves us off the screen, go back to where we were. */
323          if (event->xconfigure.x + proper_width > display_width ||
324              event->xconfigure.y + proper_height > display_height ||
325              event->xconfigure.x < 0 || event->xconfigure.y < 0)
326            broken = 1;
327          else
328            {
329              ok_x = event->xconfigure.x;
330              ok_y = event->xconfigure.y;
331            }
332
333          if (broken)
334            XMoveResizeWindow(root->core.display, from_jet->core.window,
335                              ok_x, ok_y, proper_width, proper_height);
336        }
337      break;
338
339    case ReparentNotify:
340      /* If we are reparented away from the root, reparent back to
341       * the root.
342       */
343      if (event->xreparent.window == from_jet->core.window &&
344          event->xreparent.event != root->core.window)
345        XReparentWindow(root->core.display, from_jet->core.window,
346                        root->core.window, ok_x, ok_y);
347
348      /* If someone reparents us a child, destroy it. */
349      if (event->xreparent.window != from_jet->core.window)
350        XDestroyWindow(root->core.display, event->xreparent.window);
351      break;
352
353    case CreateNotify:
354      /* If someone creates a child for us, destroy it. */
355      if (event->xcreatewindow.parent == from_jet->core.window)
356        XDestroyWindow(root->core.display, event->xcreatewindow.window);
357      break;
358
359    /* Let the user drag the window around, since we aren't letting
360     * the window manager do it.
361     */
362    case ButtonPress:
363      clickx = event->xbutton.x_root;
364      clicky = event->xbutton.y_root;
365      movex = ok_x;
366      movey = ok_y;
367      break;
368
369    case MotionNotify:
370      movex += event->xmotion.x_root - clickx;
371      movey += event->xmotion.y_root - clicky;
372
373      if (movex < 0)
374        movex = 0;
375      if (movex + proper_width + 1 >= display_width)
376        movex = display_width - proper_width - 2;
377      if (movey < 0)
378        movey = 0;
379      if (movey + proper_height + 1 >= display_height)
380        movey = display_height - proper_height - 2;
381
382      clickx = event->xmotion.x_root;
383      clicky = event->xmotion.y_root;
384
385      XMoveWindow(root->core.display, from_jet->core.window, movex, movey);
386      break;
387
388    default:
389      break;
390    }
391
392  return False; /* Pass the event on to whoever cares */
393}
394
395void post_warning(char *name, int id)
396{
397  XjRealizeJet(XjVaCreateJet(name, treeJetClass, root, NULL, NULL));
398}
399
400void post_intermittent_warning(Intermittent *i, int id)
401{
402  XjRealizeJet(XjVaCreateJet(i->name, treeJetClass, root, NULL, NULL));
403  XjAddWakeup(post_intermittent_warning, i, i->timeout);
404}
405
406void setup_warnings()
407{
408  char name[50], class[50];
409  char *type, *what, *next = NULL;
410  XrmValue value;
411  int timeout, freq;
412  Intermittent *i;
413
414  while (what = strtok(parms.bugnames, " "))
415    {
416      parms.bugnames = NULL;
417
418      sprintf(name, "%s.%s.timeout", programName, what);
419      sprintf(class, "%s.%s.Timeout", programClass, what);
420      if (XrmGetResource(rdb, name, class, &type, &value))
421        {
422          timeout = 1000 * strtol((char *)value.addr, &next, 0);
423          if (next)
424            freq = 1000 * strtol(next, NULL, 0);
425           
426          if (freq == 0)
427            XjAddWakeup(post_warning, what, timeout);
428          else
429            {
430              i = (Intermittent *)XjMalloc(sizeof(Intermittent));
431              i->name = what;
432              i->timeout = freq;
433              XjAddWakeup(post_intermittent_warning, i, timeout);
434            }
435        }
436      else
437        {
438          char errtext[100];
439
440          sprintf(errtext, "no timeout setting for %s exists", what);
441          XjWarning(errtext);
442        }
443    }
444}
445
446XjCallbackRec callbacks[] =
447{
448  /* tree operations */
449  { "createTree", create_tree },
450  { "createMapTree", create_map_tree },
451  { "destroyTree", destroy_tree },
452  { "warpTree", warp_tree },
453  { "mapTree", map_tree },
454  { "unmapTree", unmap_tree },
455  { "printTree", print_tree },
456  /* misc */
457  { "delete", delete },
458  { "deleteParent", delete_parent },
459  { "deleteAncestor", delete_ancestor },
460  { "event", event },
461};
462
463/* Compare two strings, ignoring leading whitespace and
464 * terminating at trailing whitespace, ignoring case.
465 */
466int strwhitecasecmp(char *s1, char *s2)
467{
468  char *c1, *c2;
469
470  c1 = s1;
471  c2 = s2;
472
473  while (isspace(*c1) && *c1 != '\0')
474    c1++;
475  while (isspace(*c2) && *c2 != '\0')
476    c2++;
477
478  while (*c1 != '\0' && *c2 != '\0' &&
479         !isspace(*c1) && !isspace(*c2) &&
480         tolower(*c1) == tolower(*c2))
481    c1++, c2++;
482
483  if ((*c1 == '\0' || isspace(*c1)) &&
484      (*c2 == '\0' || isspace(*c2)))
485    return 0;
486
487  return 1;
488}
489
490/* Return 1 if the timer should be started for this host, 0 otherwise. */
491int start_timer()
492{
493  FILE *hosts;
494  char ourname[MAXHOSTNAMELEN + 1], thisname[MAXHOSTNAMELEN + 1];
495
496  if (gethostname(ourname, sizeof(ourname)))
497    return 0;
498
499  hosts = fopen(QUICK_FILE, "r");
500  if (hosts == NULL)
501    return 0;
502
503  while (!feof(hosts) && fgets(thisname, sizeof(thisname), hosts))
504    if (!strwhitecasecmp(ourname, thisname))
505      {
506        fclose(hosts);
507        return 1;
508      }
509
510  fclose(hosts);
511
512  return 0;
513}
514
515void child()
516{
517  int status;
518
519  if (-1 == waitpid(pid, &status, WNOHANG))
520    return;
521
522  if (WIFEXITED(status))
523    XjExit(WEXITSTATUS(status));
524
525  if (WIFSIGNALED(status))
526    XjExit(WTERMSIG(status));
527}
528
529main(int argc, char **argv)
530{
531  Display *display;
532  char user_file[100];
533  Jet window, form, timecounter, snooper;
534  struct timeval t;
535  char *fakeargv[2];
536  int fakeargc = 1;
537  struct sigaction sigact;
538
539  if (argc < 2)
540    {
541      fprintf(stderr, "usage: %s command [arg ...]\n",
542              argv ? *argv : "bugme");
543      exit(2);
544    }
545
546  if (!start_timer())
547    {
548      execvp(argv[1], &argv[1]);
549      fprintf(stderr, "%s: couldn't exec %s (%s)\n",
550              argv[0], argv[1], strerror(errno));
551      exit(3);
552    }
553
554  /* Not taking X args in this program, but make something to pass
555   * to the toolkit.
556   */
557  fakeargv[0] = argv[0];
558  fakeargv[1] = NULL;
559
560  gettimeofday(&t, NULL);
561
562  (void)XSetIOErrorHandler(fatal);
563
564  strcpy(user_file, "Bugme");
565
566  root = XjCreateRoot(&fakeargc, fakeargv, "Bugme", user_file,
567                      op_table, XjNumber(op_table));
568
569  XjLoadFromResources(NULL,
570                      NULL,
571                      programClass,
572                      programName,
573                      app_resources,
574                      XjNumber(app_resources),
575                      (void *) &parms);
576  display = root->core.display;
577
578  XjRegisterCallbacks(callbacks, XjNumber(callbacks));
579
580  window = XjVaCreateJet("timerWindow", windowJetClass, root, NULL, NULL);
581  snooper = XjVaCreateJet("snooper", snoop_jet_class, window, NULL, NULL);
582  form = XjVaCreateJet("timerForm", formJetClass, window, NULL, NULL);
583  timecounter = XjVaCreateJet("timer", dClockJetClass, form,
584                              XjNtimeOffset, -t.tv_sec, NULL, NULL);
585
586  XjRealizeJet(root);
587  XjSelectInput(display, window->core.window, SubstructureNotifyMask |
588                ButtonPressMask | ButtonMotionMask);
589
590  proper_width = window->core.width;
591  proper_height = window->core.height;
592  ok_x = window->core.x;
593  ok_y = window->core.y;
594  display_width = XDisplayWidth(display, DefaultScreen(display));
595  display_height = XDisplayHeight(display, DefaultScreen(display));
596  srand(t.tv_sec);
597
598  setup_warnings();
599
600  /* Now that we have a good head start (mainly we need to wait until
601   * after we've read our resource database), start the user's session
602   * (well, whatever command was passwd). Fortunately, Xj does not
603   * support Xdefaults.
604   */
605
606  sigact.sa_flags = SA_NOCLDSTOP;
607  sigemptyset(&sigact.sa_mask);
608  sigact.sa_handler = child;
609  sigaction(SIGCHLD, &sigact, NULL);
610
611  pid = fork();
612  if (pid == 0)
613    {
614      putenv("ATHENA_QUICK=1");
615      execvp(argv[1], &argv[1]);
616      fprintf(stderr, "%s: couldn't exec %s (%s)\n",
617              argv[0], argv[1], strerror(errno));
618      XjExit(3);
619    }
620
621  XjEventLoop(root);
622}
Note: See TracBrowser for help on using the repository browser.