/*
 * A simple socket client, borrowed and commented from 
 * pp. 45-46 of "Computer Networks: A Systems Approach", 
 * 2nd Edition, by
 * Larry L. Peterson and Bruce S. Davie.
 *
 * Usage:
 *   client server-name
 *
 * Exit values:
 *   0: Success
 *   1: Incorrect number of parameters
 *   2: Unknown host
 *   3: Trouble creating a socket
 *   4: Trouble connecting
 */

/* Include libraries. */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

/* Define constants. */
#define SERVER_PORT 5432
#define MAX_LINE 256

/* The script that drives our program. */
int
main(int argc, char *argv[])
{
  /* I. Declare Variables */
  /* A struct is a group of fields (record).  This
     particular one has information on the current host. */
  struct hostent *hp;
  /* The address of something. */
  struct sockaddr_in sin;
  /* The name of the host, stored as a string. */
  char *host;
  /* An array of MAX_LINE characters. */
  char buf[MAX_LINE];
  /* The number of the socket we use. */
  int s;
  /* The length of a message that was just read. */
  int len;

  /* II. Check arguments.
   * This program requires one argument. 
   * Note: argc gives the number of arguments, with the program
   * name counting as one of them
   */
  if (argc == 2) {
    host = argv[1];
  }
  else {
    fprintf(stderr, "Usage: client servername\n");
    exit(1);
  }
  
  /* III. Get information on the server. */
  /* Translate host name into IP address. */
  hp = gethostbyname(host);
  /* Hack!  Many C routines, including gethostbyname, return the 
   * null (0) pointer when they fail.  In C, 0 is "false".
   */
  if (hp == NULL) {
    /* fprintf ("file print formatted") takes N arguments
     * the file (stream) to print to, a pattern to print,
     * and things to put in the pattern.  The parts of the
     * pattern are typically %s (for string), %d (for integer),
     * or %f (for float).
     */
    fprintf(stderr, "unknown host: %s\n", host);
    exit(2);
  }

  /* IV. Set up the address. */
  /* (1) Clear the structure. 
   *      & gives the address of something
   *      (char *) is a cast (to a pointer)
   *      sizeof() gives the number of bytes used by a structure
   *      bzero(pointer,len) sets len bytes to 0.
   */
  bzero((char *)&sin, sizeof(sin));
  /* (2) Set the fields. 
   *     This is a lot like Java: object.field = ... 
   */
  sin.sin_family = AF_INET;
  /*     Copy bytes.  Parameters are pointer to source, pointer
   *     to destination, and number of bytes to copy.
   */
  bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
  /*     Different machines may represent numeric values differently.
   *     Hence, there are standard methods for converting from "host"
   *     to "network" numbers.  htons is "host to network short"
   */
  sin.sin_port = htons(SERVER_PORT);

  /* V. Open the socket. */
  /*     The socket command returns a negative number if it fails.
   *     It is typical C programming style to build something and
   *     test its value in one line. 
   */
  if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
    /*   Print an error.  When a system call fails, you can use
     *   perror to print your own message plus a system message.
     */
    perror("failed to create socket");
    exit(3);
  }
  if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
    perror("failed to connect");
    /* We've opened the socket, so it's a good policy to close it. */
    close(s);
    exit(4);
  }
  /* VI. Main loop: Get and send lines of text. */
  /*    fgets(char *str, int n, FILE *source) reads n-1
   *    characters from the source and stores them in the
   *    string.  It also stops when it hits a new-line or
   *    end of file.  In general, fgets returns str.  If you're
   *    at the end of the file, it returns null, which we've already
   *    seen can be treated as false.
   *      Here you also see a typical C programming techinique;
   *    we're using the return value from fgets to determine whether
   *    or not the loop should continue.
   */
  while (fgets(buf, sizeof(buf), stdin)) {
    /* This sets the last character to the null character.
     * I'm not sure why this is necessary. 
     */
    buf[MAX_LINE-1] = '\0';
    /* Since the string may have ended early (e.g., with a carriage
     * return), we get the length.
     */
    len = strlen(buf) + 1;
    /* And we send everything. */
    send(s, buf, len, 0);
  } /*  while */

  /* VII. We're done.  Clean up and exit. */
  close(s);
  exit(0);
} /* main() */
