#include <errno.h>
#include <sys/wait.h>
- #include <pcre.h>
+ #define PCRE2_CODE_UNIT_WIDTH 8
+ #include <pcre2.h>
#include <miniexpect.h>
mexp_h *h;
- h = mexp_spawnl ("ssh", "ssh", "host");
- switch (mexp_expect (h, regexps, ovector, ovecsize)) {
+ h = mexp_spawnl ("ssh", "ssh", "host", NULL);
+ switch (mexp_expect (h, regexps, match_data)) {
...
}
mexp_close (h);
- cc prog.c -o prog -lminiexpect -lpcre
+ cc prog.c -o prog -lminiexpect -lpcre2-8
=head1 DESCRIPTION
standards.
Miniexpect is a standalone library, except for a single dependency: it
-requires the PCRE (Perl Compatible Regular Expressions) library from
-L<http://www.pcre.org/>. The PCRE dependency is fundamental because
+requires the PCRE2 (Perl Compatible Regular Expressions) library from
+L<http://www.pcre.org/>. The PCRE2 dependency is fundamental because
we want to offer the most powerful regular expression syntax to match
-on, but more importantly because PCRE has a convenient way to detect
+on, but more importantly because PCRE2 has a convenient way to detect
partial matches which made this library very simple to implement.
This manual page documents the API. Examples of how to use the API
=head1 SPAWNING THE SUBPROCESS
-There are two calls for creating a subprocess:
+There are four calls for creating a subprocess:
B<mexp_h *mexp_spawnl (const char *file, const char *arg, ...);>
This creates a subprocess running the external program C<file> (the
current C<$PATH> is searched unless you give an absolute path).
-C<arg, ...> are the arguments to the program. Usually the first
-argument should be the name of the program.
+C<arg, ...> are the arguments to the program. You should terminate
+the list of arguments with C<NULL>. Usually the first argument should
+be the name of the program.
The return value is a handle (see next section).
For example, to run an ssh subprocess you could do:
- h = mexp_spawnl ("ssh", "ssh", "-l", "root", "host");
+ h = mexp_spawnl ("ssh", "ssh", "-l", "root", "host", NULL);
or to run a particular ssh binary:
- h = mexp_spawnl ("/usr/local/bin/ssh", "ssh", "-l", "root", "host");
+ h = mexp_spawnl ("/usr/local/bin/ssh", "ssh", "-l", "root", "host", NULL);
An alternative to C<mexp_spawnl> is:
This is the same as C<mexp_spawnl> except that you pass the arguments
in a NULL-terminated array.
+There are also two versions of the above calls which take flags:
+
+B<mexp_h *mexp_spawnlf (unsigned flags, const char *file, const char *arg, ...);>
+
+B<mexp_h *mexp_spawnvf (unsigned flags, const char *file, char **argv);>
+
+The flags may contain the following values, logically ORed together:
+
+=over 4
+
+=item B<MEXP_SPAWN_KEEP_SIGNALS>
+
+Do not reset signal handlers to C<SIG_DFL> in the subprocess.
+
+=item B<MEXP_SPAWN_KEEP_FDS>
+
+Do not close file descriptors E<ge> 3 in the subprocess.
+
+=item B<MEXP_SPAWN_COOKED_MODE> or B<MEXP_SPAWN_RAW_MODE>
+
+Configure the pty in cooked mode or raw mode. Raw mode is the
+default.
+
+=back
+
=head1 HANDLES
-After spawning a subprocess, you get back a handle. There are various
-fields in this handle which you can read or write:
+After spawning a subprocess, you get back a handle which is a pointer
+to a struct:
+
+ struct mexp_h;
+ typedef struct mexp_h mexp_h;
+
+Various methods can be used on the handle:
+
+B<int mexp_get_fd (mexp_h *h);>
+
+Return the file descriptor of the pty of the subprocess. You can read
+and write to this if you want, although convenience functions are also
+provided (see below).
+
+B<pid_t mexp_get_pid (mexp_h *h);>
+
+Return the process ID of the subprocess. You can send it signals if
+you want.
+
+B<int mexp_get_timeout_ms (mexp_h *h);>
+
+B<void mexp_set_timeout_ms (mexp_h *h, int millisecs);>
+
+B<void mexp_set_timeout (mexp_h *h, int secs);>
+
+Get or set the timeout used by C<mexp_expect> [see below]. The
+resolution is milliseconds (1/1000th of a second). Set this before
+calling C<mexp_expect>. Passing -1 to either of the C<set_> methods
+means no timeout. The default setting is 60000 milliseconds (60
+seconds).
- struct mexp_h {
- int fd;
- pid_t pid;
+B<size_t mexp_get_read_size (mexp *h);>
-C<fd> is the pty of the subprocess. You can read and write to this if
-you want, although convenience functions are also provided (see
-below). C<pid> is the process ID of the subprocess. You can send it
-signals if you want.
+B<void mexp_set_read_size (mexp *h, size_t read_size);>
- int timeout;
+Get or set the natural size (in bytes) for reads from the subprocess.
+The default is 1024. Most callers will not need to change this.
-C<timeout> is the timeout in milliseconds (1/1000th of a second) used
-by C<mexp_expect> (see below). You can set this before calling
-C<mexp_expect> if you want. Setting it to -1 means no timeout. The
-default setting is 60000 (60 seconds).
+B<int mexp_get_pcre_error (mexp *h);>
- char *buffer;
- size_t len;
- size_t alloc;
+When C<mexp_expect> [see below] calls the PCRE function
+L<pcre2_match(3)>, it stashes the return value in the C<pcre_error>
+field in the handle, and that field is returned by this method.
+
+If C<mexp_expect> returns C<MEXP_PCRE_ERROR>, then the actual PCRE
+error code returned by L<pcre2_match(3)> is available by calling this
+method. For a list of PCRE error codes, see L<pcre2api(3)>.
+
+B<void mexp_set_debug_file (mexp *h, FILE *fp);>
+
+B<FILE *mexp_get_debug_file (mexp *h);>
+
+Set or get the debug file of the handle. To enable debugging, pass a
+non-C<NULL> file handle, eg. C<stderr>. To disable debugging, pass
+C<NULL>. Debugging messages are printed on the file handle.
+
+Note that all output and input gets printed, including passwords. To
+prevent passwords from being printed, modify your code to call
+C<mexp_printf_password> instead of C<mexp_printf>.
+
+The following fields in the handle do not have methods, but can be
+accessed directly instead:
+
+ char *buffer;
+ size_t len;
+ size_t alloc;
If C<mexp_expect> returns a match then these variables contain the
read buffer. Note this buffer does not contain the full input from
regular expression (and maybe some more). C<buffer> is the read
buffer and C<len> is the number of bytes of data in the buffer.
- ssize_t next_match;
+ ssize_t next_match;
If C<mexp_expect> returns a match, then C<next_match> points to the
first byte in the buffer I<after> the fully matched expression. (It
callers can ignore this field, and C<mexp_expect> will just do the
right thing when called repeatedly.
- size_t read_size;
-
-Callers may set this to the natural size (in bytes) for reads from the
-subprocess. The default is 1024. Most callers will not need to
-change this.
-
- int pcre_error;
-
-If C<mexp_expect> returns C<MEXP_PCRE_ERROR>, then the actual PCRE
-error code returned by L<pcre_exec(3)> is available here. For a list
-of PCRE error codes, see L<pcreapi(3)>.
-
- void *user1;
- void *user2;
- void *user3;
+ void *user1;
+ void *user2;
+ void *user3;
Opaque pointers for use by the caller. The library will not touch
these.
- };
-
- typedef struct mexp_h mexp_h;
-
=head1 CLOSING THE HANDLE
To close the handle and clean up the subprocess, call:
=back
+This is how code should check for and print errors from C<mexp_close>:
+
+ status = mexp_close (h);
+ if (status == -1) {
+ perror ("mexp_close");
+ return -1;
+ }
+ if (WIFSIGNALED (status) && WTERMSIG (status) == SIGHUP)
+ goto ignore; /* not an error */
+ if (!WIFEXITED (status) || WEXITSTATUS (status) != 0)
+ /* You could use the W* macros to print a better error message. */
+ fprintf (stderr, "error: subprocess failed, status = %d", status);
+ return -1;
+ }
+ ignore:
+ /* no error case */
+
=head1 EXPECT FUNCTION
Miniexpect contains a powerful regular expression matching function
-based on L<pcre(3)>:
+based on L<pcre2(3)>:
B<int mexp_expect (mexp_h *h, const mexp_regexp *regexps,
-int *ovector, int ovecsize);>
+pcre2_match_data *match_data);>
The output of the subprocess is matched against the list of PCRE
regular expressions in C<regexps>. C<regexps> is a list of regular
struct mexp_regexp {
int r;
- const pcre *re;
- const pcre_extra *extra;
+ const pcre2_code *re;
int options;
};
typedef struct mexp_regexp mexp_regexp;
=item C<MEXP_PCRE_ERROR>
-There was a C<pcre_exec> error. C<h-E<gt>pcre_error> is set to the
+There was a C<pcre2_match> error. C<h-E<gt>pcre_error> is set to the
error code. See L<pcreapi(3)> for a list of the C<PCRE_*> error codes
and what they mean.
=item *
-C<regexps[].re>, C<regexps[].extra>, C<regexps[].options>, C<ovector>
-and C<ovecsize> are passed through to the L<pcre_exec(3)> function.
+C<regexps[].re>, C<regexps[].options> and C<match_data> are passed
+through to the L<pcre2_match(3)> function.
=item *
compiler, you can just use a local variable instead.
mexp_h *h;
- char *errptr;
+ int errcode;
int offset;
- pcre *password_re, *prompt_re;
- const int ovecsize = 12;
- int ovector[ovecsize];
+ pcre2_code *password_re, *prompt_re;
+ pcre2_match_data *match_data = pcre2_match_data_create (4, NULL);
- password_re = pcre_compile ("assword", 0, &errptr, &offset, NULL);
- prompt_re = pcre_compile ("[$#] ", 0, &errptr, &offset, NULL);
+ password_re = pcre2_compile ("assword", PCRE2_ZERO_TERMINATED,
+ 0, &errcode, &offset, NULL);
+ prompt_re = pcre2_compile ("[$#] ", PCRE2_ZERO_TERMINATED,
+ 0, &errcode, &offset, NULL);
switch (mexp_expect (h,
(mexp_regexp[]) {
{ 100, .re = password_re },
{ 101, .re = prompt_re },
{ 0 },
- }, ovector, ovecsize)) {
+ }, match_data)) {
case 100:
/* here you would send a password */
break;
B<int mexp_printf (mexp_h *h, const char *fs, ...);>
+B<int mexp_printf_password (mexp_h *h, const char *fs, ...);>
+
This returns the number of bytes, if the whole message was written OK.
If there was an error, -1 is returned and the error is available in
C<errno>.
mexp_printf (h, "exit\n");
+=item *
+
+C<mexp_printf_password> works identically to C<mexp_printf> except
+that the output is I<not> sent to the debugging file if debugging is
+enabled. As the name suggests, use this for passwords so that they
+don't appear in debugging output.
+
=back
+B<int mexp_send_interrupt (mexp_h *h);>
+
+Send the interrupt character (C<^C>, Ctrl-C, C<\003>). This is like
+pressing C<^C> - the subprocess (or remote process, if using C<ssh>)
+is gracefully killed.
+
+Note this only works if the pty is in cooked mode
+(ie. C<MEXP_SPAWN_COOKED_MODE> was passed to C<mexp_spawnlf> or
+C<mexp_spawnvf>). In raw mode, all characters are passed through
+without any special interpretation.
+
=head1 SOURCE
Source is available from:
=head1 SEE ALSO
-L<pcre(3)>,
-L<pcre_exec(3)>,
-L<pcreapi(3)>,
+L<pcre2(3)>,
+L<pcre2_match(3)>,
+L<pcre2api(3)>,
L<waitpid(2)>,
L<system(3)>.
=head1 COPYRIGHT
-Copyright (C) 2014 Red Hat Inc.
+Copyright (C) 2014-2022 Red Hat Inc.