ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/rxvt-unicode/src/rclock.C
Revision: 1.1
Committed: Mon May 10 00:36:04 2021 UTC (3 years ago) by root
Content type: text/plain
Branch: MAIN
Log Message:
original 2.7.10 rclock.c

File Contents

# Content
1 /*--------------------------------*-C-*---------------------------------*
2 * Copyright 1997 1998 Oezguer Kesim <kesim@math.fu-berlin.de>
3 * Copyright 1992, 1993 Robert Nation <nation@rocket.sanders.lockheed.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *----------------------------------------------------------------------*/
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #else
22 /* # define STDC_HEADERS */
23 # define HAVE_UNISTD_H
24 # define TIME_WITH_SYS_TIME
25 # define HAVE_SYS_TIME_H
26 # ifdef _AIX
27 # define HAVE_SYS_SELECT_H
28 # endif
29 #endif
30
31 #include "../src/version.h"
32 #include "feature.h"
33
34 #include <ctype.h>
35 /* #ifdef STDC_HEADERS */
36 # include <stdarg.h>
37 # include <stdio.h>
38 # include <stdlib.h>
39 # include <string.h>
40 /* #endif */
41
42 #ifdef HAVE_UNISTD_H
43 # include <unistd.h>
44 #endif
45
46 #ifdef TIME_WITH_SYS_TIME
47 # include <sys/time.h>
48 # include <time.h>
49 #else
50 # ifdef HAVE_SYS_TIME_H
51 # include <sys/time.h>
52 # else
53 # include <time.h>
54 # endif
55 #endif
56
57 #if defined (__svr4__)
58 # include <sys/resource.h> /* for struct rlimit */
59 #endif
60
61 #ifdef HAVE_SYS_SELECT_H
62 # include <sys/select.h>
63 #endif
64 #include <sys/stat.h>
65
66 #ifdef MAIL
67 #include <dirent.h>
68 #endif
69
70 #include <X11/Intrinsic.h> /* Xlib, Xutil, Xresource, Xfuncproto */
71
72 #define APL_CLASS "Clock"
73 #define APL_NAME "rclock"
74 #define MSG_CLASS "Appointment"
75 #define MSG_NAME "Appointment"
76 #define CONFIG_FILE ".rclock"
77
78 #ifndef EXIT_SUCCESS /* missed from <stdlib.h> ? */
79 # define EXIT_SUCCESS 0
80 # define EXIT_FAILURE 1
81 #endif
82
83 /*----------------------------------------------------------------------*/
84
85 static Display* Xdisplay; /* X display */
86 static int Xfd; /* file descriptor of server connection */
87 static GC Xgc, Xrvgc; /* normal, reverse video GC */
88
89 #define Xscreen DefaultScreen (Xdisplay)
90 #define Xcmap DefaultColormap (Xdisplay, Xscreen)
91 #define Xroot DefaultRootWindow (Xdisplay)
92
93 /* windows and their sizes */
94 typedef struct {
95 Window win;
96 int width, height;
97 } mywindow_t;
98
99 static mywindow_t Clock = {None, 80, 80}; /* parent window */
100
101 #define fgColor 0
102 #define bgColor 1
103 static const char * rs_color [2] = { FG_COLOR_NAME, BG_COLOR_NAME };
104 static Pixel PixColors [2];
105 static const char * rs_geometry = NULL;
106
107 #ifdef ICONWIN
108 static const char * rs_iconGeometry = NULL;
109 static mywindow_t Icon = {None, 65, 65}; /* icon window */
110 static int iconic_state = NormalState; /* iconic startup? */
111 #endif
112
113 #ifdef REMINDERS
114 static mywindow_t Msg = {0, 0, 0}; /* message window */
115 static struct {
116 Window
117 # ifndef NO_REMINDER_EXEC
118 Start,
119 # endif
120 Dismiss,
121 Defer;
122 int width, height;
123 } msgButton;
124
125 static XFontStruct * Xfont;
126 #define FontHeight() (Xfont->ascent + Xfont->descent)
127 static int Msg_Mapped = 0; /* message window mapped? */
128 static int reminderTime = -1;
129 static char message [256] = "";
130 #ifndef NO_REMINDER_EXEC
131 static char execPrgm [256] = "";
132 #endif
133 static const char * reminders_file = NULL; /* name of ~/.rclock file */
134 #ifdef DATE_ON_CLOCK_FACE
135 static int show_date = 1; /* show date on face of clock */
136 #endif
137 #endif
138
139 #ifdef ADJUST_TIME
140 static int adjustTime = 0;
141 #else
142 # define adjustTime 0
143 #endif
144
145 #ifdef CENTURY
146 # if (CENTURY < 1900)
147 Error, Cenury incorrectly set.
148 # endif
149 #else
150 # define CENTURY 1900
151 #endif
152
153 static int clockUpdate = CLOCKUPDATE;
154
155 #ifdef MAIL
156 static int mailUpdate = MAILUPDATE;
157 static char * mail_file = NULL;
158 #ifndef MAIL_SPAWN
159 static char * mail_spawn = NULL;
160 #endif
161 static int is_maildir = 0;
162 #endif
163
164 static XSizeHints szHint = {
165 PMinSize | PResizeInc | PBaseSize | PWinGravity,
166 0, 0, 80, 80, /* x, y, width and height */
167 1, 1, /* Min width and height */
168 0, 0, /* Max width and height */
169 1, 1, /* Width and height increments */
170 {1, 1}, /* x, y increments */
171 {1, 1}, /* Aspect ratio - not used */
172 0, 0, /* base size */
173 NorthWestGravity /* gravity */
174 };
175
176 /* subroutine declarations */
177 static void geometry2sizehint (mywindow_t * /* win */,
178 const char * /* geom */);
179 static void Create_Windows (int /* argc */,
180 char * /* argv */ []);
181 static void getXevent (void);
182 static void print_error (const char * /* fmt */, ...);
183
184 static void Draw_Window (mywindow_t * /* this_win */,
185 int /* full_redraw */);
186 static void Reminder (void);
187 static void Next_Reminder (int /* update_only */);
188
189 /* Arguments for Next_Reminder() */
190 #define REPLACE 0
191 #define UPDATE 1
192
193 /*----------------------------------------------------------------------*/
194
195 static void
196 usage (void)
197 {
198 int i;
199 struct {
200 const char * const opt;
201 const char * const desc;
202 } optList[] = {
203 #define optList_size() (sizeof(optList)/sizeof(optList[0]))
204 { "-display displayname", "X server to contact" },
205 { "-geometry geom", "size (in pixels) and position" },
206 { "-bg color", "background color" },
207 { "-fg color", "foreground color" },
208 #ifdef REMINDERS
209 { "-fn fontname", "normal font for messages" },
210 #ifdef DATE_ON_CLOCK_FACE
211 { "-nodate", "do not display date on the clock face" },
212 #endif
213 #endif
214 #ifdef ICONWIN
215 { "-iconic", "start iconic" },
216 #endif
217 #ifdef ADJUST_TIME
218 { "-adjust +/-ddhhmm", "adjust clock time" },
219 #endif
220 { "-update seconds", "clock update interval" },
221 #ifdef MAIL
222 { "-mail seconds", "check $MAIL interval" },
223 { "-mailfile file", "file to use for mail checking" },
224 #ifndef MAIL_SPAWN
225 { "-mailspawn cmd", "execute `cmd` when clock is clicked" },
226 #endif
227 #endif
228 { "#geom", "icon window geometry" }
229 };
230
231 fprintf (stderr, "\nUsage v" VERSION ":\n " APL_NAME " [options]\n\n"
232 "where options include:\n");
233
234 for (i = 0; i < optList_size(); i++)
235 fprintf (stderr, " %-29s%s\n", optList[i].opt, optList[i].desc);
236 }
237
238
239 /****************
240 * Check out if we are using a maildir drop (qmail)
241 * Note: this changes mail_dir to hold the "new" diretory
242 */
243 #ifdef MAIL
244 static void
245 CheckMaildir()
246 {
247 struct stat st;
248 char *buf, *p;
249
250 if( !*mail_file || stat(mail_file, &st) || !S_ISDIR(st.st_mode) )
251 return; /* no */
252
253 if( !(buf = malloc(strlen(mail_file)+5)) ) {
254 print_error ("malloc error");
255 exit( EXIT_FAILURE );
256 }
257 strcpy(buf,mail_file);
258 p = buf+strlen(buf);
259 if( p[-1] != '/' )
260 *p++ = '/';
261
262 strcpy(p, "tmp" );
263 if( stat(buf, &st) || !S_ISDIR(st.st_mode ) )
264 goto leave;
265 strcpy(p, "cur" );
266 if( stat(buf, &st) || !S_ISDIR(st.st_mode ) )
267 goto leave;
268 strcpy(p, "new" );
269 if( stat(buf, &st) || !S_ISDIR(st.st_mode ) )
270 goto leave;
271
272 mail_file = buf;
273 is_maildir = 1;
274 return;
275 leave:
276 free(buf);
277 }
278 #endif
279
280 /*----------------------------------------------------------------------*
281 * rclock - Rob's clock
282 * simple X windows clock with appointment reminder
283 *----------------------------------------------------------------------*/
284 int
285 main (int argc, char * argv [])
286 {
287 int i;
288 char * opt, * val;
289 const char * display_name = NULL;
290 XGCValues gcv;
291
292 #ifdef REMINDERS
293 const char * rs_font = FONT_NAME;
294
295 /* find the ~/.rclock file */
296 if ((val = getenv ("HOME")) != NULL)
297 {
298 char * p = malloc (strlen (CONFIG_FILE) + strlen (val) + 2);
299 if (p == NULL)
300 goto Malloc_Error;
301
302 strcpy (p, val);
303 strcat (p, "/" CONFIG_FILE);
304
305 reminders_file = p;
306 }
307 #endif
308 #ifdef MAIL
309 val = getenv ("MAIL"); /* get the mail spool file name */
310 #ifdef MAIL_SPOOL
311 if (val == NULL) /* csh doesn't set $MAIL */
312 {
313 const char * spool = MAIL_SPOOL;
314 char * user = getenv ("USER"); /* assume this works */
315 val = malloc (strlen (spool) + strlen (user) + 1);
316 if (val == NULL)
317 goto Malloc_Error;
318 strcpy (val, spool);
319 strcat (val, user);
320 }
321 #endif
322 mail_file = val;
323 if( mail_file )
324 CheckMaildir();
325 #endif
326
327 if ((display_name = getenv ("DISPLAY")) == NULL)
328 display_name = ":0";
329
330 /* parse the command line */
331 for (i = 1; i < argc; i += 2)
332 {
333 opt = argv [i];
334 val = argv [i+1];
335
336 switch (*opt++) {
337 case '-':
338 break;
339
340 case '#':
341 #ifdef ICONWIN
342 rs_iconGeometry = opt; /* drop */
343 #endif
344 default:
345 continue;
346 break;
347 }
348
349 if (*opt == 'd' && val) display_name = val; /* "d", "display" */
350 else if (*opt == 'g' && val) rs_geometry = val; /* "g", "geometry" */
351 #ifdef ICONWIN
352 else if (*opt == 'i') /* "ic", "iconic" */
353 {
354 iconic_state = IconicState;
355 i--; /* no argument */
356 }
357 #endif
358 else if (!strcmp (opt, "fg") && val) rs_color [fgColor] = val;
359 else if (!strcmp (opt, "bg") && val) rs_color [bgColor] = val;
360 #ifdef REMINDERS
361 else if (!strcmp (opt, "fn") && val) rs_font = val;
362 #ifdef DATE_ON_CLOCK_FACE
363 else if (!strcmp (opt, "nodate"))
364 {
365 show_date = 0;
366 i--; /* no argument */
367 }
368 #endif
369 #endif
370 else if (!strcmp (opt, "update") && val)
371 {
372 int x = atoi (val);
373 if (x < 1 || x > 60)
374 print_error ("update: %d sec", clockUpdate);
375 else
376 clockUpdate = x;
377 }
378 #ifdef MAIL
379 else if (!strcmp (opt, "mail") && val)
380 {
381 int x = atoi (val);
382 if (x < 1)
383 print_error ("mail update: %d sec", mailUpdate);
384 else
385 mailUpdate = x;
386 }
387 else if (!strcmp (opt, "mailfile") && val)
388 {
389 /* If the mail environment is not set, then mail_file was created
390 * with a malloc. We need to free it.
391 */
392 if( getenv ("MAIL") == NULL)
393 free( mail_file);
394 /* assume user knows what he's doing, don't check that file is valid...*/
395 mail_file = val;
396 }
397 #ifndef MAIL_SPAWN
398 else if (!strcmp (opt, "mailspawn") && val)
399 {
400 mail_spawn = val;
401 }
402 #endif
403 #endif /* MAIL */
404 #ifdef ADJUST_TIME
405 else if (!strcmp (opt, "adjust") && val)
406 {
407 /* convert ddhhmm to seconds, minimal error checking */
408 int x = atoi (val);
409 adjustTime = ((((abs (x) / 10000) % 100) * 24 /* days */
410 + ((abs (x) / 100) % 100)) * 60 /* hours */
411 + (abs (x) % 100)) * 60; /* minutes */
412 if (x < 0)
413 adjustTime = -adjustTime;
414 }
415 #endif /* ADJUST_TIME */
416 else
417 {
418 usage ();
419 goto Abort;
420 }
421 }
422
423 /* open display */
424 Xdisplay = XOpenDisplay (display_name);
425 if (!Xdisplay)
426 {
427 print_error ("can't open display %s", display_name);
428 goto Abort;
429 }
430
431 /* get display info */
432 Xfd = XConnectionNumber (Xdisplay);
433 {
434 const char * const color_msg = "can't load color \"%s\"";
435 XColor xcol;
436
437 /* allocate foreground/background colors */
438 if (!XParseColor (Xdisplay, Xcmap, rs_color [fgColor], &xcol) ||
439 !XAllocColor (Xdisplay, Xcmap, &xcol))
440 {
441 print_error (color_msg, rs_color [fgColor]);
442 goto Abort;
443 }
444 PixColors [fgColor] = xcol.pixel;
445
446 if (!XParseColor (Xdisplay, Xcmap, rs_color [bgColor], &xcol) ||
447 !XAllocColor (Xdisplay, Xcmap, &xcol))
448 {
449 print_error (color_msg, rs_color [bgColor]);
450 goto Abort;
451 }
452 PixColors [bgColor] = xcol.pixel;
453 }
454
455 #ifdef REMINDERS
456 /* load the font for messages */
457 if ((Xfont = XLoadQueryFont (Xdisplay, rs_font)) == NULL)
458 {
459 print_error ("can't load font \"%s\"", rs_font);
460 goto Abort;
461 }
462 gcv.font = Xfont->fid;
463 #endif
464
465 Create_Windows (argc, argv);
466 /* Create the graphics contexts */
467 gcv.foreground = PixColors [fgColor];
468 gcv.background = PixColors [bgColor];
469
470 Xgc = XCreateGC (Xdisplay, Clock.win,
471 #ifdef REMINDERS
472 GCFont |
473 #endif
474 GCForeground | GCBackground, &gcv);
475
476 gcv.foreground = PixColors [bgColor];
477 gcv.background = PixColors [fgColor];
478 Xrvgc = XCreateGC (Xdisplay, Clock.win,
479 #ifdef REMINDERS
480 GCFont |
481 #endif
482 GCForeground | GCBackground, &gcv);
483
484 getXevent ();
485 return EXIT_SUCCESS;
486
487 Malloc_Error:
488 print_error ("malloc error");
489 Abort:
490 print_error ("aborting");
491 return EXIT_FAILURE;
492 }
493
494 /*
495 * translate geometry string to appropriate sizehint
496 */
497 static void
498 geometry2sizehint (mywindow_t * win, const char * geom)
499 {
500 int x, y, flags;
501 unsigned int width, height;
502
503 /* copy in values */
504 szHint.width = win->width;
505 szHint.height = win->height;
506
507 if (geom == NULL)
508 return;
509
510 flags = XParseGeometry (geom, &x, &y, &width, &height);
511
512 if (flags & WidthValue)
513 {
514 szHint.width = width + szHint.base_width;
515 szHint.flags |= USSize;
516 }
517 if (flags & HeightValue)
518 {
519 szHint.height = height + szHint.base_height;
520 szHint.flags |= USSize;
521 }
522
523 if (flags & XValue)
524 {
525 if (flags & XNegative)
526 {
527 x += (DisplayWidth (Xdisplay, Xscreen) - szHint.width);
528 szHint.win_gravity = NorthEastGravity;
529 }
530 szHint.x = x;
531 szHint.flags |= USPosition;
532 }
533 if (flags & YValue)
534 {
535 if (flags & YNegative)
536 {
537 y += (DisplayHeight (Xdisplay, Xscreen) - szHint.height);
538 szHint.win_gravity = (szHint.win_gravity == NorthEastGravity ?
539 SouthEastGravity : SouthWestGravity);
540 }
541 szHint.y = y;
542 szHint.flags |= USPosition;
543 }
544
545 /* copy out values */
546 win->width = szHint.width;
547 win->height = szHint.height;
548 }
549
550 /*
551 * Open and map the windows
552 */
553 static void
554 Create_Windows (int argc, char * argv [])
555 {
556 XClassHint classHint;
557 XWMHints wmHint;
558
559 geometry2sizehint (&Clock, rs_geometry);
560 Clock.win = XCreateSimpleWindow (Xdisplay, Xroot,
561 szHint.x, szHint.y,
562 Clock.width, Clock.height,
563 0,
564 PixColors [fgColor],
565 PixColors [bgColor]);
566
567 #ifdef ICONWIN
568 geometry2sizehint (&Icon, rs_iconGeometry);
569 Icon.win = XCreateSimpleWindow (Xdisplay, Xroot,
570 szHint.x, szHint.y,
571 Icon.width, Icon.height,
572 0,
573 PixColors [fgColor],
574 PixColors [bgColor]);
575 wmHint.initial_state = iconic_state;
576 wmHint.icon_window = Icon.win;
577 wmHint.flags = InputHint | StateHint | IconWindowHint;
578 #else
579 wmHint.flags = InputHint;
580 #endif
581 wmHint.input = True;
582
583 /* ignore warning about discarded `const' */
584 classHint.res_name = APL_NAME;
585 classHint.res_class = APL_CLASS;
586 XSetWMProperties (Xdisplay, Clock.win, NULL, NULL, argv, argc,
587 &szHint, &wmHint, &classHint);
588
589 XSelectInput (Xdisplay, Clock.win,
590 (ExposureMask|StructureNotifyMask|ButtonPressMask));
591
592 #ifdef ICONWIN
593 XSelectInput (Xdisplay, Icon.win,
594 (ExposureMask|ButtonPressMask));
595 #endif
596 XMapWindow (Xdisplay, Clock.win);
597
598 /* create, but don't map a window for appointment reminders */
599 #ifdef REMINDERS
600 Msg.win = XCreateSimpleWindow (Xdisplay, Xroot,
601 szHint.x, szHint.y,
602 szHint.width, szHint.height,
603 0,
604 PixColors [fgColor],
605 PixColors [bgColor]);
606
607 szHint.flags |= USPosition;
608 /* ignore warning about discarded `const' */
609 classHint.res_name = MSG_NAME;
610 classHint.res_class = MSG_CLASS;
611 wmHint.input = True;
612 wmHint.flags = InputHint;
613
614 XSetWMProperties (Xdisplay, Msg.win, NULL, NULL, argv, argc,
615 &szHint, &wmHint, &classHint);
616 {
617 char * str = MSG_NAME;
618 XStoreName (Xdisplay, Msg.win, str);
619 XSetIconName (Xdisplay, Msg.win, str);
620 }
621
622 XSelectInput (Xdisplay, Msg.win,
623 (ExposureMask|ButtonPressMask|KeyPressMask));
624
625 /* font already loaded */
626
627 msgButton.width = 4 + 5 * XTextWidth (Xfont, "M", 1);
628 msgButton.height = 4 + FontHeight ();
629
630 msgButton.Dismiss = XCreateSimpleWindow (Xdisplay, Msg.win,
631 0, 0,
632 msgButton.width, msgButton.height,
633 0,
634 PixColors [bgColor],
635 PixColors [fgColor]);
636
637 XMapWindow (Xdisplay, msgButton.Dismiss);
638
639 msgButton.Defer = XCreateSimpleWindow (Xdisplay, Msg.win,
640 0, 0,
641 msgButton.width, msgButton.height,
642 0,
643 PixColors [bgColor],
644 PixColors [fgColor]);
645 XMapWindow (Xdisplay, msgButton.Defer);
646
647 #ifndef NO_REMINDER_EXEC
648 msgButton.Start = XCreateSimpleWindow (Xdisplay, Msg.win,
649 0, 0,
650 msgButton.width, msgButton.height,
651 0,
652 PixColors [bgColor],
653 PixColors [fgColor]);
654 XMapWindow (Xdisplay, msgButton.Start);
655 #endif /* NO_REMINDER_EXEC */
656 #endif
657 }
658
659 static time_t
660 mk_time (struct tm * tmval)
661 {
662 return (tmval->tm_min
663 + 60 * (tmval->tm_hour
664 + 24 * (tmval->tm_mday
665 + 31 * ((tmval->tm_mon+1)
666 + 12 * tmval->tm_year))));
667 }
668
669
670 #ifdef MAIL
671 static int
672 MailAvailable()
673 {
674 struct stat st;
675
676 if( is_maildir ) {
677 DIR *dirp;
678 struct dirent *d;
679
680 if( (dirp=opendir( mail_file )) ) {
681 while( (d=readdir(dirp)) ) {
682 if( *d->d_name == '.' )
683 continue;
684 if( isdigit(*d->d_name) ) {
685 closedir(dirp);
686 return 1;
687 }
688 }
689 closedir(dirp);
690 }
691 return 0;
692 }
693 else
694 return !stat(mail_file, &st) &&
695 (st.st_size > 0) && (st.st_mtime >= st.st_atime);
696 }
697 #endif
698
699 /*----------------------------------------------------------------------*
700 * Redraw the whole window after an exposure or size change.
701 * After a timeout, only redraw the hands.
702 * Provide reminder if needed.
703 *----------------------------------------------------------------------*/
704 static void
705 Draw_Window (mywindow_t * W, int full_redraw)
706 {
707 /* pre-computed values for sin() x1000, to avoid using floats */
708 static const short Sin [60] = {
709 0,
710 105, 208, 309, 407, 500, 588, 669,
711 743, 809, 866, 914, 951, 978, 995,
712 1000,
713 995, 978, 951, 914, 866, 809, 743,
714 669, 588, 500, 407, 309, 208, 105,
715 0,
716 -105, -208, -309, -407, -500, -588, -669,
717 -743, -809, -866, -914, -951, -978, -995,
718 -1000,
719 -995, -978, -951, -914, -866, -809, -743,
720 -669, -588, -500, -407, -309, -208, -105
721 };
722
723 static int savedDay = -1;
724
725 time_t currentTime;
726 struct tm * tmval;
727 int ctr_x, ctr_y;
728
729 typedef struct {
730 int h_x, h_y; /* hour */
731 int m_x, m_y; /* minute */
732 int s_x, s_y; /* second */
733 } hands_t; /* hand positions (x,y) */
734
735 hands_t HandsNow, * pHandsOld;
736
737 GC X_gc, X_rvgc;
738
739 static hands_t HandsOld = { -1 };
740 #ifdef ICONWIN
741 static hands_t HandsOld_icon = { -1 };
742 #endif
743 #ifdef REMINDERS
744 static int lastUpdateTime = -10;
745 #endif
746
747 #ifdef MAIL
748 static time_t mailTime = 0;
749 static int MailUp = 0, MailUp_rvideo = 0;
750 #ifdef ICONWIN
751 static int MailUp_icon = 0;
752 #endif
753 #endif /* MAIL */
754
755 currentTime = time (NULL) + adjustTime; /* get the current time */
756 tmval = localtime (&currentTime);
757
758 #ifdef MAIL
759 #ifdef REMINDERS
760 if (W->win != Msg.win)
761 #endif
762 {
763 int * pMailUp = (
764 #ifdef ICONWIN
765 W->win == Icon.win ? &MailUp_icon :
766 #endif
767 &MailUp);
768
769 if ((currentTime - mailTime) >= mailUpdate)
770 {
771 struct stat st;
772
773 if (
774 #ifdef ICONWIN
775 MailUp != MailUp_icon ? MailUp :
776 #endif
777 ((mail_file != NULL) && MailAvailable() ) )
778 {
779 if (!*pMailUp)
780 {
781 *pMailUp = 1;
782 full_redraw = 1;
783 XSetWindowBackground (Xdisplay, W->win,
784 PixColors [fgColor]);
785 #ifdef MAIL_BELL
786 XBell (Xdisplay, 0);
787 #endif
788 }
789 }
790 else
791 {
792 if (*pMailUp)
793 {
794 *pMailUp = 0;
795 full_redraw = 1;
796 XSetWindowBackground (Xdisplay, W->win,
797 PixColors [bgColor]);
798 }
799 }
800 #ifdef ICONWIN
801 if (MailUp == MailUp_icon)
802 #endif
803 mailTime = currentTime;
804
805 MailUp_rvideo = *pMailUp;
806 }
807 }
808 #endif /* MAIL */
809
810 /* once every day, update the window and icon name */
811 if (tmval->tm_yday != savedDay)
812 {
813 char str [20];
814
815 savedDay = tmval->tm_yday;
816 strftime (str, sizeof(str), "%a %h %d", tmval);
817 XStoreName (Xdisplay, Clock.win, str);
818 XSetIconName (Xdisplay, Clock.win, str);
819 }
820
821 if (full_redraw)
822 XClearWindow (Xdisplay, W->win);
823
824 #ifdef REMINDERS
825 /* for a message window, just re-draw the message */
826 if (W->win == Msg.win)
827 {
828 char * beg, * next;
829 int lines;
830
831 for (beg = message, lines = 0; beg; beg = next, lines++)
832 {
833 char * end;
834
835 if ((end = strstr (beg, "\\n")) == NULL)
836 {
837 end = beg + strlen (beg);
838 next = NULL;
839 }
840 else
841 {
842 next = end + 2;
843 }
844
845 XDrawString (Xdisplay, Msg.win,
846 Xgc,
847 (Msg.width -
848 XTextWidth (Xfont, beg, (end-beg))) / 2,
849 10 + Xfont->ascent + FontHeight () * lines,
850 beg, (end-beg));
851 }
852
853 XDrawString (Xdisplay, msgButton.Dismiss,
854 Xrvgc,
855 (msgButton.width - XTextWidth (Xfont, "Done", 4)) / 2,
856 Xfont->ascent + 2,
857 "Done", 4);
858
859 XDrawString (Xdisplay, msgButton.Defer,
860 Xrvgc,
861 (msgButton.width - XTextWidth (Xfont, "Defer", 5)) / 2,
862 Xfont->ascent + 2,
863 "Defer", 5);
864
865 # ifndef NO_REMINDER_EXEC
866 XDrawString (Xdisplay, msgButton.Start,
867 Xrvgc,
868 (msgButton.width - XTextWidth (Xfont, "Start", 5)) / 2,
869 Xfont->ascent + 2,
870 "Start", 5);
871
872 if (strlen (execPrgm) > 1)
873 XMapWindow (Xdisplay, msgButton.Start);
874 else
875 XUnmapWindow (Xdisplay, msgButton.Start);
876 # endif /* NO_REMINDER_EXEC */
877 return;
878 }
879
880 /*
881 * Convert multi-field time info to a single integer with a resolution
882 * in minutes.
883 */
884 currentTime = mk_time (tmval);
885
886 /* is there a reminder pending? */
887 if (reminderTime >= 0 && currentTime >= reminderTime)
888 Reminder ();
889
890 /* every 10 minutes, or at start of day, check for revised entries */
891 if (!Msg_Mapped &&
892 (currentTime > lastUpdateTime + REMINDERS_TIME ||
893 (currentTime != lastUpdateTime &&
894 tmval->tm_hour == 0 && tmval->tm_min == 0)))
895 {
896 Next_Reminder (UPDATE);
897 lastUpdateTime = currentTime;
898 }
899 #endif
900
901 /*
902 * draw clock
903 */
904
905 ctr_x = (W->width / 2);
906 ctr_y = (W->height / 2);
907
908 #define XPOS(i,val) (ctr_x + (W->width * Sin[i%60] * (val) + 100000) / 200000)
909 #define YPOS(i,val) (ctr_y - (W->height * Sin[(i+15)%60] * (val) + 100000) / 200000)
910 /*
911 * how to draw the clock face
912 */
913
914 /* calculate the positions of the hands */
915 {
916 int angle = (tmval->tm_hour % 12) * 5 + (tmval->tm_min / 12);
917 HandsNow.h_x = XPOS (angle, 60);
918 HandsNow.h_y = YPOS (angle, 60);
919 }
920 {
921 int angle = tmval->tm_min;
922 HandsNow.m_x = XPOS (angle, 80);
923 HandsNow.m_y = YPOS (angle, 80);
924 }
925 if (clockUpdate == 1)
926 {
927 int angle = tmval->tm_sec;
928 HandsNow.s_x = XPOS (angle, 85);
929 HandsNow.s_y = YPOS (angle, 85);
930 }
931
932 pHandsOld = (
933 #ifdef ICONWIN
934 W->win == Icon.win ? &HandsOld_icon :
935 #endif
936 &HandsOld);
937
938 #ifdef MAIL
939 if (MailUp_rvideo)
940 {
941 X_gc = Xrvgc;
942 X_rvgc = Xgc;
943 }
944 else
945 #endif
946 {
947 X_gc = Xgc;
948 X_rvgc = Xrvgc;
949 }
950
951 /*
952 * Draw the date in the lower half of the clock window.
953 * The code is enclosed in REMINDERS because it uses the same
954 * font as the reminders code.
955 * I believe this should be drawn always so it does not get
956 * "swept away" by the minute hand.
957 */
958 #ifdef REMINDERS && DATE_ON_CLOCK_FACE
959 if( show_date)
960 {
961 char date[10];
962 currentTime = time (NULL) + adjustTime; /* get the current time */
963 tmval = localtime (&currentTime);
964 strftime (date, sizeof(date), "%d", tmval);
965 XDrawString (Xdisplay, W->win, X_gc,
966 ctr_x - XTextWidth(Xfont, date, strlen(date))/2,
967 ctr_y + FontHeight() + (ctr_y - FontHeight())/2,
968 date, strlen(date));
969 }
970 #endif
971
972 if (full_redraw)
973 {
974 int angle;
975 /*
976 * draw clock face
977 */
978 #ifdef SUBTICKS
979 for (angle = 0; angle < 60; angle++)
980 XDrawPoint (Xdisplay, W->win, X_gc,
981 XPOS (angle, 95),
982 YPOS (angle, 95));
983 #endif
984 for (angle = 0; angle < 60; angle += 5)
985 XDrawLine (Xdisplay, W->win, X_gc,
986 XPOS (angle, 90),
987 YPOS (angle, 90),
988 XPOS (angle, 100),
989 YPOS (angle, 100));
990 }
991 else if (memcmp (pHandsOld, &HandsNow, sizeof(hands_t)))
992 {
993 int i, j;
994 /*
995 * erase old hands
996 */
997 for (i = -1; i < 2; i++) for (j = -1; j < 2; j++)
998 {
999 /* hour/minute hands */
1000 XDrawLine (Xdisplay, W->win, X_rvgc,
1001 ctr_x + i,
1002 ctr_y + j,
1003 pHandsOld->h_x, pHandsOld->h_y);
1004 XDrawLine (Xdisplay, W->win, X_rvgc,
1005 ctr_x + i,
1006 ctr_y + j,
1007 pHandsOld->m_x, pHandsOld->m_y);
1008 }
1009
1010 if (clockUpdate == 1) /* seconds hand */
1011 XDrawLine (Xdisplay,
1012 W->win, X_rvgc,
1013 ctr_x,
1014 ctr_y,
1015 pHandsOld->s_x, pHandsOld->s_y);
1016 }
1017
1018 if (full_redraw || memcmp (pHandsOld, &HandsNow, sizeof(hands_t)))
1019 {
1020 int i, j;
1021 /*
1022 * draw new hands
1023 */
1024 for (i = -1; i < 2; i++) for (j = -1; j < 2; j++)
1025 {
1026 /* hour/minute hands */
1027 XDrawLine (Xdisplay, W->win, X_gc,
1028 ctr_x + i,
1029 ctr_y + j,
1030 HandsNow.h_x, HandsNow.h_y);
1031
1032 XDrawLine (Xdisplay, W->win, X_gc,
1033 ctr_x + i,
1034 ctr_y + j,
1035 HandsNow.m_x, HandsNow.m_y);
1036 }
1037 if (clockUpdate == 1) /* seconds hand */
1038 XDrawLine (Xdisplay, W->win, X_gc,
1039 ctr_x,
1040 ctr_y,
1041 HandsNow.s_x, HandsNow.s_y);
1042
1043 *pHandsOld = HandsNow;
1044 }
1045 }
1046
1047 #ifdef REMINDERS
1048 /*
1049 * Read a single integer from *pstr, returns default value if it finds "*"
1050 * DELIM = trailing delimiter to skip
1051 */
1052 static int
1053 GetOneNum (char ** pstr, int def)
1054 {
1055 int num, hit = 0;
1056
1057 for (num = 0; isdigit (**pstr); (*pstr)++)
1058 {
1059 num = num * 10 + (**pstr - '0');
1060 hit = 1;
1061 }
1062 if (!hit)
1063 {
1064 num = def;
1065 while (**pstr == '*') (*pstr)++;
1066 }
1067 return num;
1068 }
1069
1070 /*
1071 * find if TODAY is found in PSTR
1072 */
1073 static int
1074 isToday (char ** pstr, int wday)
1075 {
1076 const char * dayNames = DAY_NAMES;
1077 int rval, today;
1078
1079 today = dayNames [wday];
1080 /* no day specified is same as wildcard */
1081 if (!strchr (dayNames, tolower (**pstr)))
1082 return 1;
1083
1084 for (rval = 0; strchr (dayNames, tolower (**pstr)); (*pstr)++)
1085 {
1086 if (today == tolower (**pstr) || **pstr == '*')
1087 rval = 1; /* found it */
1088 }
1089 return rval;
1090 }
1091
1092 static char *
1093 trim_string (char * str)
1094 {
1095 if (str && *str)
1096 {
1097 int n;
1098 while (*str && isspace (*str)) str++;
1099
1100 n = strlen (str) - 1;
1101 while (n > 0 && isspace (str [n])) n--;
1102 str [n+1] = '\0';
1103 }
1104 return str;
1105 }
1106
1107 # ifndef NO_REMINDER_EXEC
1108 static char *
1109 extract_program (char * text)
1110 {
1111 char * prgm = text;
1112 while ((prgm = strchr (prgm, ';')) != NULL)
1113 {
1114 if (*(prgm-1) == '\\') /* backslash escaped */
1115 {
1116 /* remove backslash - avoid memmove() */
1117 int i, n = strlen (prgm);
1118 for (i = 0; i <= n; i++)
1119 prgm [i - 1] = prgm [i];
1120 }
1121 else
1122 {
1123 *prgm++ = '\0';
1124 /* remove leading/trailing space */
1125 prgm = trim_string (prgm);
1126 break;
1127 }
1128 }
1129 return prgm;
1130 }
1131 # endif /* NO_REMINDER_EXEC */
1132
1133 /*
1134 * Read the ~/.rclock file and find the next reminder
1135 *
1136 * update_only = 1
1137 * look for a reminder whose time is greater than the current time,
1138 * but less than the currently set reminder time
1139 *
1140 * update_only = 0
1141 * look for a reminder whose time is greater than the reminder that
1142 * just went off
1143 */
1144 static void
1145 Next_Reminder (int update_only)
1146 {
1147 struct tm * tmval;
1148 char buffer [256];
1149 #ifndef INT_MAX
1150 # define INT_MAX 1e8
1151 #endif
1152 time_t currentTime;
1153 int savedTime = INT_MAX;
1154 FILE * fd;
1155
1156 if (reminders_file == NULL || (fd = fopen (reminders_file, "r")) == NULL)
1157 {
1158 reminderTime = -1; /* no config file, no reminders */
1159 return;
1160 }
1161
1162 currentTime = time (NULL) + adjustTime; /* get the current time */
1163 tmval = localtime (&currentTime);
1164 currentTime = mk_time (tmval);
1165
1166 /* initial startup*/
1167 if (reminderTime < 0)
1168 {
1169 /* ignore reminders that have already occurred */
1170 reminderTime = currentTime;
1171 # ifndef NO_REMINDER_EXEC
1172 /* scan for programs run on start-up */
1173 while (fgets (buffer, sizeof(buffer), fd))
1174 {
1175 char * prgm, * text;
1176
1177 text = trim_string (buffer);
1178 if (*text != ';') continue;
1179
1180 prgm = extract_program (text);
1181 if (prgm != NULL && strlen (prgm) > 1)
1182 system (prgm);
1183 }
1184 rewind (fd);
1185 # endif /* NO_REMINDER_EXEC */
1186 }
1187
1188 /* now scan for next reminder */
1189 while (fgets (buffer, sizeof(buffer), fd))
1190 {
1191 int testTime, hh, mm, mo, dd, yy;
1192 char * text;
1193
1194 text = trim_string (buffer);
1195 if (*text == '#') continue; /* comment */
1196 if (*text == ';') continue; /* program run on startup */
1197 /*
1198 * parse the line, format is hh:mm mo/dd/yr message; program
1199 * any of hh, mm, mo, dd, yr could be a wildcard `*'
1200 */
1201 hh = GetOneNum (&text, tmval->tm_hour); if (*text == ':') text++;
1202 mm = GetOneNum (&text, 0);
1203
1204 while (isspace (*text)) text++;
1205 if (!isToday (&text, tmval->tm_wday)) continue;
1206 while (isspace (*text)) text++;
1207
1208 mo = GetOneNum (&text, tmval->tm_mon+1); if (*text == '/') text++;
1209 dd = GetOneNum (&text, tmval->tm_mday); if (*text == '/') text++;
1210 yy = GetOneNum (&text, tmval->tm_year);
1211
1212 /* handle 20th/21st centuries */
1213 if (yy > CENTURY)
1214 yy -= 1900;
1215 else if (yy < CENTURY)
1216 yy += (CENTURY - 1900);
1217
1218 while (isspace (*text)) text++;
1219 if (!*text) continue;
1220
1221 testTime = (mm + 60 * (hh + 24 * (dd + 31 * (mo + 12 * yy))));
1222
1223 if (testTime > (update_only ? currentTime : reminderTime))
1224 {
1225 #ifndef NO_REMINDER_EXEC
1226 char * prgm = extract_program (text);
1227 #endif /* NO_REMINDER_EXEC */
1228 /* trim leading/trailing space */
1229 text = trim_string (text);
1230
1231 /*
1232 * have a reminder whose time is greater than the last
1233 * reminder, now make sure it is the smallest available
1234 */
1235 if (testTime < savedTime)
1236 {
1237 savedTime = testTime;
1238 strncpy (message, text, sizeof(message));
1239 #ifndef NO_REMINDER_EXEC
1240 strncpy (execPrgm, (prgm ? prgm : ""), sizeof(execPrgm));
1241 #endif
1242 }
1243 else if (testTime == savedTime)
1244 {
1245 if (strlen (text))
1246 {
1247 int n = (sizeof(message) - strlen (message) - 3);
1248 if (n > 0)
1249 {
1250 /* for co-occurring events */
1251 strcat (message, "\\n");
1252 strncat (message, text, n);
1253 }
1254 }
1255 #ifndef NO_REMINDER_EXEC
1256 if (prgm != NULL)
1257 {
1258 int n = (sizeof(execPrgm) - strlen (execPrgm) - 2);
1259 if ((n > 0) && (n >= strlen (prgm)))
1260 {
1261 /* for co-occurring programs */
1262 strcat (execPrgm, ";");
1263 strncat (execPrgm, prgm, n);
1264 }
1265 }
1266 #endif /* NO_REMINDER_EXEC */
1267 }
1268 }
1269 }
1270
1271 reminderTime = (savedTime < INT_MAX) ? savedTime : -1;
1272 fclose (fd);
1273 }
1274
1275 /*
1276 * Provide reminder by mapping the message window
1277 */
1278 static void
1279 Reminder (void)
1280 {
1281 char * beg, * next;
1282 int lines;
1283
1284 if (Msg_Mapped)
1285 return;
1286
1287 #ifndef NO_REMINDER_EXEC
1288 if (strlen (message) == 0)
1289 {
1290 if (strlen (execPrgm) > 1)
1291 {
1292 system (execPrgm);
1293 Next_Reminder (REPLACE);
1294 }
1295 return; /* some sort of error */
1296 }
1297 #endif
1298
1299 /* compute the window size */
1300 #ifdef NO_REMINDER_EXEC
1301 Msg.width = 10 * XTextWidth (Xfont, "M", 1);
1302 #else
1303 Msg.width = 18 * XTextWidth (Xfont, "M", 1);
1304 #endif
1305
1306 for (beg = message, lines = 1; beg; beg = next, lines++)
1307 {
1308 int width;
1309 char * end;
1310
1311 if ((end = strstr (beg, "\\n")) == NULL)
1312 {
1313 end = beg + strlen (beg);
1314 next = NULL;
1315 }
1316 else
1317 {
1318 next = end + 2;
1319 }
1320
1321 width = XTextWidth (Xfont, beg, (end-beg));
1322 if (Msg.width < width)
1323 Msg.width = width;
1324 }
1325
1326 Msg.width += 30;
1327 Msg.height = (lines+1) * FontHeight () + 30;
1328
1329 /* resize and centre the window */
1330 XMoveResizeWindow (Xdisplay, Msg.win,
1331 (DisplayWidth (Xdisplay, Xscreen) - Msg.width ) / 2,
1332 (DisplayHeight (Xdisplay, Xscreen) - Msg.height) / 2,
1333 Msg.width, Msg.height);
1334
1335 #define BUTTON_MARGIN 8
1336
1337 XMoveWindow (Xdisplay, msgButton.Dismiss,
1338 BUTTON_MARGIN,
1339 (Msg.height - msgButton.height - BUTTON_MARGIN));
1340 XMoveWindow (Xdisplay, msgButton.Defer,
1341 (Msg.width - msgButton.width - BUTTON_MARGIN),
1342 (Msg.height - msgButton.height - BUTTON_MARGIN));
1343 #ifndef NO_REMINDER_EXEC
1344 XMoveWindow (Xdisplay, msgButton.Start,
1345 (Msg.width - msgButton.width) / 2,
1346 (Msg.height - msgButton.height - BUTTON_MARGIN));
1347 #endif
1348
1349 XMapRaised (Xdisplay, Msg.win);
1350 XBell (Xdisplay, 0);
1351 Msg_Mapped = 1;
1352 }
1353 #endif /* REMINDERS */
1354
1355 #ifndef _POSIX_VERSION
1356 # if defined (__svr4__)
1357 static int
1358 getdtablesize (void)
1359 {
1360 struct rlimit rlim;
1361 getrlimit (RLIMIT_NOFILE, &rlim);
1362 return rlim.rlim_cur;
1363 }
1364 # endif
1365 #endif
1366
1367 /*
1368 * Loops forever, looking for stuff to do. Sleeps 1 minute if nothing to do
1369 */
1370 static void
1371 getXevent (void)
1372 {
1373 XEvent ev;
1374 int num_fds; /* number of file descriptors being used */
1375 struct timeval tm;
1376 struct tm * tmval;
1377 Atom wmDeleteWindow;
1378 fd_set in_fdset;
1379
1380 /* Enable delete window protocol */
1381 wmDeleteWindow = XInternAtom (Xdisplay, "WM_DELETE_WINDOW", False);
1382 XSetWMProtocols (Xdisplay, Clock.win, &wmDeleteWindow, 1);
1383 #ifdef ICONWIN
1384 XSetWMProtocols (Xdisplay, Icon.win, &wmDeleteWindow, 1);
1385 #endif
1386 #ifdef REMINDERS
1387 XSetWMProtocols (Xdisplay, Msg.win, &wmDeleteWindow, 1);
1388 #endif
1389
1390 #ifdef _POSIX_VERSION
1391 num_fds = sysconf (_SC_OPEN_MAX);
1392 #else
1393 num_fds = getdtablesize ();
1394 #endif
1395 #ifdef FD_SETSIZE
1396 if (num_fds > FD_SETSIZE)
1397 num_fds = FD_SETSIZE;
1398 #endif
1399
1400 while (1) {
1401 /* take care of all pending X events */
1402 while (XPending (Xdisplay)) {
1403 XNextEvent (Xdisplay, &ev);
1404 switch (ev.type) {
1405 case ClientMessage:
1406 /* check for delete window requests */
1407 if ((ev.xclient.format == 32) &&
1408 (ev.xclient.data.l[0] == wmDeleteWindow))
1409 {
1410 #ifdef REMINDERS
1411 if (ev.xany.window == Msg.win)
1412 {
1413 XUnmapWindow (Xdisplay, Msg.win);
1414 Msg_Mapped = 0;
1415 Next_Reminder (REPLACE);
1416 }
1417 else
1418 #endif
1419 return; /* delete window is how this terminates */
1420 }
1421 break;
1422
1423 case Expose:
1424 case GraphicsExpose:
1425 /* need to re-draw a window */
1426 if (ev.xany.window == Clock.win)
1427 Draw_Window (&Clock, 1);
1428 #ifdef ICONWIN
1429 else if (ev.xany.window == Icon.win)
1430 Draw_Window (&Icon, 1);
1431 #endif
1432 #ifdef REMINDERS
1433 else
1434 Draw_Window (&Msg, 1);
1435 #endif
1436 break;
1437
1438 case ConfigureNotify:
1439 /* window has been re-sized */
1440 if (ev.xany.window == Clock.win)
1441 {
1442 Clock.width = ev.xconfigure.width;
1443 Clock.height = ev.xconfigure.height;
1444 }
1445 break;
1446
1447 #ifdef REMINDERS
1448 case KeyPress:
1449 /* any key press to dismiss message window */
1450 if (ev.xany.window == Msg.win)
1451 {
1452 Next_Reminder (REPLACE);
1453 Msg_Mapped = 0;
1454 XUnmapWindow (Xdisplay, Msg.win);
1455 }
1456 break;
1457 #endif
1458
1459 case ButtonPress:
1460 #ifdef REMINDERS
1461 /* button press to dismiss message window */
1462 if (ev.xany.window == Msg.win)
1463 {
1464 if (ev.xbutton.subwindow == msgButton.Dismiss)
1465 {
1466 Next_Reminder (REPLACE);
1467 Msg_Mapped = 0;
1468 XUnmapWindow (Xdisplay, Msg.win);
1469 }
1470 else if (ev.xbutton.subwindow == msgButton.Defer)
1471 {
1472 time_t t = time (NULL) + adjustTime;
1473 tmval = localtime (&t);
1474 reminderTime = mk_time (tmval) + DEFER_TIME;
1475 Msg_Mapped = 0;
1476 XUnmapWindow (Xdisplay, Msg.win);
1477 }
1478 #ifndef NO_REMINDER_EXEC
1479 else if (ev.xbutton.subwindow == msgButton.Start)
1480 {
1481 system (execPrgm);
1482 Next_Reminder (REPLACE);
1483 Msg_Mapped = 0;
1484 XUnmapWindow (Xdisplay, Msg.win);
1485 }
1486 #endif /* NO_REMINDER_EXEC */
1487 }
1488 #endif
1489 #ifdef MAIL
1490 if (ev.xany.window == Clock.win)
1491 {
1492 #ifdef MAIL_SPAWN
1493 /* left button action - spawn a mail reader */
1494 if (ev.xbutton.button == Button1)
1495 system (MAIL_SPAWN);
1496 #else
1497 if ( (ev.xbutton.button == Button1) && (mail_spawn != NULL) )
1498 system(mail_spawn);
1499 #endif
1500 /* redraw the window */
1501 Draw_Window (&Clock, 1);
1502 }
1503 #endif
1504 break;
1505 }
1506 }
1507
1508 /* Now wait for time out or new X event */
1509 FD_ZERO (&in_fdset);
1510 FD_SET (Xfd, &in_fdset);
1511 tm.tv_sec = clockUpdate;
1512 tm.tv_usec = 0;
1513 select (num_fds, &in_fdset, NULL, NULL, &tm);
1514
1515 Draw_Window (&Clock, 0);
1516 #ifdef ICONWIN
1517 Draw_Window (&Icon, 0);
1518 #endif
1519 }
1520 }
1521
1522 /*
1523 * Print an error message.
1524 */
1525 static void
1526 print_error (const char * fmt, ...)
1527 {
1528 va_list arg_ptr;
1529
1530 va_start (arg_ptr, fmt);
1531 fprintf (stderr, APL_NAME ": ");
1532 vfprintf (stderr, fmt, arg_ptr);
1533 fprintf (stderr,"\n");
1534 va_end (arg_ptr);
1535 }
1536 /*----------------------- end-of-file (C source) -----------------------*/