1 /* -*- mode: C; mode: fold -*- */
2 /*
3  This file is part of SLRN.
4 
5  Copyright (c) 1994, 1999, 2007-2016 John E. Davis <jed@jedsoft.org>
6  Copyright (c) 2001-2006 Thomas Schultz <tststs@gmx.de>
7 
8  This program is free software; you can redistribute it and/or modify it
9  under the terms of the GNU General Public License as published by the Free
10  Software Foundation; either version 2 of the License, or (at your option)
11  any later version.
12 
13  This program is distributed in the hope that it will be useful, but WITHOUT
14  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  more details.
17 
18  You should have received a copy of the GNU General Public License along
19  with this program; if not, write to the Free Software Foundation, Inc.,
20  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22 #include "config.h"
23 #include "slrnfeat.h"
24 /*{{{ Include files */
25 
26 #include <stdio.h>
27 #include <signal.h>
28 #include <string.h>
29 
30 #ifdef HAVE_STDLIB_H
31 # include <stdlib.h>
32 #endif
33 
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37 
38 #ifdef HAVE_LOCALE_H
39 # include <locale.h>
40 #endif
41 
42 #ifdef VMS
43 # include "vms.h"
44 #else
45 # if !defined(sun) && !defined(IBMPC_SYSTEM)
46 #  include <sys/ioctl.h>
47 # endif
48 # ifdef HAVE_TERMIOS_H
49 #  include <termios.h>
50 # endif
51 # ifdef SYSV
52 #  include <sys/termio.h>
53 #  include <sys/stream.h>
54 #  include <sys/ptem.h>
55 #  include <sys/tty.h>
56 # endif
57 # ifndef __os2__
58 #  include <sys/types.h>
59 #  include <sys/stat.h>
60 # endif
61 # ifdef __MINGW32__
62 #  include <process.h>
63 # endif
64 #endif /* !VMS */
65 
66 #ifdef HAVE_SYS_WAIT_H
67 # include <sys/wait.h>
68 #endif
69 
70 #include <slang.h>
71 #include "jdmacros.h"
72 
73 #include <errno.h>
74 
75 #include "common.h"
76 #include "util.h"
77 #include "server.h"
78 #include "slrn.h"
79 #include "group.h"
80 #include "misc.h"
81 #include "startup.h"
82 #include "art.h"
83 #include "score.h"
84 #include "snprintf.h"
85 #include "charset.h"
86 #include "post.h"
87 #include "xover.h"
88 #include "mime.h"
89 #include "hooks.h"
90 #include "strutil.h"
91 
92 #if SLRN_USE_SLTCP
93 # include "sltcp.h"
94 #endif
95 
96 #if SLRN_HAS_GROUPLENS
97 # include "grplens.h"
98 #endif
99 #include "interp.h"
100 #ifdef __os2__
101 # define INCL_VIO
102 # include <os2.h>
103 #endif
104 
105 #if defined(__NT__) || defined(__WIN32__)
106 # include <windows.h>
107 #endif
108 
109 /*}}}*/
110 
111 /*{{{ Global Variables */
112 
113 #if SLANG_VERSION >= 20000
114 int Slrn_UTF8_Mode = 0;
115 #endif
116 
117 int Slrn_TT_Initialized = 0;
118 
119 /* If -1, force mouse.  If 1 the mouse will be used on in XTerm.  If 0,
120  * do not use it.
121  */
122 int Slrn_Use_Mouse;
123 
124 /* Find out whether there was a warning at startup. Used for option -w0 */
125 int Slrn_Saw_Warning = 0;
126 
127 /* We do not (yet) offer a batch mode; however, I consider it an interesting
128  * idea and might implement it after version 1.0.*/
129 int Slrn_Batch;
130 int Slrn_Suspension_Ok;
131 int Slrn_Simulate_Graphic_Chars = 0;
132 
133 char *Slrn_Newsrc_File = NULL;
134 Slrn_Mode_Type *Slrn_Current_Mode;
135 
136 int Slrn_Default_Server_Obj = SLRN_DEFAULT_SERVER_OBJ;
137 int Slrn_Default_Post_Obj = SLRN_DEFAULT_POST_OBJ;
138 
139 FILE *Slrn_Debug_Fp = NULL;
140 
141 /* You need to call slrn_init_graphic_chars before using these */
142 SLwchar_Type Graphic_LTee_Char;
143 SLwchar_Type Graphic_UTee_Char;
144 SLwchar_Type Graphic_LLCorn_Char;
145 SLwchar_Type Graphic_HLine_Char;
146 SLwchar_Type Graphic_VLine_Char;
147 SLwchar_Type Graphic_ULCorn_Char;
148 
149 int Graphic_Chars_Mode = ALT_CHAR_SET_MODE;
150 
151 /*}}}*/
152 /*{{{ Static Variables */
153 
154 static int Can_Suspend;
155 static volatile int Want_Suspension;
156 static volatile int Want_Window_Size_Change;
157 static void perform_suspend (int);
158 static int Current_Mouse_Mode;
159 static int Ran_Startup_Hook;
160 
161 /*}}}*/
162 /*{{{ Static Function Declarations */
163 
164 #if defined(SIGSTOP) && defined(REAL_UNIX_SYSTEM)
165 static int suspend_display_mode (int);
166 static int resume_display_mode (int, int, int);
167 #endif
168 static void init_suspend_signals (int);
169 static void slrn_hangup (int);
170 static void run_winch_functions (int, int);
171 /*}}}*/
172 
173 /*{{{ Newsrc Locking Routines */
174 
test_lock(char * file)175 static void test_lock( char *file ) /*{{{*/
176 {
177    int pid;
178    FILE *fp;
179 
180    if ((fp = fopen (file, "r")) != NULL)
181      {
182 	if (1 == fscanf (fp, "%d", &pid) )
183 	  {
184 	     if ((pid > 0)
185 #if !defined(IBMPC_SYSTEM)
186 		 && (0 == kill (pid, 0))
187 #endif
188 		 )
189 	       {
190 #if defined(IBMPC_SYSTEM)
191 		  slrn_exit_error (_("\
192 slrn: pid %d is locking the newsrc file. If you're not running another\n\
193       copy of slrn, delete the file %s"),
194 				   pid, file);
195 #else
196 		  slrn_exit_error (_("slrn: pid %d is locking the newsrc file."), pid);
197 #endif
198 	       }
199 	  }
200 	slrn_fclose (fp);
201      }
202 }
203 
204 /*}}}*/
205 
make_lock(char * file)206 static int make_lock( char *file ) /*{{{*/
207 {
208    int pid;
209    FILE *fp;
210 
211 #ifdef VMS
212    fp = fopen (file, "w", "fop=cif");
213 #else
214    fp = fopen (file, "w");
215 #endif
216 
217    if (fp == NULL) return -1;
218 
219    pid = getpid ();
220    if (EOF == fprintf (fp, "%d", pid))
221      {
222 	slrn_fclose (fp);
223 	return -1;
224      }
225    return slrn_fclose (fp);
226 }
227 
228 /*}}}*/
229 
lock_file(int how)230 static void lock_file (int how) /*{{{*/
231 {
232    char name[SLRN_MAX_PATH_LEN];
233    static int not_ok_to_unlock;
234 #if SLRN_HAS_RNLOCK
235    int rnlock = 0;
236    char file_rn[SLRN_MAX_PATH_LEN];
237 #endif
238 
239    if (Slrn_Newsrc_File == NULL) return;
240    if (not_ok_to_unlock) return;
241 
242    not_ok_to_unlock = 1;
243 
244 #ifdef SLRN_USE_OS2_FAT
245    slrn_os2_make_fat (name, sizeof (name), Slrn_Newsrc_File, ".lck");
246 #else
247    slrn_snprintf (name, sizeof (name), "%s-lock", Slrn_Newsrc_File);
248 #endif
249 
250 #if SLRN_HAS_RNLOCK
251    slrn_make_home_filename (".newsrc", file_rn, sizeof (file_rn));
252    if (0 == strcmp (file_rn, Slrn_Newsrc_File))
253      {
254 	rnlock = 1;
255 	slrn_make_home_filename (".rnlock", file_rn, sizeof (file_rn));
256      }
257 #endif
258 
259    if (how == 1)
260      {
261 	test_lock (name);
262 #if SLRN_HAS_RNLOCK
263 	if (rnlock) test_lock (file_rn);
264 #endif
265 	if (-1 == make_lock (name))
266 	  {
267 	     slrn_exit_error (_("Unable to create lock file %s."), name);
268 	  }
269 
270 #if SLRN_HAS_RNLOCK
271 	if (rnlock && (-1 == make_lock (file_rn)))
272 	  {
273 	     slrn_delete_file (name); /* delete the "normal" lock file */
274 	     slrn_exit_error (_("Unable to create lock file %s."), file_rn);
275 	  }
276 #endif
277      }
278    else
279      {
280 	if (-1 == slrn_delete_file (name))
281 	  {
282 	     /* slrn_exit_error ("Unable to remove lockfile %s.", file); */
283 	  }
284 #if SLRN_HAS_RNLOCK
285         if (rnlock && -1 == slrn_delete_file (file_rn))
286 	  {
287 	     /* slrn_exit_error ("Unable to remove lockfile %s.", file_rn); */
288 	  }
289 #endif
290      }
291    not_ok_to_unlock = 0;
292 }
293 
294 /*}}}*/
295 
296 /*}}}*/
297 
298 /*{{{ Signal Related Functions */
299 
300 /*{{{ Low-Level signal-related utility functions */
301 
init_like_signals(int argc,int * argv,void (* f0)(int),void (* f1)(int),int state)302 static void init_like_signals (int argc, int *argv, /*{{{*/
303 			       void (*f0)(int),
304 			       void (*f1)(int),
305 			       int state)
306 {
307 #ifdef HAVE_SIGACTION
308    struct sigaction sa;
309 #endif
310    int i;
311 
312    if (state == 0)
313      {
314 	for (i = 0; i < argc; i++)
315 	  SLsignal_intr (argv[i], f0);
316 	return;
317      }
318 
319    for (i = 0; i < argc; i++)
320      {
321 	int sig = argv[i];
322 	SLsignal_intr (sig, f1);
323 
324 #if defined(SLRN_POSIX_SIGNALS)
325 	if (-1 != sigaction (sig, NULL, &sa))
326 	  {
327 	     int j;
328 	     for (j = 0; j < argc; j++)
329 	       {
330 		  if (j != i) sigaddset (&sa.sa_mask, argv[j]);
331 	       }
332 
333 	     (void) sigaction (sig, &sa, NULL);
334 	  }
335 #endif
336      }
337 }
338 
339 /*}}}*/
340 
341 /*}}}*/
342 
343 /*{{{ Suspension signals */
344 
345 #ifdef REAL_UNIX_SYSTEM
346 #define SUSPEND_STACK_SIZE 512
347 static char Suspend_Stack [SUSPEND_STACK_SIZE];
348 static unsigned int Suspension_Stack_Depth = 0;
349 static int Ok_To_Suspend = 0;
350 #endif
351 
352 static int Suspend_Sigtstp_Suspension = 0;
353 
slrn_push_suspension(int ok)354 void slrn_push_suspension (int ok) /*{{{*/
355 {
356 #ifdef REAL_UNIX_SYSTEM
357    if (Suspension_Stack_Depth < SUSPEND_STACK_SIZE)
358      {
359 	Suspend_Stack [Suspension_Stack_Depth] = Ok_To_Suspend;
360      }
361    else ok = 0;
362 
363    Suspension_Stack_Depth++;
364 
365    (void) slrn_handle_interrupts ();
366 
367    Ok_To_Suspend = ok;
368 #else
369    (void) ok;
370 #endif
371 }
372 /*}}}*/
slrn_pop_suspension(void)373 void slrn_pop_suspension (void) /*{{{*/
374 {
375 #ifdef REAL_UNIX_SYSTEM
376 
377    if (Suspension_Stack_Depth == 0)
378      {
379 	slrn_error (_("pop_suspension: underflow!"));
380 	return;
381      }
382 
383    Suspension_Stack_Depth--;
384 
385    if (Suspension_Stack_Depth < SUSPEND_STACK_SIZE)
386      {
387 	Ok_To_Suspend = Suspend_Stack [Suspension_Stack_Depth];
388      }
389    else Ok_To_Suspend = 0;
390 
391    (void) slrn_handle_interrupts ();
392 #endif
393 }
394 /*}}}*/
395 
396 /* This function is called by the SIGTSTP handler.  Since it operates
397  * in an asynchronous fashion, care must be exercised to control when that
398  * can happen.  This is accomplished via the push/pop_suspension functions.
399  */
sig_suspend(int sig)400 static void sig_suspend (int sig)
401 {
402 #ifdef REAL_UNIX_SYSTEM
403    sig = errno;
404 
405    if (Ok_To_Suspend
406        && (0 == Suspend_Sigtstp_Suspension))
407      {
408 	perform_suspend (1);
409      }
410    else Want_Suspension = 1;
411 
412    init_suspend_signals (1);
413    errno = sig;
414 #else
415    (void) sig;
416 #endif
417 }
418 
init_suspend_signals(int state)419 static void init_suspend_signals (int state) /*{{{*/
420 {
421    int argv[2];
422    int argc = 0;
423 
424    if (Can_Suspend == 0)
425      return;
426 
427 #ifdef SIGTSTP
428    argv[argc++] = SIGTSTP;
429 #endif
430 
431 #ifdef SIGTTIN
432    argv[argc++] = SIGTTIN;
433 #endif
434 
435    init_like_signals (argc, argv, SIG_DFL, sig_suspend, state);
436 }
437 
438 /*}}}*/
439 
perform_suspend(int smg_suspend_flag)440 static void perform_suspend (int smg_suspend_flag) /*{{{*/
441 {
442 #if !defined(SIGSTOP) || !defined(REAL_UNIX_SYSTEM)
443    (void) smg_suspend_flag;
444    slrn_error (_("Not implemented."));
445    Want_Suspension = 0;
446 #else
447 
448    int init;
449    int mouse_mode = Current_Mouse_Mode;
450 
451 # ifdef SLRN_POSIX_SIGNALS
452    sigset_t mask;
453 
454    Want_Suspension = 0;
455    if (Can_Suspend == 0)
456      {
457 	slrn_error (_("Suspension not allowed by shell."));
458 	return;
459      }
460 
461    sigemptyset (&mask);
462    sigaddset (&mask, SIGTSTP);
463 
464    /* This function resets SIGTSTP to default */
465    init = suspend_display_mode (smg_suspend_flag);
466 
467    kill (getpid (), SIGTSTP);
468 
469    /* If SIGTSTP is pending, it will be delivered now.  That's ok. */
470    sigprocmask (SIG_UNBLOCK, &mask, NULL);
471 # else
472 
473    Want_Suspension = 0;
474    if (Can_Suspend == 0)
475      {
476 	slrn_error (_("Suspension not allowed by shell."));
477 	return;
478      }
479 
480    init = suspend_display_mode (smg_suspend_flag);
481    kill(getpid(),SIGSTOP);
482 # endif
483 
484    resume_display_mode (smg_suspend_flag, init, mouse_mode);
485 #endif				       /* !defined(SIGSTOP) || !defined(REAL_UNIX_SYSTEM) */
486 }
487 
488 /*}}}*/
489 
slrn_suspend_cmd(void)490 void slrn_suspend_cmd (void)
491 {
492    perform_suspend (0);
493 }
494 
check_for_suspension(void)495 static void check_for_suspension (void)
496 {
497 #ifdef SIGTSTP
498    void (*f)(int);
499 
500    f = SLsignal (SIGTSTP, SIG_DFL);
501    (void) SLsignal (SIGTSTP, f);
502 
503 #if 0
504    Can_Suspend = (f == SIG_DFL);
505 #else
506    Can_Suspend = (f != SIG_IGN);
507 #endif
508 
509 #else
510    Can_Suspend = 0;
511 #endif
512 }
513 
514 /*}}}*/
515 
516 /*{{{ Hangup Signals */
517 
slrn_init_hangup_signals(int state)518 void slrn_init_hangup_signals (int state) /*{{{*/
519 {
520    int argv[3];
521    int argc = 0;
522 
523 #ifdef SIGHUP
524    argv[argc++] = SIGHUP;
525 #endif
526 #ifdef SIGTERM
527    argv[argc++] = SIGTERM;
528 #endif
529 #ifdef SIGQUIT
530    argv[argc++] = SIGQUIT;
531 #endif
532 
533    init_like_signals (argc, argv, SIG_IGN, slrn_hangup, state);
534 }
535 
536 /*}}}*/
537 
538 /*}}}*/
539 
540 #ifdef SIGWINCH
sig_winch_handler(int sig)541 static void sig_winch_handler (int sig)
542 {
543    if (Slrn_Batch) return;
544    sig = errno;
545    Want_Window_Size_Change = 1;
546    SLsignal_intr (SIGWINCH, sig_winch_handler);
547    errno = sig;
548 }
549 #endif
550 
slrn_set_screen_size(int sig)551 static void slrn_set_screen_size (int sig) /*{{{*/
552 {
553    int old_r, old_c;
554 
555    old_r = SLtt_Screen_Rows;
556    old_c = SLtt_Screen_Cols;
557 
558    SLtt_get_screen_size ();
559 
560    Slrn_Full_Screen_Update = 1;
561    Want_Window_Size_Change = 0;
562 
563    run_winch_functions (old_r, old_c);
564 
565    if (sig)
566      {
567 #if SLANG_VERSION < 10306
568 	SLsmg_reset_smg ();
569 	SLsmg_init_smg ();
570 #else
571 	SLsmg_reinit_smg ();
572 #endif
573 	slrn_redraw ();
574      }
575 }
576 
577 /*}}}*/
578 
init_display_signals(int mode)579 static void init_display_signals (int mode) /*{{{*/
580 {
581    init_suspend_signals (mode);
582 
583    if (mode)
584      {
585 	SLang_set_abort_signal (NULL);
586 #ifdef SIGPIPE
587 	SLsignal (SIGPIPE, SIG_IGN);
588 #endif
589 #ifdef SIGTTOU
590 	/* Allow background writes */
591 	SLsignal (SIGTTOU, SIG_IGN);
592 #endif
593 #ifdef SIGWINCH
594 	SLsignal_intr (SIGWINCH, sig_winch_handler);
595 #endif
596      }
597    else
598      {
599 #ifdef SIGWINCH
600 	/* SLsignal_intr (SIGWINCH, SIG_DFL); */
601 #endif
602      }
603 }
604 
605 /*}}}*/
606 
slrn_handle_interrupts(void)607 int slrn_handle_interrupts (void)
608 {
609    if (Want_Suspension)
610      {
611 	slrn_suspend_cmd ();
612      }
613 
614    if (Want_Window_Size_Change)
615      {
616 	slrn_set_screen_size (1);
617      }
618 
619    return 0;
620 }
621 
622 /*}}}*/
623 
624 /*{{{ Screen Management and Terminal Init/Reset Functions */
625 
626 int Slrn_Use_Flow_Control = 0;
init_tty(void)627 static int init_tty (void) /*{{{*/
628 {
629    if (Slrn_TT_Initialized & SLRN_TTY_INIT)
630      {
631 	return 0;
632      }
633 
634    if (Slrn_TT_Initialized == 0)
635      init_display_signals (1);
636 
637    if (-1 == SLang_init_tty (7, !Slrn_Use_Flow_Control, 0))
638      slrn_exit_error (_("Unable to initialize the tty"));
639 
640 #ifdef REAL_UNIX_SYSTEM
641    SLang_getkey_intr_hook = slrn_handle_interrupts;
642 #endif
643 
644 #ifdef REAL_UNIX_SYSTEM
645    if (Can_Suspend)
646      SLtty_set_suspend_state (1);
647 #endif
648 
649    Slrn_TT_Initialized |= SLRN_TTY_INIT;
650 
651    return 0;
652 }
653 
654 /*}}}*/
655 
reset_tty(void)656 static int reset_tty (void) /*{{{*/
657 {
658    if (0 == (Slrn_TT_Initialized & SLRN_TTY_INIT))
659      {
660 	return 0;
661      }
662 
663    SLang_reset_tty ();
664    Slrn_TT_Initialized &= ~SLRN_TTY_INIT;
665 
666    if (Slrn_TT_Initialized == 0)
667      init_display_signals (0);
668 
669    return 0;
670 }
671 
672 /*}}}*/
673 
init_smg(int use_resume,int mouse_mode)674 static int init_smg (int use_resume, int mouse_mode) /*{{{*/
675 {
676    if (Slrn_TT_Initialized & SLRN_SMG_INIT)
677      return 0;
678 
679    slrn_enable_mouse (mouse_mode);
680 
681    if (Slrn_TT_Initialized == 0)
682      init_display_signals (1);
683 
684    if (use_resume)
685      {
686 	SLsmg_resume_smg ();
687 	Slrn_TT_Initialized |= SLRN_SMG_INIT;
688      }
689    else
690      {
691 	slrn_set_screen_size (0);
692 	SLsmg_init_smg ();
693 	Slrn_TT_Initialized |= SLRN_SMG_INIT;
694 
695 	/* We do not want the -> overlay cursor to affect the scroll. */
696 #if !defined(IBMPC_SYSTEM)
697 	SLsmg_Scroll_Hash_Border = 5;
698 #endif
699 	slrn_redraw ();
700      }
701 
702    return 0;
703 }
704 
705 /*}}}*/
706 
reset_smg(int smg_suspend_flag)707 static int reset_smg (int smg_suspend_flag) /*{{{*/
708 {
709    if (0 == (Slrn_TT_Initialized & SLRN_SMG_INIT))
710      return 0;
711 
712    slrn_enable_mouse (0);
713 
714    if (smg_suspend_flag)
715      SLsmg_suspend_smg ();
716    else
717      {
718 	SLsmg_gotorc (SLtt_Screen_Rows - 1, 0);
719 	slrn_smg_refresh ();
720 	SLsmg_reset_smg ();
721      }
722 
723    /* SLsignal_intr (SIGWINCH, SIG_DFL); */
724 
725    Slrn_TT_Initialized &= ~SLRN_SMG_INIT;
726 
727    if (Slrn_TT_Initialized == 0)
728      init_display_signals (0);
729 
730    return 0;
731 }
732 
733 /*}}}*/
734 
735 #if defined(SIGSTOP) && defined(REAL_UNIX_SYSTEM)
suspend_display_mode(int smg_suspend_flag)736 static int suspend_display_mode (int smg_suspend_flag) /*{{{*/
737 {
738    int mode = Slrn_TT_Initialized;
739 
740    SLsig_block_signals ();
741 
742    reset_smg (smg_suspend_flag);
743    reset_tty ();
744 
745    SLsig_unblock_signals ();
746 
747    return mode;
748 }
749 
750 /*}}}*/
751 
resume_display_mode(int smg_suspend_flag,int mode,int mouse_mode)752 static int resume_display_mode (int smg_suspend_flag, int mode, int mouse_mode) /*{{{*/
753 {
754    SLsig_block_signals ();
755 
756    if (mode & SLRN_TTY_INIT)
757      init_tty ();
758 
759    if (mode & SLRN_SMG_INIT)
760      init_smg (smg_suspend_flag, mouse_mode);
761 
762    SLsig_unblock_signals ();
763    return 0;
764 }
765 
766 /*}}}*/
767 #endif /* SIGSTOP && REAL_UNIX_SYSTEM*/
768 
slrn_set_display_state(int state)769 void slrn_set_display_state (int state) /*{{{*/
770 {
771    if (Slrn_Batch) return;
772 
773    SLsig_block_signals ();
774 
775    if (state & SLRN_TTY_INIT)
776      init_tty ();
777    else
778      reset_tty ();
779 
780    if (state & SLRN_SMG_INIT)
781      init_smg (0, 1);
782    else
783      reset_smg (0);
784 
785    SLsig_unblock_signals ();
786 }
787 
788 /*}}}*/
789 
slrn_enable_mouse(int mode)790 void slrn_enable_mouse (int mode) /*{{{*/
791 {
792    if (Current_Mouse_Mode == mode)
793      return;
794 
795    if (Slrn_Use_Mouse)
796      {
797 	if (-1 == SLtt_set_mouse_mode (mode, (Slrn_Use_Mouse < 0)))
798 	  Slrn_Use_Mouse = 0;
799      }
800    Current_Mouse_Mode = mode;
801 }
802 
803 /*}}}*/
804 
slrn_init_graphic_chars(void)805 void slrn_init_graphic_chars (void) /*{{{*/
806 {
807 #ifndef IBMPC_SYSTEM
808    if (SLtt_Has_Alt_Charset == 0)
809      Slrn_Simulate_Graphic_Chars = 1;
810 #endif
811 
812    if (Slrn_Simulate_Graphic_Chars)
813      {
814 	Graphic_LTee_Char = '+';
815 	Graphic_UTee_Char = '+';
816 	Graphic_LLCorn_Char = '`';
817 	Graphic_HLine_Char = '-';
818 	Graphic_VLine_Char = '|';
819 	Graphic_ULCorn_Char = '/';
820 
821 	Graphic_Chars_Mode = SIMULATED_CHAR_SET_MODE;
822      }
823    else
824      {
825 	Graphic_Chars_Mode = ALT_CHAR_SET_MODE;
826 	Graphic_LTee_Char = SLSMG_LTEE_CHAR;
827 	Graphic_UTee_Char = SLSMG_UTEE_CHAR;
828 	Graphic_LLCorn_Char = SLSMG_LLCORN_CHAR;
829 	Graphic_HLine_Char = SLSMG_HLINE_CHAR;
830 	Graphic_VLine_Char = SLSMG_VLINE_CHAR;
831 	Graphic_ULCorn_Char = SLSMG_ULCORN_CHAR;
832 
833 	Graphic_Chars_Mode = ALT_CHAR_SET_MODE;
834      }
835 }
836 /*}}}*/
837 /*}}}*/
838 
perform_cleanup(void)839 static void perform_cleanup (void)
840 {
841    slrn_set_display_state (0);
842 
843 #if SLRN_HAS_GROUPLENS
844    slrn_close_grouplens ();
845 #endif
846 
847    if (Slrn_Server_Obj != NULL)
848      Slrn_Server_Obj->sv_close ();
849 
850    if (Slrn_Debug_Fp != NULL)
851      {
852 	(void) fclose (Slrn_Debug_Fp);
853 	Slrn_Debug_Fp = NULL;
854      }
855 
856 #if SLRN_HAS_GROUPLENS
857    slrn_close_grouplens ();
858 #endif
859 
860    if (Ran_Startup_Hook)
861      (void) slrn_run_hooks (HOOK_QUIT, 0);
862    (void) slrn_reset_slang ();
863 
864 #if SLRN_USE_SLTCP && SLRN_HAS_NNTP_SUPPORT
865    (void) sltcp_close_sltcp ();
866 #endif
867    lock_file (0);
868 }
869 
slrn_quit(int retcode)870 void slrn_quit (int retcode) /*{{{*/
871 {
872    perform_cleanup ();
873    if (retcode) fprintf (stderr, _("slrn: quiting on signal %d.\n"), retcode);
874    exit (retcode);
875 }
876 
877 /*}}}*/
878 
slrn_va_exit_error(char * fmt,va_list ap)879 void slrn_va_exit_error (char *fmt, va_list ap)
880 {
881    static int trying_to_exit;
882 
883    if (trying_to_exit == 0)
884      {
885 	trying_to_exit = 1;
886 	slrn_set_display_state (0);
887 
888 	if (fmt != NULL)
889 	  {
890 	     fputs (_("slrn fatal error:"), stderr);
891 #ifdef IBMPC_SYSTEM
892 	     fputc ('\r', stderr);
893 #endif
894 	     fputc ('\n', stderr);
895 	     vfprintf (stderr, fmt, ap);
896 	  }
897 	if (Slrn_Groups_Dirty) slrn_write_newsrc (0);
898 	perform_cleanup ();
899      }
900 
901    putc ('\n', stderr);
902    exit (1);
903 }
904 
slrn_exit_error(char * fmt,...)905 void slrn_exit_error (char *fmt, ...) /*{{{*/
906 {
907    va_list ap;
908    va_start (ap, fmt);
909    slrn_va_exit_error (fmt, ap);
910    va_end(ap);
911 }
912 
913 /*}}}*/
914 
slrn_error(char * fmt,...)915 void slrn_error (char *fmt, ...) /*{{{*/
916 {
917    va_list ap;
918 
919    va_start(ap, fmt);
920    slrn_verror (fmt, ap);
921    va_end (ap);
922 }
923 
924 /*}}}*/
925 
usage(char * extra,int exit_status)926 static void usage (char *extra, int exit_status) /*{{{*/
927 {
928    printf (_("\
929 Usage: slrn [--inews] [--nntp ...] [--spool] OPTIONS\n\
930 -a              Use active file for getting new news.\n\
931 -f newsrc-file  Name of the newsrc file to use.\n\
932 -C[-]           [Do not] use colors.\n\
933 -Dname          Add 'name' to list of predefined preprocessing tokens.\n\
934 -d              Get new text descriptions of each group from server.\n\
935                  Note: This may take a LONG time to retrieve this information.\n\
936                  The resulting file can be several hundred Kilobytes!\n\
937 -i init-file    Name of initialization file to use (default: %s)\n\
938 -k              Do not process score file.\n\
939 -k0             Process score file but inhibit expensive scores.\n\
940 -m              Force XTerm mouse reporting\n\
941 -n              Do not check for new groups.  This usually results in\n\
942                  a faster startup.\n\
943 -w              Wait for key before switching to full screen mode\n\
944 -w0             Wait for key (only when warnings / errors occur)\n\
945 --create        Create a newsrc file by getting list of groups from server.\n\
946 --debug FILE    Write debugging information to FILE\n\
947 --help          Print this usage.\n\
948 --kill-log FILE Keep a log of all killed articles in FILE\n\
949 --show-config   Print configuration\n\
950 --version       Show version and supported features\n\
951 \n\
952 NNTP mode has additional options; use \"slrn --nntp --help\" to display them.\n\
953 "), SLRN_USER_SLRNRC_FILENAME);
954 
955    if (extra != NULL)
956      {
957 	printf ("\n%s\n", extra);
958      }
959    exit (exit_status);
960 }
961 
962 /*}}}*/
963 
parse_object_args(char * obj,char ** argv,int argc)964 static int parse_object_args (char *obj, char **argv, int argc) /*{{{*/
965 {
966    int num_parsed;
967    int zero_ok = 1;
968 
969    if (obj == NULL)
970      {
971 	zero_ok = 0;
972 	obj = slrn_map_object_id_to_name (0, SLRN_DEFAULT_SERVER_OBJ);
973      }
974 
975    num_parsed = slrn_parse_object_args (obj, argv, argc);
976    if (num_parsed < 0)
977      {
978 	if (num_parsed == -1)
979 	  slrn_exit_error (_("%s is not a supported option."), *argv);
980 	else
981 	  slrn_exit_error (_("%s is not supported."), obj);
982      }
983    if ((num_parsed == 0) && (zero_ok == 0))
984      usage (NULL, 1);
985 
986    return num_parsed;
987 }
988 
989 /*}}}*/
990 
read_score_file(void)991 static void read_score_file (void)
992 {
993    char file[SLRN_MAX_PATH_LEN];
994 
995    if (Slrn_Score_File == NULL)
996      return;
997 
998    slrn_make_home_filename (Slrn_Score_File, file, sizeof (file));
999 
1000    if (1 != slrn_file_exists (file))
1001      {
1002 	slrn_message_now (_("*** Warning: Score file %s does not exist"), file);
1003 	Slrn_Saw_Warning = 1;
1004 	return;
1005      }
1006 
1007    if (-1 == slrn_read_score_file (file))
1008      {
1009 	slrn_exit_error (_("Error processing score file %s."), file);
1010      }
1011 }
1012 
main_init_and_parse_args(int argc,char ** argv)1013 static int main_init_and_parse_args (int argc, char **argv) /*{{{*/
1014 {
1015    char *hlp_file;
1016    unsigned int i;
1017    int create_flag = 0;
1018    int no_new_groups = 0;
1019    int no_score_file = 0;
1020    int use_color = 0;
1021    int use_mouse = 0;
1022    int dsc_flag = 0;
1023    int use_active = 0;
1024    int wait_for_key = 0;
1025    FILE *fp;
1026    char file [SLRN_MAX_PATH_LEN];
1027    char *init_file = NULL;
1028    char *kill_logfile = NULL;
1029    int print_config = 0;
1030 
1031 #if defined(HAVE_SETLOCALE) && defined(LC_ALL)
1032    (void) setlocale(LC_ALL, "");
1033 #endif
1034 #ifdef ENABLE_NLS
1035    bindtextdomain(NLS_PACKAGE_NAME, NLS_LOCALEDIR);
1036    textdomain (NLS_PACKAGE_NAME);
1037 #endif
1038 
1039    check_for_suspension ();
1040 #ifdef __unix__
1041    (void) umask (077);
1042 #endif
1043 
1044 #if 0
1045    if (NULL != getenv ("AUTOSUBSCRIBE"))
1046      Slrn_Unsubscribe_New_Groups = 0;
1047    if (NULL != getenv ("AUTOUNSUBSCRIBE"))
1048      Slrn_Unsubscribe_New_Groups = 1;
1049 #endif
1050 
1051    for (i = 1; i < (unsigned int) argc; i++)
1052      {
1053 	char *argv_i = argv[i];
1054 
1055 	if ((argv_i[0] == '-') && (argv_i[1] == '-'))
1056 	  {
1057 	     int status;
1058 
1059 	     argv_i += 2;
1060 
1061 	     status = slrn_parse_object_args (argv_i, NULL, 0);
1062 	     if (status != -1)
1063 	       i += parse_object_args (argv_i, argv + (i + 1), argc - (i + 1));
1064 #if 0
1065 	     else if (!strcmp ("batch", argv_i)) Slrn_Batch = 1;
1066 #endif
1067 	     else if (!strcmp ("create", argv_i)) create_flag = 1;
1068 	     else if (!strcmp ("version", argv_i))
1069 	       {
1070 		  slrn_show_version (stdout);
1071 		  exit (0);
1072 	       }
1073 	     else if (!strcmp ("show-config", argv_i))
1074 	       print_config = 1;
1075 	     else if ((!strcmp ("kill-log", argv_i)) &&
1076 		      (i + 1 < (unsigned int) argc))
1077 	       kill_logfile = argv[++i];
1078              else if ((!strcmp ("debug", argv_i)) &&
1079                       (i + 1 < (unsigned int) argc))
1080                {
1081 		  if (Slrn_Debug_Fp != NULL)
1082 		    fclose (Slrn_Debug_Fp);
1083                   Slrn_Debug_Fp = fopen (argv[++i], "w");
1084                   if (Slrn_Debug_Fp == NULL)
1085                     slrn_exit_error (_("Unable to open %s for debugging."), argv[i]);
1086                   setbuf (Slrn_Debug_Fp, (char *) NULL);
1087                }
1088 	     else if (!strcmp ("help", argv_i))
1089 	       usage (NULL, 0);
1090 	     else usage (NULL, 1);
1091 	  }
1092 	else if (!strcmp ("-create", argv_i)) create_flag = 1;
1093 	else if (!strcmp ("-C", argv_i)) use_color = 1;
1094 	else if (!strcmp ("-C-", argv_i)) use_color = -1;
1095 	else if (!strcmp ("-a", argv_i)) use_active = 1;
1096 	else if (!strcmp ("-n", argv_i)) no_new_groups = 1;
1097 	else if (!strcmp ("-d", argv_i)) dsc_flag = 1;
1098 	else if (!strcmp ("-m", argv_i)) use_mouse = 1;
1099 	else if (!strcmp ("-k", argv_i)) no_score_file = 1;
1100 	else if (!strcmp ("-k0", argv_i))
1101 	  Slrn_Perform_Scoring &= ~SLRN_EXPENSIVE_SCORING;
1102 	else if (!strcmp ("-w", argv_i))
1103 	  wait_for_key = 1;
1104 	else if (!strcmp ("-w0", argv_i))
1105 	  wait_for_key = 2;
1106 	else if (!strncmp ("-D", argv_i, 2) && (argv_i[2] != 0))
1107 	  {
1108 	     if (-1 == SLdefine_for_ifdef (argv_i + 2))
1109 	       {
1110 		  slrn_exit_error (_("Unable to add preprocessor name %s."),
1111 				   argv_i + 2);
1112 	       }
1113 	  }
1114 	else if (i + 1 < (unsigned int) argc)
1115 	  {
1116 	     if (!strcmp ("-f", argv_i)) Slrn_Newsrc_File = argv[++i];
1117 	     else if (!strcmp ("-i", argv_i)) init_file = argv[++i];
1118 	     else
1119 	       {
1120 		  i += parse_object_args (NULL, argv + i, argc - i);
1121 		  i -= 1;
1122 	       }
1123 	  }
1124 	else
1125 	  {
1126 	     i += parse_object_args (NULL, argv + i, argc - i);
1127 	     i -= 1;
1128 	  }
1129      }
1130 
1131    fprintf (stdout, "slrn %s\n", Slrn_Version_String);
1132 
1133    if (dsc_flag && create_flag)
1134      {
1135 	usage (_("The -d and --create flags must not be specified together."), 1);
1136      }
1137 
1138    if (Slrn_Batch == 0)
1139      {
1140 	Slrn_UTF8_Mode = SLutf8_enable (-1);
1141 	SLtt_get_terminfo ();
1142 	if (use_color == 1) SLtt_Use_Ansi_Colors = 1;
1143 	else if (use_color == -1) SLtt_Use_Ansi_Colors = 0;
1144      }
1145 
1146 #if SLRN_USE_SLTCP && SLRN_HAS_NNTP_SUPPORT
1147    SLtcp_Verror_Hook = slrn_verror;
1148    /* This is necessary to ensure that gethostname works on Win32 */
1149    if (-1 == sltcp_open_sltcp ())
1150      slrn_exit_error (_("Error initializing SLtcp interface"));
1151 #endif
1152 
1153    if (-1 == slrn_init_slang ())
1154      slrn_exit_error (_("Error initializing S-Lang interpreter.\n"));
1155 
1156    slrn_startup_initialize ();
1157 
1158    /* We need to get user info first, because the request file in true offline
1159     * reading mode is chosen based on login name */
1160    slrn_get_user_info ();
1161 
1162    /* The next function call will also define slang preprocessing tokens
1163     * for the appropriate objects.  For that reason, it is called after
1164     * startup initialize.
1165     */
1166    if (-1 == slrn_init_objects ())
1167      {
1168 	slrn_exit_error (_("Error configuring server objects."));
1169      }
1170 
1171    slrn_init_hangup_signals (1);
1172 
1173 #ifdef VMS
1174    slrn_snprintf (file, sizeof (file), "%s%s", SLRN_CONF_DIR, "slrn.rc");
1175 #else
1176    slrn_snprintf (file, sizeof (file), "%s/%s", SLRN_CONF_DIR, "slrn.rc");
1177 #endif
1178 
1179    /* Make sure terminal is initialized before setting colors.  The
1180     * SLtt_get_terminfo call above fixed that.
1181     */
1182    slrn_read_startup_file (file);      /* global file for all users */
1183 
1184    if (init_file == NULL)
1185      {
1186 	slrn_make_home_filename (SLRN_USER_SLRNRC_FILENAME, file, sizeof (file));
1187 	init_file = file;
1188      }
1189 
1190    if ((-1 == slrn_read_startup_file (init_file)) && (init_file != file))
1191      {
1192 	slrn_exit_error (_("\
1193 Could not read specified config file %s\n"), init_file);
1194      }
1195 
1196    if (Slrn_Saw_Obsolete)
1197      {
1198 	slrn_message (_("\n! Your configuration file contains obsolete commands or function names that\n"
1199 		      "! will not be supported by future versions of this program.\n"
1200 		      "! If you have Perl installed, you can use the script slrnrc-conv to change\n"
1201 		      "! your configuration accordingly.  It can be found in the source distribution\n"
1202 		      "! or retrieved from <URL:http://slrn.sourceforge.net/data/>.\n"));
1203 	Slrn_Saw_Warning = 1;
1204      }
1205 
1206    if (Slrn_Server_Id == 0) Slrn_Server_Id = Slrn_Default_Server_Obj;
1207    if (Slrn_Post_Id == 0) Slrn_Post_Id = Slrn_Default_Post_Obj;
1208    if (no_new_groups) Slrn_Check_New_Groups = 0;
1209 
1210    slrn_prepare_charset();
1211 
1212    if (print_config)
1213      {
1214 	(void) putc ('\n', stdout);
1215 	slrn_show_version (stdout);
1216 	slrn_print_config (stdout);
1217 	slrn_quit (0);
1218      }
1219 
1220 #ifdef SIGINT
1221    if (Slrn_TT_Initialized == 0)
1222      SLsignal_intr (SIGINT, SIG_DFL);
1223 #endif
1224 
1225    if ((-1 == slrn_select_server_object (Slrn_Server_Id))
1226        || (-1 == slrn_select_post_object (Slrn_Post_Id)))
1227      {
1228 	slrn_exit_error (_("Unable to select server/post object."));
1229      }
1230 
1231 #if !defined(IBMPC_SYSTEM)
1232    /* Allow blink characters if in mono */
1233    if (SLtt_Use_Ansi_Colors == 0)
1234      SLtt_Blink_Mode = 1;
1235 #endif
1236 
1237    /* Now that we have read in the startup file, check to see if the user
1238     * has a username and a usable hostname.  Without those, we are not
1239     * starting up.
1240     */
1241    if ((NULL == Slrn_User_Info.hostname)
1242        || (0 == slrn_is_fqdn (Slrn_User_Info.hostname)))
1243      {
1244 	slrn_exit_error (_("\
1245 Unable to find a valid hostname for constructing your e-mail address.\n\
1246 You probably want to specify a hostname in your %s file.\n\
1247 Please see the \"slrn reference manual\" for full details.\n"),
1248 			 SLRN_USER_SLRNRC_FILENAME);
1249      }
1250    if ((NULL == Slrn_User_Info.username)
1251        || (0 == *Slrn_User_Info.username)
1252        || (NULL != slrn_strbyte (Slrn_User_Info.username, '@')))
1253      {
1254 	slrn_exit_error (_("\
1255 Unable to find your user name.  This means that a valid 'From' header line\n\
1256 cannot be constructed.  Try setting the USER environment variable.\n"));
1257      }
1258 
1259    if ((NULL == Slrn_User_Info.posting_host)
1260        && Slrn_Generate_Message_Id)
1261      {
1262 	slrn_stderr_strcat (_("\
1263 ***Warning: Unable to find a unique fully-qualified host name."), "\n", _("\
1264             slrn will not generate any Message-IDs."), "\n", _("\
1265             Please note that the \"hostname\" setting does not affect this;"), "\n", _("\
1266             see the \"slrn reference manual\" for details."), "\n", NULL);
1267 	Slrn_Saw_Warning = 1;
1268      }
1269 
1270    if (no_score_file == 0) read_score_file ();
1271 
1272    if (NULL == (hlp_file = getenv ("SLRNHELP")))
1273      {
1274 	hlp_file = file;
1275 #ifdef VMS
1276 	slrn_snprintf (file, sizeof (file), "%s%s", SLRN_CONF_DIR, "help.txt");
1277 #else
1278 	slrn_snprintf (file, sizeof (file), "%s/%s", SLRN_CONF_DIR, "help.txt");
1279 #endif
1280      }
1281 
1282    slrn_parse_helpfile (hlp_file);
1283 
1284    if ((Slrn_Newsrc_File == NULL)
1285        && ((Slrn_Newsrc_File = slrn_map_file_to_host (Slrn_Server_Obj->sv_name)) == NULL))
1286      {
1287 #if defined(VMS) || defined(IBMPC_SYSTEM)
1288 	Slrn_Newsrc_File = "jnews.rc";
1289 #else
1290 	Slrn_Newsrc_File = ".jnewsrc";
1291 #endif
1292 	slrn_make_home_filename (Slrn_Newsrc_File, file, sizeof (file));
1293 	Slrn_Newsrc_File = slrn_safe_strmalloc (file);
1294      }
1295 
1296    slrn_message_now (_("Using newsrc file %s for server %s."),
1297 		     Slrn_Newsrc_File, Slrn_Server_Obj->sv_name);
1298 
1299    if (use_active) Slrn_List_Active_File = 1;
1300    if (use_mouse) Slrn_Use_Mouse = -1; /* -1 forces it. */
1301    if (use_color == 1) SLtt_Use_Ansi_Colors = 1;
1302    else if (use_color == -1) SLtt_Use_Ansi_Colors = 0;
1303 
1304    if (dsc_flag)
1305      {
1306 	if (Slrn_Server_Obj->sv_initialize () != 0)
1307 	  {
1308 	     slrn_exit_error (_("Unable to initialize server."));
1309 	  }
1310 	slrn_get_group_descriptions ();
1311 	Slrn_Server_Obj->sv_close ();
1312 	exit (0);
1313      }
1314 
1315    if (create_flag == 0)
1316      {
1317 	/* Check to see if the .newsrc file exists -- I should use the access
1318 	 * system call but for now, do it this way.
1319 	 */
1320 	if (NULL == (fp = fopen (Slrn_Newsrc_File, "r")))
1321 	  {
1322 #if 0 /* now disabled in group.c */
1323 	     slrn_error (_("Unable to open %s.  I will try .newsrc."), Slrn_Newsrc_File);
1324 	     if (NULL == (fp = slrn_open_home_file (".newsrc", "r", file,
1325 						    sizeof (file), 0)))
1326 	       {
1327 #endif
1328 		  slrn_exit_error (_("\nUnable to open %s.\n\
1329 If you want to create %s, add command line options:\n\
1330    -f %s --create\n"), Slrn_Newsrc_File, Slrn_Newsrc_File, Slrn_Newsrc_File);
1331 #if 0
1332 	       }
1333 #endif
1334 	  }
1335 	slrn_fclose (fp);
1336      }
1337 
1338    /* make sure we have an entry for the server */
1339    slrn_add_to_server_list (Slrn_Server_Obj->sv_name, NULL, NULL, NULL);
1340 
1341    if (kill_logfile != NULL)
1342      {
1343 	Slrn_Kill_Log_FP = fopen(kill_logfile, "a");
1344 	if (Slrn_Kill_Log_FP == NULL)
1345 	  slrn_error (_("Unable to open %s for logging."), kill_logfile);
1346      }
1347 
1348    lock_file (1);
1349 
1350    (void) slrn_run_hooks (HOOK_STARTUP, 0);
1351    Ran_Startup_Hook = 1;
1352 
1353 #if SLRN_HAS_GROUPLENS
1354    if (Slrn_Server_Id != SLRN_SERVER_ID_NNTP)
1355      Slrn_Use_Group_Lens = 0;
1356 
1357    if (Slrn_Use_Group_Lens && (Slrn_Batch == 0))
1358      {
1359 	slrn_message (_("Initializing GroupLens"));
1360 	if (-1 == slrn_init_grouplens ())
1361 	  {
1362 	     fprintf (stderr, _("GroupLens disabled.\n"));
1363 	     Slrn_Use_Group_Lens = 0;
1364 	     Slrn_Saw_Warning = 1;
1365 	  }
1366      }
1367 #endif
1368 
1369    if (Slrn_Server_Obj->sv_initialize () != 0)
1370      {
1371 	slrn_exit_error (_("Failed to initialize server."));
1372      }
1373 
1374    if (Slrn_Server_Obj->sv_has_xover == 1)
1375      {
1376 	if (0 == (Slrn_Server_Obj->sv_has_xover = slrn_read_overview_fmt ()))
1377 	  slrn_message (_("OVERVIEW.FMT not RFC 2980 compliant -- XOVER support disabled."));
1378      }
1379 
1380    if (Slrn_Check_New_Groups || create_flag)
1381      {
1382 	slrn_get_new_groups (create_flag);
1383      }
1384 
1385    slrn_read_newsrc (create_flag);
1386    slrn_read_group_descriptions ();
1387 
1388    if (wait_for_key && ((wait_for_key == 1) || Slrn_Saw_Warning))
1389      {
1390 	slrn_message (_("* Press Ctrl-C to quit, any other key to continue.\n"));
1391 	slrn_set_display_state (SLRN_TTY_INIT);
1392 	if ('\003' == SLang_getkey ())
1393 	  slrn_exit_error (_("Exit on user request."));
1394      }
1395    else
1396      putc ('\n', stdout);
1397 
1398    slrn_set_display_state (SLRN_SMG_INIT | SLRN_TTY_INIT);
1399 
1400 #if defined(__unix__) && !defined(IBMPC_SYSTEM)
1401    if (Slrn_Autobaud) SLtt_Baud_Rate = SLang_TT_Baud_Rate;
1402 #endif
1403 
1404    return 0;
1405 }
1406 
1407 /*}}}*/
1408 
1409 /*{{{ Main Loop and Key Processing Functions */
1410 #define MAX_MODE_STACK_LEN 15
1411 /* Allow one extra because the top one is the current mode */
1412 static Slrn_Mode_Type *Mode_Stack [MAX_MODE_STACK_LEN + 1];
1413 static unsigned int Mode_Stack_Depth;
1414 
slrn_push_mode(Slrn_Mode_Type * mode)1415 void slrn_push_mode (Slrn_Mode_Type *mode)
1416 {
1417    if (Mode_Stack_Depth == MAX_MODE_STACK_LEN)
1418      slrn_exit_error (_("Internal Error: Mode_Stack overflow"));
1419 
1420    Mode_Stack[Mode_Stack_Depth] = Slrn_Current_Mode;
1421    Mode_Stack_Depth++;
1422 
1423    Mode_Stack[Mode_Stack_Depth] = Slrn_Current_Mode = mode;
1424 
1425    Slrn_Full_Screen_Update = 1;
1426    if ((mode != NULL) && (mode->enter_mode_hook != NULL))
1427      (*mode->enter_mode_hook) ();
1428 }
1429 
slrn_pop_mode(void)1430 void slrn_pop_mode (void)
1431 {
1432    if (Mode_Stack_Depth == 0)
1433      slrn_exit_error (_("Internal Error: Mode_Stack underflow"));
1434 
1435    Mode_Stack [Mode_Stack_Depth] = NULL;   /* null current mode */
1436    Mode_Stack_Depth--;
1437    Slrn_Current_Mode = Mode_Stack[Mode_Stack_Depth];
1438 
1439    Slrn_Full_Screen_Update = 1;
1440    if ((Slrn_Current_Mode != NULL) && (Slrn_Current_Mode->enter_mode_hook != NULL))
1441      (*Slrn_Current_Mode->enter_mode_hook) ();
1442 }
1443 
slrn_update_screen(void)1444 void slrn_update_screen (void)
1445 {
1446    Slrn_Mode_Type *mode;
1447    unsigned int i, imax;
1448 
1449    if (Slrn_Batch) return;
1450 
1451    slrn_push_suspension (0);
1452    imax = Mode_Stack_Depth + 1;	       /* include current mode */
1453    for (i = 0; i < imax; i++)
1454      {
1455 	Slrn_Full_Screen_Update = 1;
1456 	mode = Mode_Stack [i];
1457 	if ((mode != NULL)
1458 	    && (mode->redraw_fun != NULL))
1459 	  (*mode->redraw_fun) ();
1460      }
1461    slrn_smg_refresh ();
1462    slrn_pop_suspension ();
1463 }
1464 
run_winch_functions(int old_r,int old_c)1465 static void run_winch_functions (int old_r, int old_c)
1466 {
1467    Slrn_Mode_Type *mode;
1468    unsigned int i, imax;
1469 
1470    imax = Mode_Stack_Depth + 1;	       /* include current */
1471    for (i = 0; i < imax; i++)
1472      {
1473 	mode = Mode_Stack [i];
1474 	if ((mode != NULL)
1475 	    && (mode->sigwinch_fun != NULL))
1476 	  (*mode->sigwinch_fun) (old_r, old_c);
1477      }
1478 
1479    if (SLang_get_error() == 0)
1480      slrn_run_hooks (HOOK_RESIZE_SCREEN, 0);
1481 }
1482 
slrn_hangup(int sig)1483 static void slrn_hangup (int sig) /*{{{*/
1484 {
1485    Slrn_Mode_Type *mode;
1486    unsigned int i;
1487 
1488    slrn_init_hangup_signals (0);
1489 
1490    i = Mode_Stack_Depth + 1;	       /* include current */
1491    while (i != 0)
1492      {
1493 	i--;
1494 	mode = Mode_Stack [i];
1495 	if ((mode != NULL)
1496 	    && (mode->hangup_fun != NULL))
1497 	  (*mode->hangup_fun) (sig);
1498      }
1499 
1500    slrn_write_newsrc (0);
1501    slrn_quit (sig);
1502 }
1503 
1504 /*}}}*/
1505 
slrn_call_command(char * cmd)1506 void slrn_call_command (char *cmd) /*{{{*/
1507 {
1508    SLKeymap_Function_Type *list;
1509 
1510    if ((Slrn_Current_Mode == NULL)
1511        || (Slrn_Current_Mode->keymap == NULL))
1512      list = NULL;
1513    else
1514      list = Slrn_Current_Mode->keymap->functions;
1515 
1516    while ((list != NULL) && (list->name != NULL))
1517      {
1518 	if (0 == strcmp (cmd, list->name))
1519 	  {
1520 	     (void) (*list->f) ();
1521 	     /* sync the line number to avoid surprises in subsequent calls
1522 	      * that might change the article window (not the article itself) */
1523 	     if (Slrn_Current_Mode->mode == SLRN_ARTICLE_MODE)
1524 	       slrn_art_sync_article (Slrn_Current_Article);
1525 	     return;
1526 	  }
1527 	list++;
1528      }
1529 
1530    slrn_error (_("call: %s not in current keymap."), cmd);
1531 }
1532 
1533 /*}}}*/
1534 
slrn_getkey(void)1535 int slrn_getkey (void)
1536 {
1537    static char buf[32];
1538    static unsigned int buf_len;
1539    static int timeout_active;
1540 
1541    int ch;
1542 
1543    if (SLang_Key_TimeOut_Flag == 0)
1544      {
1545 	timeout_active = 0;
1546 	buf_len = 0;
1547      }
1548    else	if ((timeout_active || (0 == SLang_input_pending (10)))
1549 	    && (buf_len + 2 < sizeof (buf)))
1550      {
1551 	int r, c;
1552 
1553 	buf[buf_len] = '-';
1554 	buf[buf_len + 1] = 0;
1555 
1556 	slrn_push_suspension (0);
1557 	r = SLsmg_get_row (); c = SLsmg_get_column ();
1558 	slrn_message ("%s", buf);
1559 	SLsmg_gotorc (r, c);
1560 	slrn_smg_refresh ();
1561 	slrn_pop_suspension ();
1562 	timeout_active = 1;
1563      }
1564 
1565    while (1)
1566      {
1567 #if defined(REAL_UNIX_SYSTEM) && (SLANG_VERSION < 20202)
1568 	int ttyfd = SLang_TT_Read_FD;
1569 #endif
1570 	int e;
1571 	errno = 0;
1572 
1573 	ch = SLang_getkey ();
1574 	if (ch != SLANG_GETKEY_ERROR)
1575 	  break;
1576 	e = errno;
1577 
1578 #if defined(REAL_UNIX_SYSTEM) && (SLANG_VERSION < 20202)
1579 	if (ttyfd != SLang_TT_Read_FD)
1580 	  continue;
1581 #endif
1582 
1583 	slrn_exit_error ("%s: errno=%d [%s]", _("SLang_getkey failed"),
1584 			 e, SLerrno_strerror (e));
1585      }
1586 
1587    if (buf_len + 4 < sizeof (buf))
1588      {
1589 	if (ch == 0)
1590 	  {
1591 	     /* Need to handle NULL character. */
1592 	     buf[buf_len++] = '^';
1593 	     buf[buf_len] = '@';
1594 	  }
1595 	else if (ch == 27)
1596 	  {
1597 	     buf[buf_len++] = 'E';
1598 	     buf[buf_len++] = 'S';
1599 	     buf[buf_len++] = 'C';
1600 	     buf[buf_len] = ' ';
1601 	  }
1602 	else buf[buf_len] = (char) ch;
1603      }
1604    buf_len++;
1605 
1606    return ch;
1607 }
1608 
slrn_do_keymap_key(SLKeyMap_List_Type * map)1609 void slrn_do_keymap_key (SLKeyMap_List_Type *map) /*{{{*/
1610 {
1611    SLang_Key_Type *key;
1612    static SLKeyMap_List_Type *last_map;
1613    static SLang_Key_Type *last_key;
1614 
1615    Suspend_Sigtstp_Suspension = 1;
1616    key = SLang_do_key (map, slrn_getkey);
1617    Suspend_Sigtstp_Suspension = 0;
1618 
1619    if (Slrn_Message_Present || SLang_get_error())
1620      {
1621 	if (SLang_get_error()) SLang_restart (0);
1622 	slrn_clear_message ();
1623      }
1624    SLKeyBoard_Quit = 0;
1625    SLang_set_error (0);
1626 
1627    if ((key == NULL) || (key->type == 0))
1628      {
1629 	SLtt_beep ();
1630 	return;
1631      }
1632 
1633    if (key->type == SLKEY_F_INTRINSIC)
1634      {
1635 	if ((map == last_map) && (key->f.f == (FVOID_STAR) slrn_repeat_last_key))
1636 	  key = last_key;
1637 
1638 	/* set now to avoid problems with recursive call */
1639 	last_key = key;
1640 	last_map = map;
1641 
1642 	if (key->type == SLKEY_F_INTRINSIC)
1643 	  {
1644 	     (((void (*)(void))(key->f.f)) ());
1645 	     return;
1646 	  }
1647      }
1648 
1649    /* Otherwise we have interpreted key. */
1650    last_key = key;
1651    last_map = map;
1652 
1653    Slrn_Full_Screen_Update = 1;
1654 
1655    if ((*key->f.s == '.')
1656        || !SLang_execute_function (key->f.s))
1657      SLang_load_string(key->f.s);
1658 }
1659 
1660 /*}}}*/
1661 
slrn_set_prefix_argument(int rep)1662 void slrn_set_prefix_argument (int rep) /*{{{*/
1663 {
1664    static int repeat;
1665 
1666    repeat = rep;
1667    Slrn_Prefix_Arg_Ptr = &repeat;
1668 }
1669 
1670 /*}}}*/
1671 
slrn_digit_arg(void)1672 void slrn_digit_arg (void) /*{{{*/
1673 {
1674    char buf[20];
1675    unsigned char key;
1676    unsigned int i;
1677 
1678    i = 0;
1679    buf[i++] = (char) SLang_Last_Key_Char;
1680 
1681    SLang_Key_TimeOut_Flag = 1;
1682 
1683    while (1)
1684      {
1685 	buf[i] = 0;
1686 	key = (unsigned char) slrn_getkey ();
1687 	if ((key < '0') || (key > '9')) break;
1688 	if (i + 1 < sizeof (buf))
1689 	  {
1690 	     buf[i] = (char) key;
1691 	     i++;
1692 	  }
1693      }
1694 
1695    SLang_Key_TimeOut_Flag = 0;
1696    slrn_set_prefix_argument (atoi (buf));
1697 
1698    SLang_ungetkey (key);
1699    if ((Slrn_Current_Mode != NULL)
1700        && (Slrn_Current_Mode->keymap != NULL))
1701      slrn_do_keymap_key (Slrn_Current_Mode->keymap);
1702 
1703    Slrn_Prefix_Arg_Ptr = NULL;
1704 }
1705 
1706 /*}}}*/
1707 
slrn_repeat_last_key(void)1708 void slrn_repeat_last_key (void) /*{{{*/
1709 {
1710    SLtt_beep ();
1711 }
1712 
1713 /*}}}*/
1714 
main(int argc,char ** argv)1715 int main (int argc, char **argv) /*{{{*/
1716 {
1717    if (-1 == main_init_and_parse_args (argc, argv))
1718      return 1;
1719 
1720    if (-1 == slrn_select_group_mode ())
1721      return 1;
1722 
1723    slrn_push_suspension (1);
1724 
1725    (void) slrn_run_hooks (HOOK_GROUP_MODE_STARTUP, 0);
1726 
1727    if (Slrn_Batch) return 1;
1728 
1729    if (SLang_get_error ())
1730      {
1731 	SLang_restart (1);	       /* prints any queued messages */
1732 	slrn_exit_error (_("An error was encountered during initialization."));
1733      }
1734 
1735    while (Slrn_Current_Mode != NULL)
1736      {
1737 	if (SLKeyBoard_Quit)
1738 	  {
1739 	     SLKeyBoard_Quit = 0;
1740 	     slrn_error (_("Quit!"));
1741 	  }
1742 
1743 	(void) slrn_handle_interrupts ();
1744 
1745 	if (SLang_get_error() || !SLang_input_pending(0))
1746 	  {
1747 	     slrn_update_screen ();
1748 	  }
1749 
1750 	slrn_do_keymap_key (Slrn_Current_Mode->keymap);
1751      }
1752 
1753    return 1;
1754 }
1755 
1756 /*}}}*/
1757 
1758 /*}}}*/
1759 
slrn_posix_system(char * cmd,int reset)1760 int slrn_posix_system (char *cmd, int reset) /*{{{*/
1761 {
1762    int ret;
1763    int init_mode = Slrn_TT_Initialized;
1764 
1765    if (reset) slrn_set_display_state (0);
1766    ret = SLsystem (cmd);
1767    if (reset) slrn_set_display_state (init_mode);
1768    Slrn_Full_Screen_Update = 1;
1769    return ret;
1770 }
1771 
1772 /*}}}*/
1773