#include <stdio.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>

extern int errno;

struct sgttyb terminit, termmod;

struct twoway
{
  FILE *sock_rd, *sock_wt;
};

/* Setup deals with signals, putting the terminal into cbreak mode,
 * and opening the socket connection to the news server.
 */

struct twoway *Setup(newsserver)
     char *newsserver;
{
  struct twoway *sock_rw;
  char *dump;
  int status;
  FILE *sock_rd;

  void Shutdown(), error();
  struct twoway *MakeSockets();
  int Suspend(), Restart();
  char *GetLine();

/* Set up signal traps -- how to make the program die. */

  signal(SIGHUP, Shutdown);
  signal(SIGINT, Shutdown);
  signal(SIGTERM, Shutdown);
  signal(SIGXCPU, Shutdown);
  signal(SIGXFSZ, Shutdown);

/* In case of suspend and restart. */

  signal(SIGTSTP, Suspend);
  signal(SIGCONT, Restart);

/* Ignore SIGPIPE -- prevents a quit in the middle of more from
 * exiting the program.
 */

  signal(SIGPIPE, SIG_IGN);

/* Ignore user-defined stuff. */

  signal(SIGUSR1, SIG_IGN);
  signal(SIGUSR2, SIG_IGN);

/* Put the terminal into cbreak mode, so that all characters typed are
 * immediately available as input.
 */

  if (ioctl(fileno(stdout), TIOCGETP, &terminit) == -1)
    {
      perror("TIOCGETP blew it");
      printf("%d\n", errno);
    }

  termmod.sg_ispeed = terminit.sg_ispeed;
  termmod.sg_ospeed = terminit.sg_ospeed;
  termmod.sg_erase = terminit.sg_erase;
  termmod.sg_kill = terminit.sg_kill;
  termmod.sg_flags = terminit.sg_flags | CBREAK;

  if (ioctl(fileno(stdout), TIOCSETP, &termmod) == -1)
    {
      perror("TIOCSETP blew it");
      printf("%d\n", errno);
    }

/* Create the sockets needed to talk to the NNTP server. */

  sock_rw = MakeSockets("nntp", "tcp", newsserver);
  sock_rd = sock_rw->sock_rd;

/* Check the status of the server line returned. */

  dump = GetLine(sock_rd);
  sscanf(dump, "%d\n", &status);
  if (status == 200 || status == 201)
    return(sock_rw);
  if (status >= 400)
    error("Nmsgs: Setup: Cannot read newsgroups at this time.");
}


/* Suspend sets the terminal back to the original mode and sends an
 * uncatchable suspend signal to the process.
 */

int Suspend()
{
  if (ioctl(fileno(stdout), TIOCSETP, &terminit) == -1)
    {
      perror("TIOCGETP blew it");
      printf("%d\n", errno);
    }
  kill(getpid(), SIGSTOP);
  return(0);
}


/* Restart sets the terminal back to cbreak mode and continues the
 * program.
 */

int Restart()
{
  if (ioctl(fileno(stdout), TIOCSETP, &termmod) == -1)
    {
      perror("TIOCGETP blew it");
      printf("%d\n", errno);
    }
  return(0);
}


/* MakeSockets, given the name of a service, the protocol for the
 * service, and the machine providing the service, creates and returns
 * a structure containing two file pointers: one for reading from the
 * socket, and one to write to the socket.
 *
 * Much of this code was taken verbatim from the UNIX Programmer's
 * Supplementary Documents, volume 1 (PS1), sections 7 and 8.
 */

struct twoway *MakeSockets(service, protocol, machine)
     char *service, *protocol, *machine;
{
  struct twoway *sock_rw;
  struct sockaddr_in server;
  struct servent *sp;
  struct hostent *hp;
  int sock, sockdup;

#ifdef ultrix
  /* sigh. ultrix 4.2 doesn't provide a declaration for h_errno, even
   * though the need for one is mentioned in /usr/include/netdb.h.
   *
   * -tuna (tuna@lcs.mit.edu), 28 august 1991
   */
  extern int h_errno;
#endif

  void CheckError(), error();

/* Create the appropriate service entry structure */

  sp = getservbyname(service, protocol);
  if (sp == NULL)
    CheckError("Nmsgs: MakeSockets: getservbyname");

/* Snare a host entry and make a structure out of it, too */

  hp = gethostbyname(machine);
  if (h_errno == HOST_NOT_FOUND)
    error("Nmsgs: MakeSockets: gethostbyname: Host unknown.");
  if (h_errno == TRY_AGAIN)
    error("Nmsgs: MakeSockets: gethostbyname: Couldn't get host.  Try later.");
  if (h_errno == NO_RECOVERY)
    error("Nmsgs: MakeSockets: gethostbyname: Non-recoverable error.");
  if (h_errno == NO_ADDRESS)
    error("Nmsgs: MakeSockets: gethostbyname: No IP address for host.");

/* Clear and then set server. */

  bzero((char *)&server, sizeof(server));
  bcopy(hp->h_addr, (char *)&server.sin_addr, hp->h_length);
  server.sin_family = hp->h_addrtype;
  server.sin_port = sp->s_port;

/* Create the socket, and make a twin. */

  sock = socket(AF_INET, SOCK_STREAM, 0);
  sockdup = dup(sock);

/* Check the existence of the sockets. */

  if (sock < 0 || sockdup < 0)
    CheckError("Nmsgs: MakeSockets: Socket creation failed.");

/* Connect the socket to the server. */

  if (connect(sock, (char *)&server, sizeof(server)) < 0)
    CheckError("Nmsgs: MakeSockets: Socket connection failed.");

/* Create and set the two parts of sock_rw. */

  sock_rw = (struct twoway *)malloc(sizeof(struct twoway));
  if (sock_rw == NULL)
    CheckError("Nmsgs: MakeSockets: malloc");
  sock_rw->sock_rd = fdopen(sock, "r");
  if (sock_rw->sock_rd == NULL)
    error("Nmsgs: MakeSockets: fdopen");
  sock_rw->sock_wt = fdopen(sockdup, "w");
  if (sock_rw->sock_wt == NULL)
    error("Nmsgs: MakeSockets: fdopen");

/* Send it back, you're done here. */

  return(sock_rw);
}
