home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Atari / Gnu / gdb36p4s.zoo / atari-dep.c < prev    next >
C/C++ Source or Header  |  1993-08-26  |  17KB  |  736 lines

  1. /* Machine-dependent code which would otherwise be in inflow.c and core.c,
  2.    for GDB, the GNU debugger.
  3.    Copyright (C) 1986, 1987, 1989 Free Software Foundation, Inc.
  4.  
  5. This file is part of GDB.
  6.  
  7. GDB is free software; you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 1, or (at your option)
  10. any later version.
  11.  
  12. GDB is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. GNU General Public License for more details.
  16.  
  17. You should have received a copy of the GNU General Public License
  18. along with GDB; see the file COPYING.  If not, write to
  19. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  20.  
  21. #include <stdio.h>
  22. #include "defs.h"
  23. #include "param.h"
  24. #include "frame.h"
  25. #include "inferior.h"
  26. #include "symtab.h"
  27.  
  28. #include <osbind.h>
  29. #include <mintbind.h>
  30. #include <unistd.h>
  31. #include <signal.h>
  32. #include <ioctl.h>
  33. #include <process.h>
  34. #include <ctype.h>
  35. #include <st-out.h>
  36. #include <fcntl.h>
  37. #include <stat.h>
  38. #include <regexp.h>
  39. #include <setjmp.h>
  40. #include <limits.h>
  41.  
  42. long _stksize = 100 * 1024;
  43.  
  44. /* Nonzero if we are debugging an attached outside process
  45.    rather than an inferior.  */
  46.  
  47. extern int attach_flag;
  48.  
  49. extern int errno;
  50.  
  51. static int inferior_fd;
  52. long inferior_base_address = 0;
  53.  
  54. #ifndef PTRACESFLAGS
  55. #define PTRACESFLAGS    (('P'<< 8) | 6)
  56. #define PTRACEGFLAGS    (('P'<< 8) | 7)
  57. #define    P_ENABLE    (1 << 0)
  58. #define PTRACEGO    (('P'<< 8) | 8)
  59. #define PTRACESTEP    (('P'<< 8) | 10)
  60. #endif
  61.  
  62. int
  63. call_ptrace (request, pid, procaddr, buf)
  64.      int request, pid, procaddr, buf;
  65. {
  66.   int fd;
  67.   short flag = P_ENABLE;
  68.  
  69.   if (request != 0)
  70.     error ("no ptrace");
  71.   fd = open ("u:/proc/.-1", O_RDONLY);
  72.   if (fd < 0)
  73.     perror_with_name ("ptrace");
  74.   ioctl (fd, PTRACESFLAGS, &flag);
  75.   close (fd);
  76.   return 0;
  77. }
  78.  
  79. #ifdef NO_SHELL
  80. /* If there is no /bin/sh available, this function executes the program,
  81.    recognizing some simple redirections. */
  82.  
  83. int
  84. atari_exec (char *cmd, char **envp)
  85. {
  86.   char *p;
  87.   char *execname;
  88.   char **argv;
  89.   char *input, *output;
  90.   int append;
  91.   int argc;
  92.   int fd;
  93.   short flag = P_ENABLE;
  94.   long ret;
  95.  
  96.   /* This function should not use any library function which can change
  97.      the parent because of vfork (e.g, the isatty flags with open,
  98.      dup, or dup2) */
  99.   execname = cmd + 5;        /* "exec " */
  100.   for (p = execname; *p && !isspace (*p); p++) ;
  101.   argv = alloca ((strlen (p) / 2 + 2) * sizeof (char *));
  102.   argc = 0;
  103.   argv[argc++] = execname;
  104.   input = 0;
  105.   output = 0;
  106.   while (*p)
  107.     {
  108.       *p++ = 0;
  109.       while (isspace (*p))
  110.     p++;
  111.       if (*p == 0)
  112.     break;
  113.       if (*p == '<')
  114.     {
  115.       p++;
  116.       while (isspace (*p))
  117.         p++;
  118.       input = p;
  119.     }
  120.       else if (*p == '>')
  121.     {
  122.       append = *++p == '>';
  123.       if (append)
  124.         p++;
  125.       while (isspace (*p))
  126.         p++;
  127.       output = p;
  128.     }
  129.       else
  130.     argv[argc++] = p;
  131.       while (*p && !isspace (*p))
  132.     p++;
  133.     }
  134.   argv[argc++] = NULL;
  135.   if (input)
  136.     {
  137.       char xinput[PATH_MAX];
  138.       _unx2dos (input, xinput);
  139.       fd = Fopen (xinput, O_RDONLY);
  140.       if (fd >= 0 && fd != 0)
  141.     {
  142.       Fclose (0);
  143.       Fforce (0, fd);
  144.       Fclose (fd);
  145.     }
  146.     }
  147.   if (output)
  148.     {
  149.       char xoutput[PATH_MAX];
  150.       _unx2dos (output, xoutput);
  151.       fd = Fopen (output, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC));
  152.       if (fd >= 0 && fd != 1)
  153.     {
  154.       Fclose (1);
  155.       Fforce (1, fd);
  156.       Fclose (fd);
  157.     }
  158.     }
  159.   fd = Fopen ("U:\\proc\\.-1", 0);
  160.   if (fd < 0)
  161.     {
  162.       errno = -fd;
  163.       return -1;
  164.     }
  165.   if ((ret = Fcntl (fd, &flag, PTRACESFLAGS)) < 0)
  166.     {
  167.       errno = -ret;
  168.       return -1;
  169.     }
  170.   Fclose (fd);
  171.   return _spawnve (P_OVERLAY, execname, argv, envp);
  172. }
  173. #endif
  174.  
  175. static void relocate_apropriate_symbols ();
  176.  
  177. void
  178. relocate_addresses ()
  179. {
  180.   long base_address, text_base;
  181.  
  182.   ioctl (inferior_fd, PBASEADDR, &base_address);
  183.   lseek (inferior_fd, base_address + 8, 0);
  184.   read (inferior_fd, &text_base, sizeof (long));
  185.   relocate_apropriate_symbols (text_base - inferior_base_address);
  186.   relocate_breakpoints (text_base - inferior_base_address);
  187.   inferior_base_address = text_base;
  188. }
  189.  
  190. void
  191. unrelocate_addresses ()
  192. {
  193.   relocate_apropriate_symbols (-inferior_base_address);
  194.   relocate_breakpoints (-inferior_base_address);
  195.   inferior_base_address = 0;
  196. }
  197.  
  198. void
  199. create_inferior_hook (pid)
  200.      int pid;
  201. {
  202.   char procname[16];
  203.  
  204.   sprintf (procname, "u:/proc/.%03d", pid);
  205.   inferior_fd = open (procname, O_EXCL | O_RDWR);
  206.   if (inferior_fd < 0)
  207.     perror_with_name ("open");
  208. #ifdef NO_SHELL
  209.   relocate_addresses ();
  210. #endif
  211. }
  212.  
  213. kill_inferior ()
  214. {
  215.   short sig = SIGKILL;
  216.   short zero = 0;
  217.   if (inferior_pid == 0)
  218.     return;
  219.   ioctl (inferior_fd, PTRACEGO, &sig);
  220.   close (inferior_fd);
  221.   inferior_fd = 0;
  222.   wait (0);
  223.   inferior_died ();
  224. }
  225.  
  226. /* This is used when GDB is exiting.  It gives less chance of error.*/
  227.  
  228. kill_inferior_fast ()
  229. {
  230.   short sig = SIGKILL;
  231.   short zero = 0;
  232.   if (inferior_pid == 0)
  233.     return;
  234.   ioctl (inferior_fd, PTRACEGO, &sig);
  235.   close (inferior_fd);
  236.   inferior_fd = 0;
  237.   wait (0);
  238. }
  239.  
  240. /* Resume execution of the inferior process.
  241.    If STEP is nonzero, single-step it.
  242.    If SIGNAL is nonzero, give it that signal.  */
  243.  
  244. void
  245. resume (step, signal)
  246.      int step;
  247.      int signal;
  248. {
  249.   short sig = signal;
  250.   if (ioctl (inferior_fd, step ? PTRACESTEP : PTRACEGO, &sig) < 0)
  251.     perror_with_name ("resume");
  252. }
  253.  
  254. /* Replacement for the wait library routine.  GDB assumes that traced
  255.    processes are always reported by wait, but MiNT treats them like
  256.    stopped processes, and reports them only if WUNTRACED is set.  */
  257.  
  258. int
  259. wait (status)
  260.      int *status;
  261. {
  262.   long r;
  263.   int exit_status, sig_term, pid;
  264.  
  265.   r = Pwait3 (2, 0L);
  266.  
  267.   if (r < 0)
  268.     {
  269.       errno = (int) -r;
  270.       return -1;
  271.     }
  272.   pid = (int) ((r & 0xffff0000L) >> 16);
  273.   if (status)
  274.     {
  275.       exit_status = r & 0x000000ffL;
  276.       sig_term = (r & 0x00007f00L) >> 8;
  277.       if (sig_term >= NSIG)
  278.     sig_term = 0;
  279.       if (sig_term && exit_status != 0 && exit_status != 0177)
  280.     sig_term = 0;
  281.       if (exit_status == 0177 && sig_term)
  282.     *status = (sig_term << 8) | 0177;
  283.       else
  284.     *status = (exit_status << 8) | sig_term;
  285.     }
  286.   return pid;
  287. }
  288.  
  289. #ifdef ATTACH_DETACH
  290.  
  291. /* Start debugging the process whose number is PID.  */
  292.  
  293. attach (pid)
  294.      int pid;
  295. {
  296.   char procname[16];
  297.   int fd;
  298.   short flag = P_ENABLE;
  299.   sprintf (procname, "u:/proc/.%03d", pid);
  300.   fd = open (procname, O_EXCL | O_RDWR);
  301.   if (fd < 0)
  302.     perror_with_name ("attach");
  303.   if (ioctl (fd, PTRACESFLAGS, &flag) < 0)
  304.     {
  305.       close (fd);
  306.       perror_with_name ("PTRACESFLAGS");
  307.     }
  308.   kill (pid, SIGTRAP);
  309.   attach_flag = 1;
  310.   setpgid (pid, pid);
  311.   inferior_fd = fd;
  312.   relocate_addresses ();
  313.   return pid;
  314. }
  315.  
  316. /* Stop debugging the process whose number is PID
  317.    and continue it with signal number SIGNAL.
  318.    SIGNAL = 0 means just continue it.  */
  319.  
  320. void
  321. detach (signal)
  322.      int signal;
  323. {
  324.   short sig = signal;
  325.   short zero = 0;
  326.   ioctl (inferior_fd, PTRACEGO, &sig);
  327.   ioctl (inferior_fd, PTRACESFLAGS, &zero);
  328.   close (inferior_fd);
  329.   inferior_fd = 0;
  330.   attach_flag = 0;
  331.   unrelocate_addresses ();
  332. }
  333. #endif /* ATTACH_DETACH */
  334.  
  335. struct context
  336. {
  337.   long    regs[15];        /* registers d0-d7, a0-a6 */
  338.   long    usp;            /* user stack pointer (a7) */
  339.   short    sr;            /* status register */
  340.   long    pc;            /* program counter */
  341.   long    ssp;            /* supervisor stack pointer */
  342.   long    term_vec;        /* GEMDOS terminate vector (0x102) */
  343.   char    fstate[216];        /* FPU internal state */
  344.   long    fregs[3*8];        /* registers fp0-fp7 */
  345.   long    fctrl[3];        /* FPCR/FPSR/FPIAR */
  346. };
  347.  
  348. void
  349. fetch_inferior_registers ()
  350. {
  351.   long place, ctxtsize;
  352.   struct context inferior_context;
  353.   extern char registers[];
  354.  
  355.   ioctl (inferior_fd, PPROCADDR, &place);
  356.   /* Position on the first context.  */
  357.   ioctl (inferior_fd, PCTXTSIZE, &ctxtsize);
  358.   lseek (inferior_fd, place - 2 * ctxtsize, 0);
  359.   read (inferior_fd, &inferior_context, sizeof (struct context));
  360.  
  361.   bcopy (inferior_context.regs, registers, 16 * 4);
  362. #ifdef FP0_REGNUM
  363.   bcopy (inferior_context.fregs, ®isters[REGISTER_BYTE (FP0_REGNUM)],
  364.      sizeof inferior_context.fregs);
  365. #endif 
  366.   *(long *) ®isters[REGISTER_BYTE (PS_REGNUM)] = inferior_context.sr;
  367.   *(long *) ®isters[REGISTER_BYTE (PC_REGNUM)] = inferior_context.pc;
  368. #ifdef FP0_REGNUM
  369.   bcopy (inferior_context.fctrl, ®isters[REGISTER_BYTE (FPC_REGNUM)],
  370.      sizeof inferior_context.fctrl);
  371. #endif 
  372. }
  373.  
  374. /* Store our register values back into the inferior.
  375.    If REGNO is -1, do this for all registers.
  376.    Otherwise, REGNO specifies which register (so we can save time).  */
  377.  
  378. store_inferior_registers (regno)
  379.      int regno;
  380. {
  381.   long place, ctxtsize;
  382.   struct context inferior_context;
  383.   extern char registers[];
  384.  
  385.   ioctl (inferior_fd, PPROCADDR, &place);
  386.   /* Position on the first context.  */
  387.   ioctl (inferior_fd, PCTXTSIZE, &ctxtsize);
  388.   place -= 2 * ctxtsize;
  389.   lseek (inferior_fd, place, 0);
  390.   read (inferior_fd, &inferior_context, sizeof (struct context));
  391.  
  392.   bcopy (registers, inferior_context.regs, 16 * 4);
  393. #ifdef FP0_REGNUM
  394.   bcopy (®isters[REGISTER_BYTE (FP0_REGNUM)], inferior_context.fregs,
  395.      sizeof inferior_context.fregs);
  396. #endif
  397.   inferior_context.sr &= ~0xff;
  398.   inferior_context.sr |= *(long *) ®isters[REGISTER_BYTE (PS_REGNUM)] & 0xff;
  399.   inferior_context.pc = *(long *) ®isters[REGISTER_BYTE (PC_REGNUM)];
  400.  
  401. #ifdef FP0_REGNUM
  402.   bcopy (®isters[REGISTER_BYTE (FPC_REGNUM)],
  403.      inferior_context.fctrl, sizeof inferior_context.fctrl);
  404. #endif
  405.   lseek (inferior_fd, place, 0);
  406.   write (inferior_fd, &inferior_context, sizeof (struct context));
  407. }
  408.  
  409. /* the MiNT proc file system doesn't catch bus errors (yet), we must do it
  410.    ourselves */
  411.  
  412. jmp_buf bus_error;
  413.  
  414. void
  415. sigbus (signo)
  416.      int signo;
  417. {
  418.   errno = EACCESS;
  419.   longjmp (bus_error, 1);
  420. }
  421.  
  422. /* Copy LEN bytes from inferior's memory starting at MEMADDR
  423.    to debugger memory starting at MYADDR. 
  424.    On failure (cannot read from inferior, usually because address is out
  425.    of bounds) returns the value of errno. */
  426.  
  427. int
  428. read_inferior_memory (memaddr, myaddr, len)
  429.      CORE_ADDR memaddr;
  430.      char *myaddr;
  431.      int len;
  432. {
  433.   void (*old_sigbus) ();
  434.  
  435.   errno = 0;
  436.   lseek (inferior_fd, memaddr, 0);
  437.   old_sigbus = signal (SIGBUS, sigbus);
  438.   if (setjmp (bus_error) == 0)
  439.     read (inferior_fd, myaddr, len);
  440.   signal (SIGBUS, old_sigbus);
  441.   if (errno)
  442.     bzero (myaddr, len);
  443.   return errno;
  444. }
  445.  
  446. /* Copy LEN bytes of data from debugger memory at MYADDR
  447.    to inferior's memory at MEMADDR.
  448.    On failure (cannot write the inferior)
  449.    returns the value of errno.  */
  450.  
  451. /* WARNING: MiNT writes in supervisor mode, no access restrictions
  452.    (except nonexistent memory, of course) */
  453. /* News Flash: Now since MiNT has memory protection, the situation
  454.    is far better.  */
  455.  
  456. int
  457. write_inferior_memory (memaddr, myaddr, len)
  458.      CORE_ADDR memaddr;
  459.      char *myaddr;
  460.      int len;
  461. {
  462.   void (*old_sigbus) ();
  463.  
  464.   errno = 0;
  465.   lseek (inferior_fd, memaddr, 0);
  466.   old_sigbus = signal (SIGBUS, sigbus);
  467.   if (setjmp (bus_error) == 0)
  468.     write (inferior_fd, myaddr, len);
  469.   signal (SIGBUS, old_sigbus);
  470.   return errno;
  471. }
  472.  
  473.  
  474. /* Machine-dependent code which would otherwise be in core.c */
  475. /* Work with core dump and executable files, for GDB. */
  476.  
  477. #define AOUTHDR struct aexec
  478.  
  479. extern char *sys_siglist[];
  480.  
  481. /* Hook for `exec_file_command' command to call.  */
  482.  
  483. extern void (*exec_file_display_hook) ();
  484.    
  485. /* File names of core file and executable file.  */
  486.  
  487. extern char *corefile;
  488. extern char *execfile;
  489.  
  490. /* Descriptors on which core file and executable file are open.
  491.    Note that the execchan is closed when an inferior is created
  492.    and reopened if the inferior dies or is killed.  */
  493.  
  494. extern int corechan;
  495. extern int execchan;
  496.  
  497. /* Last modification time of executable file.
  498.    Also used in source.c to compare against mtime of a source file.  */
  499.  
  500. extern int exec_mtime;
  501.  
  502. /* Virtual addresses of bounds of the two areas of memory in the core file.  */
  503.  
  504. extern CORE_ADDR data_start;
  505. extern CORE_ADDR data_end;
  506. extern CORE_ADDR stack_start;
  507. extern CORE_ADDR stack_end;
  508.  
  509. /* Virtual addresses of bounds of two areas of memory in the exec file.
  510.    Note that the data area in the exec file is used only when there is no core file.  */
  511.  
  512. extern CORE_ADDR text_start;
  513. extern CORE_ADDR text_end;
  514.  
  515. extern CORE_ADDR exec_data_start;
  516. extern CORE_ADDR exec_data_end;
  517.  
  518. /* Address in executable file of start of text area data.  */
  519.  
  520. extern int text_offset;
  521.  
  522. /* Address in executable file of start of data area data.  */
  523.  
  524. extern int exec_data_offset;
  525.  
  526. /* Address in core file of start of data area data.  */
  527.  
  528. extern int data_offset;
  529.  
  530. /* Address in core file of start of stack area data.  */
  531.  
  532. extern int stack_offset;
  533.  
  534. /* a.out header saved in core file.  */
  535.   
  536. extern AOUTHDR core_aouthdr;
  537.  
  538. /* a.out header of exec file.  */
  539.  
  540. extern AOUTHDR exec_aouthdr;
  541.  
  542. extern void validate_files ();
  543.  
  544. core_file_command (filename, from_tty)
  545.      char *filename;
  546.      int from_tty;
  547. {
  548.   error ("No core files yet");
  549. }
  550.  
  551. exec_file_command (filename, from_tty)
  552.      char *filename;
  553.      int from_tty;
  554. {
  555.   int val;
  556.  
  557.   /* Eliminate all traces of old exec file.
  558.      Mark text segment as empty.  */
  559.  
  560.   if (execfile)
  561.     free (execfile);
  562.   execfile = 0;
  563.   data_start = 0;
  564.   data_end -= exec_data_start;
  565.   text_start = 0;
  566.   text_end = 0;
  567.   exec_data_start = 0;
  568.   exec_data_end = 0;
  569.   if (execchan >= 0)
  570.     close (execchan);
  571.   execchan = -1;
  572.  
  573.   /* Now open and digest the file the user requested, if any.  */
  574.  
  575.   if (filename)
  576.     {
  577.       filename = tilde_expand (filename);
  578.       make_cleanup (free, filename);
  579.       
  580.       execchan = openp (getenv ("PATH"), 1, filename, O_RDONLY, 0,
  581.             &execfile);
  582.       if (execchan < 0)
  583.     perror_with_name (filename);
  584.  
  585.       {
  586.     struct stat st_exec;
  587.     val = myread (execchan, &exec_aouthdr, sizeof (AOUTHDR));
  588.  
  589.     if (val < 0)
  590.       perror_with_name (filename);
  591.  
  592.     text_start = 0;
  593.         exec_data_start = exec_aouthdr.a_text;
  594.     text_offset = A_TXTOFF (exec_aouthdr);
  595.     exec_data_offset = A_TXTOFF (exec_aouthdr) + exec_aouthdr.a_text;
  596.  
  597.     text_end = text_start + exec_aouthdr.a_text;
  598.         exec_data_end = exec_data_start + exec_aouthdr.a_data;
  599.     data_start = exec_data_start;
  600.     data_end += exec_data_start;
  601.  
  602.     fstat (execchan, &st_exec);
  603.     exec_mtime = st_exec.st_mtime;
  604.       }
  605.  
  606.       validate_files ();
  607.     }
  608.   else if (from_tty)
  609.     printf ("No exec file now.\n");
  610.  
  611.   /* Tell display code (if any) about the changed file name.  */
  612.   if (exec_file_display_hook)
  613.     (*exec_file_display_hook) (filename);
  614. }
  615.  
  616. /* Check for a real FPU by looking at the _FPU cookie.  */
  617.  
  618. static int
  619. get_fpu_cookie ()
  620. {
  621.   long *cookie = *(long **) 0x5a0L;
  622.  
  623.   if (cookie == 0L)
  624.     return 0L;
  625.   for (; *cookie; cookie += 2)
  626.     if (*cookie == 0x5f465055) /* _FPU */
  627.       return cookie[1];
  628.   return 0L;
  629. }
  630.  
  631. int
  632. check_fpu ()
  633. {
  634.   long fpu = Supexec (get_fpu_cookie);
  635.  
  636.   /* Return true if there is a real fpu or a line F emulator.  */
  637.   return ((fpu >> 16) > 1 || fpu & 0xffff);
  638. }
  639.  
  640. /* walk through the symbol table and relocate apropriate */
  641. /* from the gdb-2.6 port, modified to support partial symtabs */
  642.  
  643. static void
  644. relocate_apropriate_block_symbols (block, offset)
  645.      struct block *block;
  646.      long offset;
  647. {
  648.   struct symbol *sym;
  649.   int nsyms, symnum;
  650.  
  651.   BLOCK_START (block) += offset;
  652.   BLOCK_END (block) += offset;
  653.   nsyms = BLOCK_NSYMS (block);
  654.   for (symnum = 0; symnum < nsyms; symnum++)
  655.     {
  656.       sym = BLOCK_SYM (block, symnum);
  657.       if (sym->namespace == VAR_NAMESPACE
  658.       && (sym->class == LOC_STATIC || sym->class == LOC_LABEL))
  659.     sym->value.value += offset;
  660.     }
  661. }
  662.  
  663. static void
  664. relocate_apropriate_symbols (offset)
  665.      long offset;
  666. {
  667.   struct symtab *s;
  668.   struct partial_symtab *ps;
  669.   struct symbol *sym;
  670.   struct blockvector *bv, *last_bv = 0;
  671.   struct block *block;
  672.   struct linetable *l;
  673.   int nsyms, symnum, i;
  674.  
  675.   for (s = symtab_list; s; s = s->next)
  676.     {
  677.       l = LINETABLE (s);
  678.       for (i = 0; i < l->nitems; i++)
  679.     l->item[i].pc += offset;
  680.       bv = BLOCKVECTOR (s);
  681.       if (bv == last_bv)
  682.     /* already relocated */
  683.     continue;
  684.       last_bv = bv;
  685.       for (i = 0; i < BLOCKVECTOR_NBLOCKS (bv); i++)
  686.     {
  687.       block = BLOCKVECTOR_BLOCK (bv, i);
  688.       relocate_apropriate_block_symbols (block, offset);
  689.     }
  690.     }
  691.   for (ps = partial_symtab_list; ps; ps = ps->next)
  692.     {
  693.       ps->textlow += offset;
  694.       ps->texthigh += offset;
  695.     }
  696.   /* relocate the misc functions */
  697.   for (i = 0; i < misc_function_count; i++)
  698.     misc_function_vector[i].address += offset;
  699. }
  700.  
  701. #ifndef __REGEX_LIBRARY_H__
  702. static char *regcomp_error = 0;
  703. static regexp *compiled_regex = 0;
  704.  
  705. /* Remember the last error message from regcomp */
  706. void
  707. regerror(message)
  708.      char *message;
  709. {
  710.   regcomp_error = message;
  711. }
  712.  
  713. char *
  714. re_comp (regex)
  715.      char *regex;
  716. {
  717.   regcomp_error = NULL;
  718.   if (compiled_regex)
  719.     free (compiled_regex);
  720.   if (compiled_regex = regcomp (regex))
  721.     return NULL;
  722.   else
  723.     return regcomp_error;
  724. }
  725.  
  726. int
  727. re_exec (buffer)
  728.      char *buffer;
  729. {
  730.   if (compiled_regex)
  731.     return regexec (compiled_regex, buffer, 1);
  732.   else
  733.     return -1;
  734. }
  735. #endif /* __REGEX_LIBRARY_H__ */
  736.