//
// An introductory example to BSD Unix socket programming in ATS
//
// The following code implements a server socket that responds to
// each request by sending out a string representation of the current
// time, and it also prints out the IP address of the client. This is
// an iterative version (in contrast to a concurrent version). 

// Author: Hongwei Xi (hwxi AT cs DOT bu DOT edu)
// Time: November, 2008

(* ****** ****** *)

staload "libc/SATS/stdio.sats"
staload "libc/SATS/time.sats"
staload "libc/sys/SATS/socket.sats"
staload "libc/netinet/SATS/in.sats"
staload "libc/arpa/SATS/inet.sats"

(* ****** ****** *)

#define LISTENQ 1
#define TIME_SERVER_PORT 13000 // default value

extern fun show_client (cliaddr: &sockaddr_in_struct_t): void
  = "show_client"

implement main (argc, argv) = let
  val [fd_s:int] (pf_sock_s | fd_s) =
    socket_family_type_exn (AF_INET, SOCK_STREAM)
  var servaddr: sockaddr_in_struct_t // uninitialized
  val nport = (if argc > 1 then int_of argv.[1] else TIME_SERVER_PORT): int
  val servport = in_port_nbo_of_int (nport)
  val in4addr_any = in_addr_nbo_of_hbo (INADDR_ANY)
  val () = sockaddr_ipv4_init (servaddr, AF_INET, in4addr_any, servport)
  val () = bind_ipv4_exn (pf_sock_s | fd_s, servaddr)
  val () = listen_exn (pf_sock_s | fd_s, LISTENQ) 
  val () = loop (pf_sock_s | fd_s) where {
    fun loop (pf_sock_s: !socket_v (fd_s, listen) | fd_s: int fd_s): void = let
      var addrlen: socklen_t // uninitialized
      var cliaddr: sockaddr_in_struct_t // uninitialized
      val (pf_sock_c | fd_c) = accept_ipv4_exn (pf_sock_s | fd_s, cliaddr, addrlen)
// (*
      val () = show_client (cliaddr)
// *)
      var ntick = time_get ()
      val time_str = ctime ntick // ctime is non-reentrant
      val time_str = string1_of_string (time_str)
      val time_str_len = string1_length (time_str)
      val _ = socket_write_substring_exn (pf_sock_c | fd_c, time_str, 0, time_str_len)
      val () = socket_close_exn (pf_sock_c | fd_c)
    in
      loop (pf_sock_s | fd_s)
    end // end of [loop]
  } // end of [val]
  val () = socket_close_exn (pf_sock_s | fd_s)
in
  // empty
end // end of [main]

(* ****** ****** *)

%{$

ats_void_type
show_client (ats_ptr_type cliaddr0) {
  struct sockaddr_in *cliaddr = cliaddr0 ;
  char *str = inet_ntoa (cliaddr->sin_addr) ;
  int port = ntohs (cliaddr->sin_port) ;
  printf ("connection from %s at port %d\n", str, port) ;
  return ;
} // end of [show_client]

%} // end of [%{$]

(* ****** ****** *)

(* end of [daytimetcpserver1.dats] *)