This is slrn.c in view mode; [Download] [Up]
/* Copyright (c) 1995 John E. Davis (davis@space.mit.edu) * All rights reserved. */ #include "config.h" #include "features.h" #include <stdio.h> #include <signal.h> #include <string.h> #ifdef HAVE_STDLIB_H # include <stdlib.h> #endif #ifdef HAVE_UNISTD_H # include <unistd.h> #endif #ifdef VMS # include <unixlib.h> # include <unixio.h> # include <dvidef.h> # include <descrip.h> #else # ifndef sun # include <sys/ioctl.h> # endif # ifdef HAVE_TERMIOS_H # include <termios.h> # endif # ifdef SYSV # include <sys/termio.h> # include <sys/stream.h> # include <sys/ptem.h> # include <sys/tty.h> # endif #endif /* !VMS */ #include <slang.h> #include "jdmacros.h" #include <errno.h> #include "server.h" #include "slrn.h" #include "group.h" #include "misc.h" #include "startup.h" #include "art.h" #include "score.h" #if SLRN_HAS_GROUPLENS # include "grplens.h" #endif #if SLRN_HAS_SLANG # include "interp.h" #endif #ifdef __os2__ # define INCL_VIO # include <os2.h> #endif #ifdef SIGSTOP # define USE_RESUME_SMG #endif int Slrn_TT_Initialized = 0; /* If -1, force mouse. If 1 the mouse will be used on in XTerm. If 0, * do not use it. */ int Slrn_Use_Mouse; char *Slrn_Version = SLRN_VERSION; int Slrn_Suspension_Ok; void slrn_set_suspension (int ok) { if (ok == Slrn_Suspension_Ok) return; #ifdef USE_RESUME_SMG SLtty_set_suspend_state (ok); #endif Slrn_Suspension_Ok = ok; } void slrn_smg_refresh (void) { int ok = Slrn_Suspension_Ok; if (ok) slrn_set_suspension (0); if (Slrn_TT_Initialized & 1) SLsmg_refresh (); if (ok) slrn_set_suspension (1); } static void set_signal_to_restart_syscalls (int sig) { #ifdef SLRN_POSIX_SIGNALS struct sigaction sa; if (-1 == sigaction (sig, NULL, &sa)) return; # ifdef SIGALRM if (sig == SIGALRM) { # ifdef SA_INTERRUPT sa.sa_flags |= SA_INTERRUPT; # endif } else { # endif /* SIGALRM */ # ifdef SA_RESTART sa.sa_flags |= SA_RESTART; # endif # ifdef SIGALRM } # endif (void) sigaction (sig, &sa, NULL); #endif /* SLRN_POSIX_SIGNALS */ } static int Can_Suspend; void slrn_suspend (int sig) { #ifndef SIGSTOP slrn_error ("Not implemented."); #else int init = Slrn_TT_Initialized; # ifdef SLRN_POSIX_SIGNALS sigset_t mask; if (Can_Suspend == 0) { slrn_error ("Suspension not allowed by shell."); return; } sigemptyset (&mask); sigaddset (&mask, SIGTSTP); /* This function resets SIGTSTP to default */ slrn_reset_display (sig); kill (getpid (), SIGTSTP); /* If SIGTSTP is pending, it will be delivered now. That's ok. */ sigprocmask (SIG_UNBLOCK, &mask, NULL); # else if (Can_Suspend == 0) { slrn_error ("Suspension not allowed by shell."); return; } slrn_reset_display (sig); kill(getpid(),SIGSTOP); # endif slrn_init_display (init, sig); if (Slrn_Suspension_Ok) SLtty_set_suspend_state (1); #endif } static void test_lock( char *file ) { int pid; FILE *fp; if ((fp = fopen (file, "r")) != NULL) { if (1 == fscanf (fp, "%d", &pid) ) { if ((pid > 0) && (0 == kill (pid, 0))) { slrn_exit_error ("slrn: pid %d is locking the newsrc file.", pid); } } slrn_fclose (fp); } } static int make_lock( char *file ) { int pid; FILE *fp; #ifdef VMS fp = fopen (file, "w", "fop=cif"); #else fp = fopen (file, "w"); #endif if (fp == NULL) return -1; pid = getpid (); fprintf (fp, "%d", pid); slrn_fclose (fp); return 0; } static void lock_file (int how) { char file[1024]; char name[256]; static int not_ok_to_unlock; #if SLRN_HAS_RNLOCK int rnlock = 0; char file_rn[1024]; #endif if (Slrn_Newsrc_File == NULL) return; if (not_ok_to_unlock) return; not_ok_to_unlock = 1; #ifndef __os2__ sprintf (name, "%s-lock", Slrn_Newsrc_File); #else sprintf (name, "lk-%s", Slrn_Newsrc_File); #endif slrn_make_home_filename (name, file); #if SLRN_HAS_RNLOCK if (0 == strcmp(".newsrc", Slrn_Newsrc_File)) { rnlock = 1; slrn_make_home_filename (".rnlock", file_rn); } #endif if (how == 1) { test_lock (file); #if SLRN_HAS_RNLOCK if (rnlock) test_lock (file_rn); #endif if (-1 == make_lock (file)) { slrn_exit_error ("Unable to create lock file %s.", file); } #if SLRN_HAS_RNLOCK if (rnlock && (-1 == make_lock (file_rn))) { slrn_delete_file (file); /* delete the "normal" lock file */ slrn_exit_error ("Unable to create lock file %s.", file_rn); } #endif } else { if (-1 == slrn_delete_file (file)) { /* slrn_exit_error ("Unable to remove lockfile %s.", file); */ } #if SLRN_HAS_RNLOCK if (rnlock && -1 == slrn_delete_file (file_rn)) { /* slrn_exit_error ("Unable to remove lockfile %s.", file_rn); */ } #endif } not_ok_to_unlock = 0; } static void slrn_set_screen_size (int sig) { int r = 0, c = 0; #ifdef VMS int status, code; unsigned short chan; $DESCRIPTOR(dev_dsc, "SYS$INPUT:"); #endif #ifdef __os2__ VIOMODEINFO vioModeInfo; #endif #ifdef TIOCGWINSZ struct winsize wind_struct; do { if ((ioctl(1,TIOCGWINSZ,&wind_struct) == 0) || (ioctl(0, TIOCGWINSZ, &wind_struct) == 0) || (ioctl(2, TIOCGWINSZ, &wind_struct) == 0)) { c = (int) wind_struct.ws_col; r = (int) wind_struct.ws_row; break; } } while (errno == EINTR); #endif #ifdef VMS status = sys$assign(&dev_dsc,&chan,0,0,0); if (status & 1) { code = DVI$_DEVBUFSIZ; status = lib$getdvi(&code, &chan,0, &c, 0,0); if (!(status & 1)) c = 80; code = DVI$_TT_PAGE; status = lib$getdvi(&code, &chan,0, &r, 0,0); if (!(status & 1)) r = 24; sys$dassgn(chan); } #endif #ifdef __os2__ vioModeInfo.cb = sizeof(vioModeInfo); VioGetMode (&vioModeInfo, 0); c = vioModeInfo.col; r = vioModeInfo.row; #endif if (r <= 0) { char *s = getenv ("LINES"); if (s != NULL) r = atoi (s); } if (c <= 0) { char *s = getenv ("COLUMNS"); if (s != NULL) c = atoi (s); } if ((r <= 0) || (r > 200)) r = 24; if ((c <= 0) || (c > 250)) c = 80; SLtt_Screen_Rows = r; SLtt_Screen_Cols = c; Slrn_Group_Window_Size = SLtt_Screen_Rows - 3; #ifdef SIGWINCH if (sig == SIGWINCH) { SLsmg_reset_smg (); SLsmg_init_smg (); if ((Slrn_Current_Mode != NULL) && (Slrn_Current_Mode->sigwinch_fun != NULL)) (*Slrn_Current_Mode->sigwinch_fun) (); slrn_redraw (); } signal (SIGWINCH, slrn_set_screen_size); #endif } static void slrn_hangup (int sig) { slrn_init_hangup_signals (0); if ((Slrn_Current_Mode != NULL) && (Slrn_Current_Mode->hangup_fun != NULL)) (*Slrn_Current_Mode->hangup_fun) (sig); else slrn_write_newsrc (); slrn_quit (sig); } static void init_like_signals (int argc, int *argv, void (*f0)(int), void (*f1)(int), int state) { #ifdef HAVE_SIGACTION struct sigaction sa; #endif int i; if (state == 0) { for (i = 0; i < argc; i++) signal (argv[i], f0); return; } for (i = 0; i < argc; i++) { int sig = argv[i]; signal (sig, f1); #if defined(SLRN_POSIX_SIGNALS) if (-1 != sigaction (sig, NULL, &sa)) { int j; for (j = 0; j < argc; j++) { if (j != i) sigaddset (&sa.sa_mask, argv[j]); } (void) sigaction (sig, &sa, NULL); } #endif } } static void init_suspend_signals (int state) { int argv[2]; int argc = 0; #ifdef SIGTSTP argv[argc++] = SIGTSTP; #endif #ifdef SIGTTIN argv[argc++] = SIGTTIN; #endif init_like_signals (argc, argv, SIG_DFL, slrn_suspend, state); } void slrn_init_hangup_signals (int state) { int argv[2]; int argc = 0; #ifdef SIGHUP argv[argc++] = SIGHUP; #endif #ifdef SIGTERM argv[argc++] = SIGTERM; #endif init_like_signals (argc, argv, SIG_IGN, slrn_hangup, state); } void slrn_enable_mouse (int mode) { #ifndef __os2__ if (Slrn_Use_Mouse) { if (-1 == SLtt_set_mouse_mode (mode, (Slrn_Use_Mouse < 0))) Slrn_Use_Mouse = 0; } #endif } /* if 1, init just slsmg. If 2, just sltt, if 3, do both */ void slrn_init_display (int init_mode, int sig) { Slrn_TT_Initialized |= init_mode; init_suspend_signals (1); #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif #ifdef SIGTTOU /* Allow background writes */ signal (SIGTTOU, SIG_IGN); #endif slrn_init_hangup_signals (1); if (init_mode & 2) { SLang_init_tty (7, 1, 0); SLang_set_abort_signal (NULL); #ifdef SIGINT set_signal_to_restart_syscalls (SIGINT); #endif } if (init_mode & 1) { slrn_enable_mouse (1); #ifdef USE_RESUME_SMG if (sig == SIGTSTP) SLsmg_resume_smg (); else { #endif slrn_set_screen_size (0); SLsmg_init_smg (); /* We do not want the -> overlay cursor to affect the scroll. */ #ifndef __os2__ SLsmg_Scroll_Hash_Border = 2; #endif slrn_redraw (); } #ifdef USE_RESUME_SMG } #endif } void slrn_reset_display (int sig) { int init = Slrn_TT_Initialized; if (init == 0) return; slrn_enable_mouse (0); #ifdef USE_RESUME_SMG if (sig == SIGTSTP) SLsmg_suspend_smg (); else { #endif SLsmg_gotorc (SLtt_Screen_Rows - 1, 0); slrn_smg_refresh (); SLsmg_reset_smg (); #ifdef SIGWINCH signal (SIGWINCH, SIG_DFL); #endif #ifdef USE_RESUME_SMG } #endif if (init & 2) SLang_reset_tty (); init_suspend_signals (0); Slrn_TT_Initialized = 0; } void slrn_tty_message (int newline_flag, char *fmt, ...) { va_list ap; if (newline_flag & 1) putc ('\n', stdout); va_start (ap, fmt); vfprintf (stdout, fmt, ap); va_end(ap); if (newline_flag & 2) putc ('\n', stdout); fflush (stdout); } int slrn_get_new_news (int no_new_groups, int create_flag) { char msg1[] = "Checking for new groups..."; char msg2[] = "Checking news..."; char msg3[] = "Checking news via active file..."; char *msg; if (Slrn_Server_Obj->sv_initialize () != 0) return (-1); if (create_flag == 0) { if (no_new_groups == 0) { if (Slrn_TT_Initialized & 1) { slrn_message (msg1); slrn_smg_refresh (); } else slrn_tty_message (1, msg1); slrn_check_new_groups (create_flag); } if (Slrn_List_Active_File) msg = msg3; else msg = msg2; if (Slrn_TT_Initialized & 1) { slrn_message (msg); slrn_smg_refresh (); } else slrn_tty_message (1, msg); } slrn_read_newsrc (create_flag); slrn_read_group_descriptions (); return (0); } char *Slrn_Newsrc_File = NULL; char *Slrn_Lib_Dir = NULL; #if !defined (SLRN_DEFAULT_SERVER_OBJ) # if SLRN_HAS_NNTP_SUPPORT # define SLRN_DEFAULT_SERVER_OBJ SLRN_SERVER_ID_NNTP # else # define SLRN_DEFAULT_SERVER_OBJ SLRN_SERVER_ID_SPOOL # endif #endif #if !defined (SLRN_DEFAULT_POST_OBJ) # if SLRN_HAS_NNTP_SUPPORT # define SLRN_DEFAULT_POST_OBJ SLRN_POST_ID_NNTP # else # define SLRN_DEFAULT_POST_OBJ SLRN_POST_ID_INEWS # endif #endif static void version (void) { unsigned int a, b, c; a = SLANG_VERSION/10000; b = (SLANG_VERSION - a * 10000) / 100; c = SLANG_VERSION - (a * 10000) - (b * 100); #if defined(__DATE__) && defined(__TIME__) fprintf (stdout, "slrn version: %s (%s %s)\n", Slrn_Version, __DATE__, __TIME__); #else fprintf (stdout, "slrn version: %s\n", Slrn_Version); #endif fprintf (stdout, "S-Lang version: %u.%u.%u\n", a, b, c); fprintf (stdout, "\nslrn compiled with support for:\n"); #if SLRN_HAS_NNTP_SUPPORT fputs (" nntp", stdout); #endif #if SLRN_HAS_SPOOL_SUPPORT fputs (" spool", stdout); #endif #if SLRN_HAS_INEWS_SUPPORT fputs (" inews", stdout); #endif #if SLRN_HAS_GROUPLENS fputs (" GroupLens", stdout); #endif #if SLRN_HAS_SLANG fputs (" S-Lang", stdout); #endif fputs ("\nDefault server object: ", stdout); switch (SLRN_DEFAULT_SERVER_OBJ) { case SLRN_SERVER_ID_NNTP: fputs ("nntp", stdout); break; case SLRN_SERVER_ID_SPOOL: fputs ("spool", stdout); break; } fputs ("\nDefault posting mechanism: ", stdout); switch (SLRN_DEFAULT_POST_OBJ) { case SLRN_POST_ID_NNTP: fputs ("nntp", stdout); break; case SLRN_POST_ID_INEWS: fputs ("inews", stdout); #if SLRN_FORCE_INEWS fputs (" (fixed)", stdout); #endif break; } fputc ('\n', stdout); exit (0); } static void usage (char *extra) { fputs ("\ Usage: slrn [--inews ...] [--nntp ...] [--spool ...] OPTIONS\n\ -n Do not check for new groups. This usually results in\n\ a faster startup.\n\ -f newsrc-file Name of the newsrc file to use.\n\ -C Use colors.\n\ -create Create a newsrc file by getting list of groups from server.\n\ -d Get new text descriptions of each group from server.\n\ Note: This may take a LONG time to retrieve this information.\n\ The resulting file can be several hundred Kilobytes!\n\ -i init-file Name of initialization file to use (default .slrnrc)\n\ -Dname Add 'name' to list of predefined preprocessing tokens.\n\ -k Do not process score file.\n\ -k0 Process score file but inhibit expensive scores.\n\ -a Use active file for getting new news\n\ -m Force XTerm mouse reporting\n\ --help Print this usage.\n\ --version Show version and supported features\n\ \n\ For additional info use one of forms:\n\ slrn --inews --help\n\ slrn --nntp --help\n\ slrn --spool --help\n\ ", stderr); if (extra != NULL) { fprintf (stderr, "\n%s\n", extra); } exit (1); } static int parse_object_args (char *obj, char **argv, int argc) { int num_parsed; int zero_ok = 1; if (obj == NULL) { zero_ok = 0; #if SLRN_DEFAULT_SERVER_OBJ == SLRN_SERVER_ID_SPOOL obj = "spool"; #endif #if SLRN_DEFAULT_SERVER_OBJ == SLRN_SERVER_ID_NNTP obj = "nntp"; #endif } num_parsed = slrn_parse_object_args (obj, argv, argc); if (num_parsed == -1) { slrn_exit_error ("%s is not a supported option.", *argv); } if ((num_parsed == 0) && (zero_ok == 0)) usage (NULL); return num_parsed; } int main (int argc, char **argv) { char *hlp_file; int i; int create_flag = 0; int no_new_groups = 0; int no_score_file = 0; int use_color = 0; int use_mouse = 0; int dsc_flag = 0; int use_active = 0; FILE *fp; char file[512]; char *init_file = SLRN_USER_SLRNRC_FILENAME; Can_Suspend = 0; #ifdef SIGTSTP if (SIG_DFL == signal (SIGTSTP, SIG_DFL)) { Can_Suspend = 1; } #endif #ifdef __os2__ SLdefine_for_ifdef ("OS2"); #else # ifdef VMS SLdefine_for_ifdef ("VMS"); # else SLdefine_for_ifdef ("UNIX"); # endif #endif #if 0 if (NULL != getenv ("AUTOSUBSCRIBE")) Slrn_Unsubscribe_New_Groups = 0; if (NULL != getenv ("AUTOUNSUBSCRIBE")) Slrn_Unsubscribe_New_Groups = 1; #endif for (i = 1; i < argc; i++) { if (!strcmp ("--spool", argv[i]) || !strcmp ("--nntp", argv[i]) || !strcmp ("--inews", argv[i])) { i += parse_object_args (argv[i] + 2, argv + (i + 1), argc - (i + 1)); } else if (!strcmp ("--help", argv[i])) usage (NULL); else if (!strcmp ("-create", argv[i])) create_flag = 1; else if (!strcmp ("-C", argv[i])) use_color = 1; else if (!strcmp ("-a", argv[i])) use_active = 1; else if (!strcmp ("-n", argv[i])) no_new_groups = 1; else if (!strcmp ("-d", argv[i])) dsc_flag = 1; else if (!strcmp ("-m", argv[i])) use_mouse = 1; else if (!strcmp ("-k", argv[i])) no_score_file = 1; else if (!strcmp ("-k0", argv[i])) Slrn_Perform_Scoring &= ~SLRN_EXPENSIVE_SCORING; else if (!strcmp ("--version", argv[i])) { version (); } else if (!strncmp ("-D", argv[i], 2) && (argv[i][2] != 0)) { if (SLdefine_for_ifdef (argv[i] + 2) == 0) { slrn_exit_error ("Unable to add preprocessor name %s.", argv[i] + 2); } } else if (i + 1 < argc) { if (!strcmp ("-f", argv[i])) Slrn_Newsrc_File = argv[++i]; else if (!strcmp ("-i", argv[i])) init_file = argv[++i]; else { i += parse_object_args (NULL, argv + i, argc - i); i -= 1; } } else { i += parse_object_args (NULL, argv + i, argc - i); i -= 1; } } if (-1 == slrn_init_objects ()) { slrn_exit_error ("Error configuring server objects."); } if (Slrn_Server_Id == 0) Slrn_Server_Id = SLRN_DEFAULT_SERVER_OBJ; if (Slrn_Post_Id == 0) Slrn_Post_Id = SLRN_DEFAULT_POST_OBJ; /* It has been suggested that this be moved to after reading the startup * files. */ if ((-1 == slrn_select_server_object (Slrn_Server_Id)) || (-1 == slrn_select_post_object (Slrn_Post_Id))) { slrn_exit_error ("Unable to select object."); } fprintf (stdout, "slrn %s\n", Slrn_Version); if (dsc_flag && create_flag) { usage ("The -d and -create flags must not be specified together."); } SLtt_get_terminfo(); if (use_color) SLtt_Use_Ansi_Colors = 1; slrn_startup_initialize (); slrn_get_user_info (); if (Slrn_Lib_Dir == NULL) { Slrn_Lib_Dir = getenv ("SLRN_LIB_DIR"); } if (Slrn_Lib_Dir == NULL) Slrn_Lib_Dir = SLRN_LIB_DIR; #ifdef VMS sprintf (file, "%s%s", Slrn_Lib_Dir, "slrn.rc"); #else sprintf (file, "%s/%s", Slrn_Lib_Dir, "slrn.rc"); #endif #if SLRN_HAS_SLANG if (-1 == slrn_init_slang ()) { fprintf (stderr, "Error initializing S-Lang interpreter.\n"); } #endif slrn_read_startup_file (file); /* global file for all users */ slrn_read_startup_file (init_file); #ifndef __os2__ /* Allow blink characters if in mono */ if (SLtt_Use_Ansi_Colors == 0) SLtt_Blink_Mode = 1; #endif /* Now that we have read in the startup file, check to see if the user * has a username and a usable hostname. Without those, we are not * starting up. */ if (0 == slrn_is_fqdn (Slrn_User_Info.host)) { slrn_exit_error ("\ Unable to find a fully qualified hostname. You will have to specify a\r\n\ hostname in your %s file.\r\n", SLRN_USER_SLRNRC_FILENAME); } if ((NULL == Slrn_User_Info.username) || (0 == *Slrn_User_Info.username) || (NULL != slrn_strchr (Slrn_User_Info.username, '@'))) { slrn_exit_error ("\ Unable to find you user name. This means that a valid 'From' header line\r\n\ cannot be constructed. Try setting the USER environment variable.\r\n"); } if ((no_score_file == 0) && (Slrn_Score_File != NULL) && (-1 == slrn_read_score_file (Slrn_Score_File))) { slrn_exit_error ("Error processing score file %s", Slrn_Score_File); } hlp_file = getenv ("SLRNHELP"); if (hlp_file != NULL) { slrn_parse_helpfile (hlp_file); } if ((Slrn_Newsrc_File == NULL) && ((Slrn_Newsrc_File = slrn_map_file_to_host (Slrn_Server_Obj->sv_name)) == NULL)) #if defined(VMS) || defined(__os2__) Slrn_Newsrc_File = "jnews.rc"; #else Slrn_Newsrc_File = ".jnewsrc"; #endif if (use_active) Slrn_List_Active_File = 1; if (use_mouse) Slrn_Use_Mouse = -1; /* -1 forces it. */ if (dsc_flag) { if (Slrn_Server_Obj->sv_initialize () != 0) { slrn_exit_error ("Unable to initialize server."); return (-1); } slrn_get_group_descriptions (); Slrn_Server_Obj->sv_close (); return 0; } if (create_flag == 0) { /* Check to see if the .newrc file exists--- I should use the access * system call but for now, do it this way. */ if (NULL == (fp = slrn_open_home_file (Slrn_Newsrc_File, "r", file, 0))) { slrn_error ("Unable to open %s. I will try .newsrc.", file); if (NULL == (fp = slrn_open_home_file (".newsrc", "r", file, 0))) { slrn_exit_error ("\r\nUnable to open %s.\r\n\ If you want to create %s, add command line options:\r\n\ -f %s -create\r\n", file, file, file); } } slrn_fclose (fp); lock_file (1); } #ifdef SLRN_HAS_SLANG (void) SLang_run_hooks ("startup_hook", NULL, NULL); #endif #if SLRN_HAS_GROUPLENS if (Slrn_Use_Group_Lens) { fprintf (stdout, "Initializing GroupLens\n"); if (-1 == slrn_init_grouplens ()) { fprintf (stderr, "GroupLens disabled.\n"); Slrn_Use_Group_Lens = 0; } } #endif if (-1 == slrn_get_new_news (no_new_groups, create_flag)) { slrn_exit_error ("Failed to initialize server."); } putc ('\n', stdout); slrn_init_display (1 | 2, 0); #if defined(unix) && !defined(__os2__) if (Slrn_Autobaud) SLtt_Baud_Rate = SLang_TT_Baud_Rate; #endif slrn_main_loop (); slrn_quit (0); return 0; } void slrn_quit (int retcode) { Slrn_Server_Obj->sv_close (); slrn_reset_display (0); lock_file (0); #if SLRN_HAS_GROUPLENS slrn_close_grouplens (); #endif if (retcode) fprintf (stderr, "slrn: quiting on signal %d.\n", retcode); exit (retcode); } void slrn_exit_error (char *fmt, ...) { va_list ap; if (Slrn_Groups_Dirty) slrn_write_newsrc (); if (Slrn_TT_Initialized) { slrn_reset_display (0); Slrn_Server_Obj->sv_close (); } #if SLRN_HAS_GROUPLENS slrn_close_grouplens (); #endif if (fmt != NULL) { va_start (ap, fmt); vfprintf (stderr, fmt, ap); va_end(ap); } putc ('\n', stderr); lock_file (0); exit (-1); } int slrn_main_loop (void) { if (-1 == slrn_select_group_mode ()) return -1; #ifdef SLRN_HAS_SLANG (void) SLang_run_hooks ("group_mode_hook", NULL, NULL); #endif while (1) { if (SLang_Error || !SLang_input_pending(0)) { (*Slrn_Current_Mode->redraw_fun) (); slrn_smg_refresh (); } slrn_do_key (Slrn_Current_Mode->keymap); } }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.