Version 1.0 Initial Release R5 release Version 1.1 Append command displays dialog box. Version 1.2 Annotations avoid automatic rescan of folder. Version 1.3 Fcc bug fixed. Fcc: truncated after first folder name. Open folders on FCC list no longer force a rescan of that folder. Side affect is that send can not do a push if FCC list includes an open folder. Version 1.4 There were two reasons that folders on the FCC were inadvertently scanned. The first cause was fixed in patch 3. This fixes the second problem. An extra NEWLINE was sometimes appended to Open folders that were targets of an FCC. Version 1.5 Most diffs reflect internal reorganization of the signal handling code. The signal handling routines in the original xmh are not reentrant. One change from the X11R4 version is that msgck is called periodically to check for new mail. That call to a process replaces a call to stat to check the modified time of the mail spool file. In the initial R5 release, the signal handling of the msgchk call can interfere with the signal handling of the editor call. This patch includes reentrant signal handling that we have been using localling as more external xmh editor users were having problems. A -DREENTRANT in the DEFINES for the Imakefile will enable these changes. These changes have also been forwarded to MIT. The pick routines have been modified to mark the cache as valid. A pick could sometime force an unnecessary rescan of a folder Prereq: patch.4 diff -c3 ../xmh.baseline/Patchlevel ./Patchlevel *** ../xmh.baseline/Patchlevel Tue Jan 28 10:01:19 1992 --- ./Patchlevel Wed Apr 22 11:18:46 1992 *************** *** 1 **** ! Xmh.sei.mods patch.4 --- 1 ---- ! Xmh.sei.mods patch.5 diff -c3 ../xmh.baseline/README_MODS ./README_MODS *** ../xmh.baseline/README_MODS Thu Nov 28 09:54:36 1991 --- ./README_MODS Wed Apr 22 11:18:53 1992 *************** *** 54,59 **** --- 54,71 ---- -DNEED_STRSTR if strstr is not in libc (Sun 4.0.3 and Ultrix 3.1) + -DREENTRANT + The signal handling routines in the original xmh are + not reentrant. One change from the X11R4 version is + that msgck is called periodically to check for new + mail. That call to a process replaces a call to stat + to check the modified time of the mail spool file. In + the initial R5 release, the signal handling of + the msgchk call can interfere with the signal handling + of the editor call. Only a few users have reported + problems (crashes after a random number of calls to the + editor). + b) There is a gnu xmh.el file available from ftp.sei.cmu.edu in pub/xmh. If that file is available then add "#define GNU" statement that is commented out in editor.c. See the comments in that file diff -c3 ../xmh.baseline/command.c ./command.c *** ../xmh.baseline/command.c Mon Jan 13 11:28:38 1992 --- ./command.c Wed Apr 22 11:18:51 1992 *************** *** 133,149 **** False); } #ifdef EDITOR extern void catch_sigchld(); int Do_pid; int childdone; /* Gets nonzero when the child process finishes. */ #else static int childdone; /* Gets nonzero when the child process finishes. */ - #endif /* EDITOR */ /* ARGSUSED */ static void --- 133,271 ---- False); } + #ifdef REENTRANT + static int MAXPROCESS = 15; + static int *Processes; + + void InitCmdList() + { + int i; + #ifndef EDITOR + void ChildDone(); + #endif + + Processes = (int *)XtMalloc((Cardinal)MAXPROCESS * sizeof(int)); + for (i = 0; i < MAXPROCESS; i++) + Processes[i] = 0; + #ifndef EDITOR + (void) signal(SIGCHLD, ChildDone); + #endif + } + + + void SetProcess(pid) + int pid; + { + int i,j; + char *ptr; + + /* It is theoretically possible but in practice impossible for the child to + have died before we call SetProcess. In that case the pid will already be + on the list. The call to SetProcess will be from the signal handler and in + this case we take the pid off the list */ + + for (i =0; i < MAXPROCESS; i++) + if(Processes[i] == pid){ + Processes[i] = 0; /* process died before SetProcess call */ + return; /* take it off the list */ + } + + /* usual situation, find first free slot */ + for (i =0; i < MAXPROCESS; i++) + if(Processes[i] == 0){ + Processes[i] = pid; + return; + } + /* should never get here but to be safe increase size. The number of + processes should not execeed two in practice */ + + j = MAXPROCESS+10; + ptr = XtRealloc((char *)Processes, j * sizeof(int)); + if(ptr){ + Processes = (int *)ptr; + /* put pid on first of new list and set rest zero */ + Processes[MAXPROCESS] = pid; + for (i = MAXPROCESS +1; i < j; i++) + Processes[i] = 0; + MAXPROCESS = j; + } + else + PopupError((Widget)NULL,"Too many commands"); + } + + Boolean SetDone(pid) + int pid; + { + /* Remove process id from the list. Editor processes are kept on a + separate list so it may return false if passed an editor pid. + */ + int i; + + for (i =0; i < MAXPROCESS; i++) + if(Processes[i] == pid){ + Processes[i] = 0; + return True; + } + return False; + } + + Boolean IsDone(pid) + int pid; + { + /* If done then is no longer on the list. Only applies to commands started via + calls to functions in command.c + */ + int i; + + for (i =0; i < MAXPROCESS; i++) + if(Processes[i] == pid){ + return False; + } + return True; + } + #endif /* REENTRANT */ + #ifdef EDITOR extern void catch_sigchld(); + #ifndef REENTRANT int Do_pid; int childdone; /* Gets nonzero when the child process finishes. */ + #endif + #else /* EDITOR */ + #ifdef REENTRANT + /*ARGSUSED*/ + void + ChildDone(signo) + int signo; + { + + int forkpid,i; + + if (signo != SIGCHLD) { + #if defined(SYSV) || defined(SYSV_SIGNAL) + signal(SIGCHLD, ChildDone); + #endif + return; + /* whoops! */ + } + + forkpid = wait((int *)NULL); + + #if defined(SYSV) || defined(SYSV_SIGNAL) + signal(SIGCHLD, ChildDone); + #endif + + if (SetDone(forkpid)) + return; + else + SetProcess(forkpid); + } #else static int childdone; /* Gets nonzero when the child process finishes. */ /* ARGSUSED */ static void *************** *** 152,158 **** --- 274,293 ---- { childdone++; } + #endif /* REENTRANT */ + #endif /* EDITOR */ + #ifdef REENTRANT + Boolean subProcessRunning() + { + int i; + for (i =0; i < MAXPROCESS; i++) + if(Processes[i] != 0) + return TRUE; + return FALSE; + } + #endif /* REENTRANT */ + /* Execute the given command, and wait until it has finished. While the command is executing, watch the X socket and cause Xlib to read in any incoming data. This will prevent the socket from overflowing during *************** *** 168,176 **** XtAppContext appCtx = XtWidgetToApplicationContext(toplevel); int return_status; int old_stdin, old_stdout, old_stderr; ! #ifndef EDITOR int pid; ! #endif /* EDITOR */ fd_set readfds, fds; Boolean output_to_pipe = False; CommandStatus status = XtNew(CommandStatusRec); --- 303,317 ---- XtAppContext appCtx = XtWidgetToApplicationContext(toplevel); int return_status; int old_stdin, old_stdout, old_stderr; ! #ifdef REENTRANT int pid; ! #else ! #ifdef EDITOR ! #define pid Do_pid ! #else ! int pid; ! #endif ! #endif fd_set readfds, fds; Boolean output_to_pipe = False; CommandStatus status = XtNew(CommandStatusRec); *************** *** 228,279 **** (void) dup2(outputfd, fileno(stdout)); close(outputfd); } childdone = FALSE; status->popup = (Widget)NULL; status->lastInput = lastInput; status->error_buffer = NULL; status->error_buf_size = 0; - #ifndef EDITOR - (void) signal(SIGCHLD, ChildDone); - pid = vfork(); - #else #if defined(SYSV) || defined(SYSV_SIGNAL) (void) signal(SIGCHLD, catch_sigchld); #endif ! Do_pid = vfork(); ! #endif /* EDITOR */ if (inputfd != -1) { - #ifdef EDITOR - if (Do_pid != 0) dup2(old_stdin, fileno(stdin)); - #else if (pid != 0) dup2(old_stdin, fileno(stdin)); - #endif /* EDITOR */ close(old_stdin); } if (outputfd != -1) { - #ifdef EDITOR - if (Do_pid != 0) dup2(old_stdout, fileno(stdout)); - #else if (pid != 0) dup2(old_stdout, fileno(stdout)); - #endif /* EDITOR */ close(old_stdout); } if (status->error_pipe[0]) { - #ifdef EDITOR - if (Do_pid != 0) dup2(old_stderr, fileno(stderr)); - #else if (pid != 0) dup2(old_stderr, fileno(stderr)); - #endif /* EDITOR */ close(old_stderr); } - #ifdef EDITOR - if (Do_pid == -1) Punt("Couldn't fork!"); - if (Do_pid) { /* We're the parent process. */ - #else if (pid == -1) Punt("Couldn't fork!"); if (pid) { /* We're the parent process. */ - #endif /* EDITOR */ XEvent typeAheadQueue[TYPEAHEADSIZE], *eventP = typeAheadQueue; XEvent *altQueue = NULL; int type_ahead_count = 0, alt_queue_size = 0, alt_queue_count = 0; --- 369,408 ---- (void) dup2(outputfd, fileno(stdout)); close(outputfd); } + #ifndef REENTRANT childdone = FALSE; + #endif status->popup = (Widget)NULL; status->lastInput = lastInput; status->error_buffer = NULL; status->error_buf_size = 0; #if defined(SYSV) || defined(SYSV_SIGNAL) + #ifdef EDITOR (void) signal(SIGCHLD, catch_sigchld); + #else + (void) signal(SIGCHLD, ChildDone); #endif ! #endif ! pid = vfork(); ! #ifdef REENTRANT ! if(pid != 0 && pid != -1) ! SetProcess(pid); ! #endif if (inputfd != -1) { if (pid != 0) dup2(old_stdin, fileno(stdin)); close(old_stdin); } if (outputfd != -1) { if (pid != 0) dup2(old_stdout, fileno(stdout)); close(old_stdout); } if (status->error_pipe[0]) { if (pid != 0) dup2(old_stderr, fileno(stderr)); close(old_stderr); } if (pid == -1) Punt("Couldn't fork!"); if (pid) { /* We're the parent process. */ XEvent typeAheadQueue[TYPEAHEADSIZE], *eventP = typeAheadQueue; XEvent *altQueue = NULL; int type_ahead_count = 0, alt_queue_size = 0, alt_queue_count = 0; *************** *** 283,297 **** num_fds = status->output_pipe[0]+1; if (status->error_pipe[0] >= num_fds) num_fds = status->error_pipe[0]+1; - #ifdef EDITOR - status->child_pid = Do_pid; - DEBUG1( " pid=%d ", Do_pid ) - #else status->child_pid = pid; DEBUG1( " pid=%d ", pid ) ! #endif /* EDITOR */ subProcessRunning = True; while (!childdone) { while (!(XtAppPending(app) & XtIMXEvent)) { /* this is gross, but the only other way is by * polling on timers or an extra pipe, since we're not --- 412,425 ---- num_fds = status->output_pipe[0]+1; if (status->error_pipe[0] >= num_fds) num_fds = status->error_pipe[0]+1; status->child_pid = pid; DEBUG1( " pid=%d ", pid ) ! #ifdef REENTRANT ! while (!IsDone(pid)) { ! #else subProcessRunning = True; while (!childdone) { + #endif while (!(XtAppPending(app) & XtIMXEvent)) { /* this is gross, but the only other way is by * polling on timers or an extra pipe, since we're not *************** *** 298,315 **** --- 426,456 ---- * guaranteed to be able to malloc in a signal handler. */ readfds = fds; + #ifdef REENTRANT + if (IsDone(pid)) break; + #else if (childdone) break; + #endif DEBUG("blocking.\n") (void) select(num_fds, (int *) &readfds, (int *) NULL, (int *) NULL, (struct timeval *) NULL); + #ifdef REENTRANT + DEBUG1("unblocked; child%s done.\n", IsDone(pid) ? "" : " not") + if (IsDone(pid)) break; + #else DEBUG1("unblocked; child%s done.\n", childdone ? "" : " not") if (childdone) break; + #endif if (!FD_ISSET(ConnectionNumber(theDisplay), &readfds)) {DEBUG("reading alternate input...") XtAppProcessEvent(appCtx, (unsigned)XtIMAlternateInput); DEBUG("read.\n")} } + #ifdef REENTRANT + if (IsDone(pid)) break; + #else if (childdone) break; + #endif XtAppNextEvent(app, eventP); switch(eventP->type) { case LeaveNotify: *************** *** 365,376 **** --- 506,521 ---- XtDispatchEvent(eventP); } } + #ifndef REENTRANT #ifndef EDITOR (void) wait(0); #endif /* EDITOR */ + #endif /* REENTRANT */ DEBUG("done\n") + #ifndef REENTRANT subProcessRunning = False; + #endif if (output_to_pipe) { CheckReadFromPipe( status->output_pipe[0], &status->output_buffer, diff -c3 ../xmh.baseline/editor.c ./editor.c *** ../xmh.baseline/editor.c Mon Jan 13 11:28:39 1992 --- ./editor.c Wed Apr 22 11:18:51 1992 *************** *** 31,40 **** #include #include #include - #include #include #include #include #if defined(sun) && defined(sparc) #include --- 31,41 ---- #include #include #include #include #include + #ifndef REENTRANT #include + #endif #if defined(sun) && defined(sparc) #include *************** *** 266,273 **** --- 267,276 ---- /* defined in command.c */ + #ifndef RREENTRANT extern childdone; extern Do_pid; + #endif int Do_Mail ; *************** *** 275,280 **** --- 278,286 ---- work from the main loop so as to avoid recursion in the signal handler*/ extern int outchannel; + #ifdef REENTRANT + extern Boolean SetDone(); + #endif /*ARGSUSED*/ void *************** *** 298,303 **** --- 304,314 ---- signal(SIGCHLD, catch_sigchld); #endif + #ifdef REENTRANT + /* This is from one of the routines in command.c. return */ + if (SetDone(forkpid)) + return; + #else /* This is from one of the routines in command.c. Just set the flag and return */ if (forkpid == Do_pid){ *************** *** 304,309 **** --- 315,321 ---- childdone++; return; } + #endif /* REENTRANT*/ /* should be an editor exiting */ for(i = 0 ; i < MAXLIST; i++) if(MList[i].process_number == forkpid){ *************** *** 313,319 **** --- 325,335 ---- write(outchannel,"1",1); return; } + #ifdef REENTRANT + SetProcess(forkpid); /* just in case process dies first */ + #else return; + #endif } /* Called from mail loop if there is processing to be done. */ *************** *** 351,356 **** --- 367,375 ---- int i; Do_Mail = False; + #ifdef REENTRANT + InitCmdList(); + #endif for(i = 0; i< MAXLIST; i++){ MList[i].message = NULL; MList[i].action = Not_Used; diff -c3 ../xmh.baseline/globals.h ./globals.h *** ../xmh.baseline/globals.h Thu Nov 28 09:54:40 1991 --- ./globals.h Wed Apr 22 11:18:50 1992 *************** *** 125,131 **** --- 125,135 ---- int y; } lastInput; + #ifdef REENTRANT + ext Boolean subProcessRunning(); /* interlock for DoCommand/CheckMail */ + #else ext Boolean subProcessRunning; /* interlock for DoCommand/CheckMail */ + #endif #define PNullSource (NullSource != NULL ? NullSource : \ (NullSource = (Widget) CreateFileSource(scrn->viewlabel, "/dev/null", False))) diff -c3 ../xmh.baseline/main.c ./main.c *** ../xmh.baseline/main.c Thu Nov 28 09:54:41 1991 --- ./main.c Wed Apr 22 11:18:51 1992 *************** *** 33,39 **** --- 33,43 ---- XtIntervalId *id; /* unused */ { int i; + #ifdef REENTRANT + if (!subProcessRunning()) { + #else if (!subProcessRunning) { + #endif DEBUG("[magic toc check ...") for (i = 0; i < numScrns; i++) { if (scrnList[i]->toc) *************** *** 55,61 **** --- 59,69 ---- { extern void XmhWMProtocols(); + #ifdef REENTRANT + if (!subProcessRunning()) { + #else if (!subProcessRunning) { + #endif Cardinal n = 1; String params = "wm_save_yourself"; DEBUG("(Checkpointing...") *************** *** 74,80 **** --- 82,92 ---- { extern void XmhCheckForNewMail(); + #ifdef REENTRANT + if (!subProcessRunning()) { + #else if (!subProcessRunning) { + #endif DEBUG("(Checking for new mail...") XmhCheckForNewMail(NULL, NULL, NULL, NULL); DEBUG(" done)\n") *************** *** 115,123 **** --- 127,140 ---- inchannel = sv[0]; outchannel = sv[1]; #endif /* EDITOR */ + #ifdef REENTRANT + InitCmdList(); + #endif InitializeWorld(argc, argv); + #ifndef REENTRANT subProcessRunning = False; + #endif appCtx = XtWidgetToApplicationContext(toplevel); (void) XtAppSetWarningMsgHandler(appCtx, PopupWarningHandler); *************** *** 143,149 **** lastInput.win = -1; /* nothing mapped yet */ #ifdef EDITOR XtAppAddInput(XtWidgetToApplicationContext(toplevel), ! inchannel, XtInputReadMask, EditProcessDone, NULL); #endif /* EDITOR */ #ifdef DEBUG_CLEANUP while (!ExitLoop) { --- 160,166 ---- lastInput.win = -1; /* nothing mapped yet */ #ifdef EDITOR XtAppAddInput(XtWidgetToApplicationContext(toplevel), ! inchannel, (XtPointer)XtInputReadMask, EditProcessDone, NULL); #endif /* EDITOR */ #ifdef DEBUG_CLEANUP while (!ExitLoop) { diff -c3 ../xmh.baseline/pick.c ./pick.c *** ../xmh.baseline/pick.c Wed Jul 17 21:28:21 1991 --- ./pick.c Wed Feb 5 09:37:19 1992 *************** *** 28,33 **** --- 28,36 ---- /* pick.c -- handle a pick subwidget. */ #include "xmh.h" + #ifdef ANNOTATE + #include "tocintrnl.h" + #endif #define WTlabel labelWidgetClass #define WTbutton commandWidgetClass *************** *** 446,452 **** --- 449,461 ---- } if (app_resources.block_events_on_busy) ShowBusyCursor(); + #ifdef ANNOTATE + toc->forcecache = True; + #endif cmd_status = DoCommand(argv, (char*)NULL, (char*)NULL); + #ifdef ANNOTATE + TocSetCacheValid(toc); + #endif TocReloadSeqLists(toc); TocChangeViewedSeq(toc, TocGetSeqNamed(toc, toseq));