[mercury-users] mercury-extras/posix comments, !Question, musing.

Julian Fondren ayrnieu at gmail.com
Mon Feb 12 00:51:19 AEDT 2007


I just finished writing a echo-server (as a test for something
else -- I don't try to make it a *useful* echo-server) using
the mercury-extras posix module.

The posix comments:

1. I had to comment out EBADMSG in posix.m to get it to build,
   and I see other comments there for presumably other
   discovered-nonuniversables.   The attached gen_errno.m will
   print out a C program which itself will print out a list of
   (a subset of posix.m's) values of errno that exist.  You
   might modify gen_errno.m to generate an errno_table.m which
   posix.m can include as a fact table (17.1 of Langref).

2. posix.read.m supports (text::di, text::uo), but posix.write.m
   doesn't!  The attached echoserv.m has its own write, copied
   from posix.write.m, which allows that.

The state variable question:

1. In the attached gen_errno.m, I have this code:

  main -->
          % ...
          { errno(L) },
          write_errnos(L),
          % ...

  :- pred write_errnos(list(string)::in, io::di, io::uo) is det.
  write_errnos(!.L, !IO) :-
          list.map(format_errno, !L),
          dump_errnos(!.L, !IO).

I wanted to have this instead:

  :- pred write_errnos(io::di, io::uo) is det.
  write_errnos(!IO) :-
          errno(!.L),
          list.map(format_errno, !L),
          dump_errnos(!.L, !IO).

but I get several errors complaining about the visibility of
!.L and !:L , as in:

  gen_errno.m:022: Error: state variable !.L is not visible in this context.

So, apparently the head of a predicate is more special as a
source of state variables than a random predicate -- but why?


(and also:)

FWIW, I really enjoy code like this compressed example, from
echoserv.m :

  :- pred echo(fd::in, io::di, io::uo) is det.
  echo(Sock, !IO) :-
          text.create(4096, 0, Buf),
          echo(Sock, Buf, !IO).

  :- pred echo(fd::in, text.text::di, io::di, io::uo) is det.
  echo(Sock, !.Buf) -->
          posix.read.read(Sock, 4096, Res, !Buf),
          write(Sock, ReadCount, !Buf, Res1),
          echo(Sock, !.Buf).

Although that's also interesting in that I thread 5 values,
counting the socket, through these functions.  ... I think
I can stuff all of that into a single !State, with help from
c_code, but I'd need to separate out the IO state whenever
I left dealing-with-the-client world.

Cheers,
Julian
-------------- next part --------------
:- module echoserv.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module posix__socket, posix, posix__read, text, require.
:- import_module int, string, list.

main(!IO) :-
	server(4012, !IO).

:- pred or_die(string::in, posix.result::in, io::di, io::uo) is det.
or_die(_, ok, !IO).
or_die(S, error(X), !IO) :- print(X, !IO), io.nl(!IO), error(S).

:- pred or_die(string::in, posix.result(T)::in, T::out, io::di, io::uo) is det.
or_die(_, ok(X), X, !IO).
or_die(S, error(X), _, !IO) :- io.print(X, !IO), io.nl(!IO), error(S).

:- pred server(int::in, io::di, io::uo) is det.
server(Port) -->
	socket(inet, stream, protocol(0), Res),
		or_die("socket", Res, Sock),
	bind(Sock, inet(port(Port), to_inet_addr(0,0,0,0)), Res1),
		or_die("bind", Res1),
	listen(Sock, 4, Res2), 
		or_die("listen", Res2),
	accept(Sock, Res3),
		or_die("accept", Res3, Client),
	echo(Client).

:- pred echo(fd::in, io::di, io::uo) is det.
echo(Sock, !IO) :-
	text.create(4096, 0, Buf),
	echo(Sock, Buf, !IO).

:- pred echo(fd::in, text.text::di, io::di, io::uo) is det.
echo(Sock, !.Buf) -->
	posix.read.read(Sock, 4096, Res, !Buf),
		or_die("read", Res, ReadCount),
	write(Sock, ReadCount, !Buf, Res1),
		or_die("write", Res1, WriteCount),
	io.format("echoed %d bytes\n", [i(WriteCount)]),
	(
		{ WriteCount = 0 }
	->
		io.write_string("Exiting.\n")
	;
		echo(Sock, !.Buf)
	).

:- func to_inet_addr(int, int, int, int) = inet_addr.
to_inet_addr(A,B,C,D) = inet_addr(D \/ (C << 8) \/ (B << 16) \/ (A << 24)).

%------------------------------------------------------------------------------%
% Copied from posix.write.m , changed to handle the buffer as a state variable
%

%-- expanded text_header.h in this
:- pragma c_header_code("
#include <unistd.h>
#ifndef ME_TEXT_HEADER_H
#define ME_TEXT_HEADER_H
typedef struct {
	unsigned len;
	char *data;
} ME_Text;
#endif
").

:- pred write(fd, int, text, text, posix__result(int), io__state, io__state).
:- mode write(in, in, di, uo, out, di, uo) is det.
write(Fd, ToWrite, !Text, Result, !IO) :-
        write0(Fd, ToWrite, !Text, Res, !IO),
        ( Res < 0 ->
                errno(Err, !IO),
                Result = error(Err)
        ;
                 Result = ok(Res)
        ).

:- pred write0(fd, int, text, text, int, io__state, io__state).
:- mode write0(in, in, di, uo, out, di, uo) is det.
:- pragma c_code(write0(Fd::in, ToWrite::in, Text0::di, Text::uo, Res::out,
                IO0::di, IO::uo), [will_not_call_mercury, thread_safe], "{
        ME_Text *txtptr;

        txtptr = (ME_Text *) Text;

        Res = write(Fd, txtptr->data, ToWrite);

        IO = IO0; Text = Text0;
}").

-------------- next part --------------
:- module gen_errno.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module string, list.

% I release this module into the public domain
% -- the author, Julian Fondren <ayrnieu at gmail.com>
% -- er, except for what isn't mine to release.
% -- that posix.write.m stuff at the bottom surely
% -- belongs to someone.

main -->
	io.write_string(c_header),
	io.write_string(c_main_pre),
	{ errno(L) },
	write_errnos(L),
	io.write_string(c_main_post).

:- pred write_errnos(list(string)::in, io::di, io::uo) is det.
write_errnos(!.L, !IO) :-
	list.map(format_errno, !L),
	dump_errnos(!.L, !IO).

:- pred format_errno(string::in, string::out) is det.
format_errno(E, S) :-
	string.format(c_errno_check, [s(E), s(E)], S).

:- pred dump_errnos(list(string)::in, io::di, io::uo) is det.
dump_errnos([], !IO).
dump_errnos([H | T]) -->
	io.write_string(H),
	dump_errnos(T).

:- func c_header = string.
:- func c_main_pre = string.
:- func c_errno_check = string.
:- func c_main_post = string.
c_header = "
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
".

c_main_pre = "
int main (void) {
".

c_errno_check = "
#ifdef %s
  printf(""%s\\n"");
#endif
".

c_main_post = "
  return 0;
}
".

%-- Copied from posix.m
:- pred errno(list(string)::out) is det.
errno([
	"E2BIG",
	"EACCES",
	"EAGAIN",
	"EBADF",
	"EBADMSG",
	"EBUSY",
	"ECANCELED",
	"ECHILD",
	"EDEADLK",
	"EDOM",
	"EEXIST",
	"EFAULT",
	"EFBIG",
	"EINPROGRESS",
	"EINTR",
	"EINVAL",
	"EIO",
	"EISDIR",
	"EMFILE",
	"EMLINK",
	"EMSGSIZE",
	"ENAMETOOLONG",
	"ENFILE",
	"ENODEV",
	"ENOENT",
	"ENOEXEC",
	"ENOLOCK",
	"ENOMEM",
	"ENOSPC",
	"ENOSYS",
	"ENOTDIR",
	"ENOTEMPTY",
	"ENOTSUP",
	"ENOTTY",
	"ENXIO",
	"EPERM",
	"EPIPE",
	"ERANGE",
	"EROFS",
	"ESPIPE",
	"ESRCH",
	"ETIMEDOUT",
	"EXDEV"]).


More information about the users mailing list