home *** CD-ROM | disk | FTP | other *** search
/ MacGames Sampler / PHT MacGames Bundle.iso / MacSource Folder / Samples from the CD / Editors / emacs / Emacs-1.14b1-sources / sources / unix-emulation-src / vfork.c < prev   
Encoding:
C/C++ Source or Header  |  1994-05-30  |  16.6 KB  |  672 lines  |  [TEXT/EMAC]

  1. /*
  2.  * Copyright (C) 1993, 1994 Marc Parmet.
  3.  * This file is part of the Macintosh port of GNU Emacs.
  4.  *
  5.  * GNU Emacs is distributed in the hope that it will be useful,
  6.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  7.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  8.  * GNU General Public License for more details.
  9.  */
  10.  
  11. #if defined(THINK_C)
  12. #include <MacHeaders>
  13. #else
  14. #include <Types.h>
  15. #include <Memory.h>
  16. #include <Quickdraw.h>
  17. #include <Windows.h>
  18. #include <TextEdit.h>
  19. #include <Resources.h>
  20. #include <LowMem.h>
  21. #endif
  22.  
  23. #include "errno.h"
  24. #include "unix-types.h"
  25.  
  26. int current_process_index = 0;
  27. struct proc proctable[MAXPROC];
  28. static int next_pid = 2;
  29.  
  30. #if defined(powerc)
  31.  
  32. static UniversalProcPtr bridge_68k_upp;
  33. static UniversalProcPtr longjmp_68k_upp;
  34.  
  35. enum {
  36.     bridge_68k_ProcInfo = kCStackBased
  37.          | RESULT_SIZE(SIZE_CODE(sizeof(int)))
  38.          | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(int (*)())))
  39.          | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(int)))
  40.          | STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(char **))),
  41.     longjmp_68k_ProcInfo = kCStackBased
  42.          | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(long)))
  43.          | STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(long)))
  44. };
  45.  
  46. #endif
  47.  
  48. #if 0
  49. void
  50. printnum(char *s,int x)
  51. {
  52.     char u[256];
  53.     sprintf(u,"%s: %x",s,x);
  54.     CtoPstr(u);
  55.     DebugStr68k((unsigned char *)u);
  56. }
  57. #endif
  58.  
  59. static void
  60. setup_stdio(int index)
  61. {
  62.     int i;
  63.     struct proc *proc = &proctable[index];
  64.  
  65.     /* No sharing of stdio buffers */
  66.     for (i = 0; i<_NFILE; ++i) {
  67.         proc->iob[i].base = 0L;
  68.         proc->iob[i].cnt = proctable[index].iob[i].buflen = 0;
  69.     }
  70.  
  71.     proc->iob[0].fd = 0;
  72.     proc->iob[0].flag = _READ | _AUTO;
  73.     proc->iob[1].fd = 1;
  74.     proc->iob[1].flag = _WRITE | _AUTO;
  75.     proc->iob[2].fd = 2;
  76.     proc->iob[2].flag = _WRITE;
  77. }
  78.  
  79. static void
  80. initialize_one_proc(struct proc *p,int ppid,char *cwd_string,char ***environ_address,
  81.                     int *errno_address)
  82. {
  83.     int i;
  84.  
  85.     p->pid = next_pid++;
  86.     p->ppid = ppid;
  87.     p->vforked = 0;
  88.     p->zombie = 0;
  89.     p->homeResFile = 0;
  90.     p->heapHandle = 0L;
  91.     p->stackbottom = 0L;
  92.     p->start_time = 0;
  93.     p->alarm_time = 0;
  94.     p->errno_address = errno_address;
  95.     p->environ_address = environ_address;
  96.     p->cwd_string = cwd_string;
  97.     for (i = 0; i<MAXDESC; ++i)
  98.         p->fd[i].flavor = fd_flavor_unused;
  99.     for (i = 0; i<=NSIG; ++i)
  100.         p->sig[i] = SIG_DFL;
  101. }
  102.  
  103. void
  104. init_proc()
  105. {
  106. #if defined(powerc)
  107.     Handle longjmp_68k,bridge_68k;
  108.  
  109.     bridge_68k = GetResource('CODE',129);
  110.     if (bridge_68k == 0L) ExitToShell();
  111.     MoveHHi(bridge_68k);
  112.     HLock(bridge_68k);
  113.     bridge_68k_upp = NewRoutineDescriptor((long (*)())*bridge_68k,
  114.                                           bridge_68k_ProcInfo,kM68kISA);
  115.  
  116.     longjmp_68k = GetResource('CODE',130);
  117.     if (longjmp_68k == 0L) ExitToShell();
  118.     MoveHHi(longjmp_68k);
  119.     HLock(longjmp_68k);
  120.     longjmp_68k_upp = NewRoutineDescriptor((long (*)())*longjmp_68k,
  121.                                            longjmp_68k_ProcInfo,kM68kISA);
  122. #endif
  123.  
  124.     initialize_one_proc(&proctable[current_process_index],1,0L,0L,0L);
  125.     setup_stdio(0);
  126. }
  127.  
  128. static int
  129. get_free_proc(void)
  130. {
  131.     int i;
  132.     
  133.     for (i = 0; i<MAXPROC; ++i)
  134.         if (proctable[i].pid == 0)
  135.             return i;
  136.     
  137.     return -1;
  138. }
  139.  
  140. static void
  141. restart(jmp_buf jbuf,int retcode,int from_vfork)
  142. {
  143. #if defined(powerc)
  144.     if (current_process_index > 0 && from_vfork) {
  145. //printnum("Jumping back to vfork",current_process_index);
  146.         CallUniversalProc(longjmp_68k_upp,longjmp_68k_ProcInfo,jbuf,retcode);
  147.     }
  148.     else
  149.         longjmp(jbuf,retcode);
  150. #else
  151.     longjmp(jbuf,retcode);
  152. #endif
  153. }
  154.  
  155. static void
  156. start_some_process(void)
  157. {
  158.     int i;
  159.     struct proc *p;
  160.     
  161.     for (i = 0; i<MAXPROC; ++i) {
  162.         current_process_index = (current_process_index + 1) % MAXPROC;
  163.         p = &proctable[current_process_index];
  164.         if (p->pid != 0 && !p->zombie && !p->vforked) {
  165.             p->start_time = TickCount();
  166.             if (p->savearea.regs.from_vfork)
  167.                 /* We are exiting vfork for the second time, back to the parent process. */
  168.                 kernel_exit(&p->savearea.lomem);
  169.             else
  170.                 /* The last process could have been running with some other zone. */
  171.                 SetZone(ApplicZone());
  172.             restart(p->savearea.regs.jbuf,p->savearea.regs.d0_or_r3,p->savearea.regs.from_vfork);
  173.         }
  174.     }
  175.     
  176.     /* There is no process to run.  This is the usual exit point. */
  177.     ExitToShell();
  178. }
  179.  
  180. void
  181. run_another_process(void)
  182. {
  183.     proctable[current_process_index].savearea.regs.from_vfork = 0;
  184.     if (setjmp(proctable[current_process_index].savearea.regs.jbuf) == 0) {
  185.         proctable[current_process_index].savearea.regs.d0_or_r3 = 1;
  186.         start_some_process();
  187.     }
  188. }
  189.  
  190. static void
  191. call_signal_function(int (*f)())
  192. {
  193. #if defined(powerc)
  194.     if (current_process_index > 0) {
  195.         /* We are calling the handler of a utility */
  196. //DebugStr68k("\pCalling sigfunc of utility");
  197.         CallUniversalProc((UniversalProcPtr)f,0);
  198.     }
  199.     else
  200.         /* We are calling a handler of Emacs */
  201.         (*f)();
  202. #else
  203.     (*f)();
  204. #endif
  205. }
  206.  
  207. static void
  208. time_for_signal_function(struct savearea_regs *regs)
  209. {
  210.     call_signal_function(regs->sigfunc);
  211.     restart(regs->jbuf,regs->d0_or_r3,regs->from_vfork);
  212. }
  213.  
  214. static void
  215. queue_up_signal_function(int target_index,int (*f)(),struct savearea_lomem *lomem)
  216. {
  217.     struct proc *p;
  218.     long stacktop,regs_addr;
  219.  
  220.     if (target_index == current_process_index) {
  221.         kernel_exit(lomem);
  222.         call_signal_function(f);
  223.         kernel_entry(lomem);
  224.     }
  225.     else {
  226.         proctable[target_index].savearea.regs.sigfunc = f;
  227.         stacktop = (long)proctable[target_index].savearea.regs.jbuf[jmp_buf_sp];
  228.         *--*(struct savearea_regs **)&stacktop = proctable[target_index].savearea.regs;
  229.         regs_addr = stacktop;
  230.         *--*(long **)&stacktop = regs_addr; /* Parameter area */
  231.         proctable[target_index].savearea.regs.from_vfork = 0;
  232. #if defined(powerc)
  233.         stacktop -= sizeof(struct linkage_area); /* Fake linkage area */
  234.         proctable[target_index].savearea.regs.jbuf[jmp_buf_sp] = (long *)stacktop;
  235.         proctable[target_index].savearea.regs.jbuf[jmp_buf_pc] = ((long **)time_for_signal_function)[0];
  236.         proctable[target_index].savearea.regs.jbuf[jmp_buf_toc] = ((long **)time_for_signal_function)[1];
  237.         proctable[target_index].savearea.regs.d0_or_r3 = regs_addr;
  238. #else
  239.         stacktop -= sizeof(long); /* Fake return address */
  240.         proctable[target_index].savearea.regs.jbuf[jmp_buf_sp] = stacktop;
  241.         proctable[target_index].savearea.regs.jbuf[jmp_buf_pc] = (long)time_for_signal_function;
  242. #endif
  243.     }
  244. }
  245.  
  246. int
  247. pid_to_index(int pid)
  248. {
  249.     int i;
  250.     
  251.     for (i = 0; i<MAXPROC; ++i)
  252.         if (proctable[i].pid == pid)
  253.             return i;
  254.     
  255.     return -1;
  256. }
  257.  
  258. static void
  259. remove_zombie(int index)
  260. {
  261.     struct proc *p = &proctable[index];
  262.     if (p->stackbottom) DisposPtr(p->stackbottom);
  263.     p->pid = 0;
  264. }
  265.  
  266. static void
  267. become_zombie(int index,int status)
  268. {
  269.     int i,parent_index,duplicate;
  270.     struct proc *p = &proctable[index];
  271.     int kill_internal(int pid,int signal,struct savearea_lomem *lomem);
  272.     
  273.     /* Remove any child zombies */
  274.     for (i = 0; i<MAXPROC; ++i)
  275.         if (proctable[i].pid != 0 && proctable[i].ppid == p->pid && proctable[i].zombie)
  276.             remove_zombie(i);
  277.     
  278.     p->status = status;
  279.     
  280.     if (p->homeResFile != 0) {
  281.         duplicate = 0;
  282.         for (i = 0; i<MAXPROC; ++i)
  283.             if (i != index && proctable[i].pid != 0 &&
  284.                     proctable[i].homeResFile == p->homeResFile)
  285.                 duplicate = 1;
  286.         
  287.         if (!duplicate) CloseResFile(p->homeResFile);
  288.     }
  289.  
  290.     if (p->heapHandle != 0L) DisposHandle(p->heapHandle);
  291.     if (p->cwd_string != 0L) DisposPtr(p->cwd_string);
  292.     
  293.     parent_index = pid_to_index(p->ppid);
  294.     if (parent_index == -1)
  295.         p->pid = 0;
  296.     else {
  297.         if (proctable[parent_index].vforked == p->pid)
  298.             proctable[parent_index].vforked = 0;
  299.         p->zombie = 1;
  300. //printnum("Sending SIGCHLD to:",parent_index);
  301.         kill_internal(proctable[parent_index].pid,SIGCHLD,(struct savearea_lomem *)0L);
  302.     }
  303. }
  304.  
  305. int
  306. kill_internal(int pid,int signal,struct savearea_lomem *lomem)
  307. {
  308.     int (*f)();
  309.     int i,target_index,retcode;
  310.  
  311.     if (pid < 0) {
  312.         /* Send signal to children also. */
  313.         for (i = 0; i<MAXPROC; ++i)
  314.             if (proctable[i].ppid == -pid)
  315.                 kill_internal(-proctable[i].pid,signal,lomem);
  316.         kill_internal(-pid,signal,lomem);
  317.     }
  318.     else if (pid == 1) {
  319.         /* Send signal to every process.  Send it to current process last. */
  320.         for (i = 0; i<MAXPROC; ++i)
  321.             if (proctable[i].pid != 0 && i != current_process_index)
  322.                 kill_internal(proctable[i].pid,signal,lomem);
  323.         kill_internal(proctable[current_process_index].pid,signal,lomem);
  324.     }
  325.     else {
  326.         target_index = pid_to_index(pid);
  327.         if (target_index == -1) { set_errno(EUNDOC); return -1; }
  328.     
  329.         if (proctable[target_index].zombie) { set_errno(EUNDOC); return -1; }
  330.         if (signal == 0) return 0;
  331.         if (signal < 0 || signal > NSIG) { set_errno(EUNDOC); return -1; }
  332.     
  333.         f = (signal == SIGKILL) ? SIG_DFL : proctable[target_index].sig[signal];
  334.     
  335.         if (f == SIG_IGN)
  336.             return 0;
  337.         else if (f == SIG_DFL) {
  338.             switch (signal) {
  339.             case SIGKILL:
  340.             case SIGINT:
  341.             case SIGABRT:
  342.             case SIGILL:
  343.             case SIGFPE:
  344.             case SIGSEGV:
  345.             case SIGHUP:
  346.             case SIGQUIT:
  347.             case SIGTRAP:
  348.             case SIGBUS:
  349.             case SIGIOT:
  350.             case SIGPIPE:
  351.                 close_all_internal(target_index);
  352.                 become_zombie(target_index,signal);
  353.                 if (target_index == current_process_index)
  354.                     start_some_process();
  355.                 else
  356.                     return 0;
  357.             default:
  358.                 return 0;
  359.             }
  360.         }
  361.         else {
  362.             queue_up_signal_function(target_index,f,lomem);
  363.             return 0;
  364.         }
  365.     }
  366. }
  367.  
  368. long
  369. sleep_internal(long n)
  370. {
  371.     int expire = TickCount() + n*60;
  372.     while (TickCount() < expire)
  373.         run_another_process();
  374.     return 0;
  375. }
  376.  
  377. int
  378. raise_internal(int signal,struct savearea_lomem *lomem)
  379. {
  380.     return kill_internal(proctable[current_process_index].pid,signal,lomem);
  381. }
  382.  
  383. int (*signal_internal(int sig,int (*f)()))()
  384. {
  385.     int (*oldf)();
  386.     if (sig <= 0 || sig > NSIG) return (int (*)())-1;
  387.     oldf = proctable[current_process_index].sig[sig];
  388.     proctable[current_process_index].sig[sig] = f;
  389.     return oldf;
  390. }
  391.  
  392. void
  393. _exit_internal(long status)
  394. {
  395. //printnum("In _exit",status);
  396.     close_all_internal(current_process_index);
  397.     become_zombie(current_process_index,status << 8);
  398. //printnum("Starting some other process in _exit_internal",0);
  399.     start_some_process();
  400. }
  401.  
  402. void
  403. exit_internal(long status)
  404. {
  405.     int i;
  406.  
  407. //printnum("In exit",status);
  408.  
  409.     for (i = 0; i<_NFILE; ++i)
  410.         if (proctable[current_process_index].iob[i].flag != 0)
  411.             fclose_internal(&proctable[current_process_index].iob[i],0L);
  412.     _exit_internal(status);
  413. }
  414.  
  415. long
  416. wait3_internal(long *status,long nohang,long ignored)
  417. {
  418.     int i,pid;
  419.     
  420.     while (1) {
  421.         /* Look for zombies */
  422.         for (i = 0; i<MAXPROC; ++i) {
  423.             if (proctable[i].pid != 0 &&
  424.                     proctable[i].ppid == proctable[current_process_index].pid &&
  425.                     proctable[i].zombie) {
  426.                 if (status != 0L) *status = proctable[i].status;
  427.                 pid = proctable[i].pid;
  428.                 remove_zombie(i);
  429.                 return pid;
  430.             }
  431.         }
  432.         
  433.         /* No zombies, look for children */
  434.         for (i = 0; i<MAXPROC; ++i)
  435.             if (proctable[i].ppid == proctable[current_process_index].pid)
  436.                 break;
  437.         
  438.         if (i == MAXPROC) {
  439.             /* No children */
  440.             set_errno(0);
  441.             return -1;
  442.         }
  443.  
  444.         /* Have children */
  445.         if (nohang) return 0;
  446.  
  447.         /* Wait for a zombie */
  448.         run_another_process();
  449.     }
  450. }
  451.  
  452. /*
  453.  * vfork is done like this, from vfork.h:
  454.  *
  455.  *     #define vfork() vfork2(setjmp(vfork1()))
  456.  *
  457.  * vfork1() returns the address of the save area of the current thread.
  458.  * setjmp stores the current register state into that savearea, and
  459.  * returns zero.  vfork2 sees that zero, then stores the low-memory state
  460.  * into that savearea, and simply changes the index of the current
  461.  * process to that of the child process.  Then vfork2 returns with a
  462.  * result of 0, indicating to the caller that we are now in the child
  463.  * thread.  The parent thread is blocked until the child calls execvp.
  464.  *
  465.  * When the child calls execvp, the parent is freed, and the parent
  466.  * continues execution by restoring the low-memory state and doing a
  467.  * longjmp using the register state saved earlier.  vfork2 sees this time
  468.  * a non-zero input, and simply returns that input immediately.  The
  469.  * parent then executes normally.
  470.  */
  471.  
  472. int
  473. vfork1_internal(void)
  474. {
  475.     proctable[current_process_index].savearea.regs.from_vfork = 1;
  476.     return (int)&proctable[current_process_index].savearea.regs.jbuf;
  477. }
  478.  
  479. int
  480. vfork2_internal(struct savearea_lomem *lomem,int setjmp_result)
  481. {
  482.     int i,child_index;
  483.     struct proc *child,*parent;
  484.     char *child_cwd_string;
  485.  
  486.     if (setjmp_result != 0) return setjmp_result;
  487.     
  488.     child_index = get_free_proc();
  489.     if (child_index == -1) { set_errno(EUNDOC); return -1; }
  490.     child = &proctable[child_index];
  491.     parent = &proctable[current_process_index];
  492.  
  493.     child_cwd_string = NewPtr(strlen(parent->cwd_string) + 1);
  494.     strcpy(child_cwd_string,parent->cwd_string);
  495.     initialize_one_proc(child,parent->pid,child_cwd_string,
  496.                         parent->environ_address,parent->errno_address);
  497.     for (i = 0; i<_NFILE; ++i)
  498.         child->iob[i] = parent->iob[i];
  499.     
  500.     for (i = 0; i<MAXDESC; ++i) {
  501.         if (parent->fd[i].flavor != fd_flavor_unused) {
  502.             add_given_fd(child_index,i,parent->fd[i].flavor,
  503.                          parent->fd[i].dispatch,parent->fd[i].object);
  504.             ++(**parent->fd[i].object).count;
  505.         }
  506.     }
  507.  
  508.     parent->savearea.lomem = *lomem;
  509.     parent->vforked = child->pid;
  510.     parent->savearea.regs.d0_or_r3 = child->pid;
  511.  
  512.     current_process_index = child_index;
  513.  
  514.     return 0;
  515. }
  516.  
  517. static char **child_argv;
  518. static int argc;
  519. static int (*pcode)();
  520.  
  521. #if defined(powerc)
  522.  
  523. static void
  524. execvp_bridge_from_601(void)
  525. {
  526.     exit_internal(CallUniversalProc(bridge_68k_upp,bridge_68k_ProcInfo,
  527.                                     pcode,argc,child_argv));
  528. }
  529.  
  530. #else
  531.  
  532. static void
  533. execvp_bridge_from_68k(void)
  534. {
  535.     /* Call main of the extension */
  536.     exit_internal((*pcode)(argc,child_argv));
  537. }
  538.  
  539. #endif
  540.  
  541. int
  542. execvp_internal(char *file,char **parent_argv)
  543. {
  544.     FSSpec spec;
  545.     short homeResFile;
  546.     int (**hcode)();
  547.     char **hz,*hp,*stacktop;
  548.     int i,heap_size,parent_index,stack_size,exitcode;
  549.     short err;
  550.     jmp_buf bridge_jmp_buf;
  551.     struct proc *child = &proctable[current_process_index],*parent;
  552. #if defined(powerc)
  553. #pragma options align=mac68k
  554. #endif
  555.     struct size {
  556.         short flags;
  557.         long size;
  558.         long minsize;
  559.     } **hsize,**ssize;
  560. #if defined(powerc)
  561. #pragma options align=reset
  562. #endif
  563.  
  564.     hcode = 0L;
  565.     hz = 0L;
  566.     
  567.     parent_index = pid_to_index(child->ppid);
  568.     if (parent_index == -1) goto error;
  569.     parent = &proctable[parent_index];
  570.  
  571.     /* Get resources of child. */
  572.     err = unixfn2FSSpec_internal(file,&spec,0);
  573.     if (err) goto error;
  574.     child->homeResFile = FSpOpenResFile(&spec,fsRdPerm);
  575.     if (child->homeResFile == -1) goto error;
  576.     
  577.     /* Get main code segment of child. */
  578.     hcode = (int (**)())GetResource('CODE',0);
  579.     if (hcode == 0L) goto error;
  580.     /* hcode should be locked when loaded, but let's lock it anyway. */
  581.     HLock((Handle)hcode);
  582.  
  583.     setup_stdio(current_process_index);
  584.  
  585.     /* Set up stack for child.  The stack comes from within the heap of Emacs. */
  586.     ssize = (struct size **)GetResource('SIZE',129);
  587.     stack_size = (ssize == 0L ? LMGetDefltStack() : (**ssize).size);
  588.     child->stackbottom = NewPtr(stack_size);
  589.     if (MemError()) goto error;
  590.     stacktop = child->stackbottom + stack_size;
  591.  
  592.     /* Set up heap for child.  This will change the current heap.  The heap
  593.        comes from temporary memory. */
  594.     hsize = (struct size **)GetResource('SIZE',128);
  595.     heap_size = (hsize == 0L ? 32768 : (**hsize).size);
  596.     hz = TempNewHandle(heap_size,&err);
  597.     if (hz == 0L) goto error;
  598.     child->heapHandle = hz;
  599.     HLock(hz);
  600.     hp = StripAddress(*hz);
  601.     InitZone(0L,16,(char *)hp+heap_size,hp);
  602.     if (MemError()) goto error;
  603.  
  604.     pcode = *hcode;
  605.  
  606.     /* Copy argument list into the heap of the child. */
  607.     for (argc = 0; parent_argv[argc] != 0L; ++argc)
  608.         ;
  609.     child_argv = (char **)NewPtr((argc + 1) * sizeof(char *));
  610.     if (MemError()) goto error;
  611.     for (i = 0; i<argc; ++i) {
  612.         child_argv[i] = NewPtr(strlen(parent_argv[i]) + 1);
  613.         if (MemError()) goto error;
  614.         strcpy(child_argv[i],parent_argv[i]);
  615.     }
  616.     child_argv[argc] = 0L;
  617.  
  618.     /* Give child its own errno. */
  619.     child->errno_address = 0L;
  620.     
  621.     /* Parent can run now. */
  622.     parent->vforked = 0;
  623.     
  624.     /* Start up the child. */
  625.     LMHiHeapMark = child->stackbottom;
  626.     LMSetHeapEnd(child->stackbottom);
  627.     LMSetApplLimit(child->stackbottom);
  628.     
  629.     /* Code resources address off of a4, so we must set that.  Each code resource
  630.        gets its own stack, so we set that.  The bridge code will push the parameters
  631.        to main onto the stack, and deal with normal returns from main. */
  632. #if defined(THINK_C)
  633.     bridge_jmp_buf[jmp_buf_pc] = (long)execvp_bridge_from_68k;
  634.     bridge_jmp_buf[jmp_buf_a4] = (long)pcode;
  635.     bridge_jmp_buf[jmp_buf_sp] = (long)stacktop;
  636. #elif defined(powerc)
  637.     bridge_jmp_buf[jmp_buf_pc] = ((long **)execvp_bridge_from_601)[0];
  638.     bridge_jmp_buf[jmp_buf_toc] = ((long **)execvp_bridge_from_601)[1];
  639.     /* Leave stack space for three parameters plus linkage area. */
  640.     stacktop -= 3 * sizeof(long) + sizeof(struct linkage_area);
  641.     bridge_jmp_buf[jmp_buf_sp] = (long *)stacktop;
  642. #endif
  643.     longjmp(bridge_jmp_buf,1);
  644.  
  645.     error:
  646.     set_errno(EUNDOC);
  647.     /* The caller must call _exit. */
  648.     return -1;
  649. }
  650.  
  651. static int max(a,b) { return a<b ? b : a; }
  652.  
  653. int
  654. alarm_internal(int new_alarm_time)
  655. {
  656.     int old_alarm_time;
  657.     int now = TickCount();
  658.  
  659.     old_alarm_time = proctable[current_process_index].alarm_time;
  660.     if (old_alarm_time != 0) old_alarm_time = max(0,(old_alarm_time - now) / 60);
  661.     if (new_alarm_time != 0) new_alarm_time = new_alarm_time * 60 + now;
  662.     proctable[current_process_index].alarm_time = new_alarm_time;
  663.     return old_alarm_time;
  664. }
  665.  
  666. long
  667. pause_internal(void)
  668. {
  669.     run_another_process();
  670.     return 0;
  671. }
  672.