/****************************************************************************\
*	Copyright (C) 1992-1997 Michael K. Johnson, johnsonm@redhat.com      *
*									     *
*	This file is licensed under the terms of the GNU General             *
*	Public License, version 2, or any later version.  See file COPYING   *
*	for information on distribution conditions.			     *
\****************************************************************************/

/*
 * $Log: tunelp.c,v $
 * Revision 1.9  1998/06/08 19:37:11  janl
 * Thus compiles tunelp with 2.1.103 kernels
 *
 * Revision 1.8  1997/07/06 00:14:06  aebr
 * Fixes to silence -Wall.
 *
 * Revision 1.7  1997/06/20 16:10:38  janl
 * tunelp refreshed from authors archive.
 *
 * Revision 1.9  1997/06/20 12:56:43  johnsonm
 * Finished fixing license terms.
 *
 * Revision 1.8  1997/06/20 12:34:59  johnsonm
 * Fixed copyright and license.
 *
 * Revision 1.7  1995/03/29 11:16:23  johnsonm
 * TYPO fixed...
 *
 * Revision 1.6  1995/03/29  11:12:15  johnsonm
 * Added third argument to ioctl needed with new kernels
 *
 * Revision 1.5  1995/01/13  10:33:43  johnsonm
 * Chris's changes for new ioctl numbers and backwards compatibility
 * and the reset ioctl.
 *
 * Revision 1.4  1995/01/03  17:42:14  johnsonm
 * -s isn't supposed to take an argument; removed : after s in getopt...
 *
 * Revision 1.3  1995/01/03  07:36:49  johnsonm
 * Fixed typo
 *
 * Revision 1.2  1995/01/03  07:33:44  johnsonm
 * revisions for lp driver updates in Linux 1.1.76
 *
 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
 * - added Native Language Support
 *
 * 1999-05-07 Merged LPTRUSTIRQ patch by Andrea Arcangeli (1998/11/29), aeb
 *
 */

#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
/* #include <linux/fs.h> */
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "lp.h"
#include "nls.h"

struct command {
  long op;
  long val;
  struct command *next;
};


static void
print_usage(char *progname) {
  printf(_("Usage: %s <device> [ -i <IRQ> | -t <TIME> | -c <CHARS> | -w <WAIT> | \n"
	   "          -a [on|off] | -o [on|off] | -C [on|off] | -q [on|off] | -s | \n"
	   "          -T [on|off] ]\n"),
	 progname);
  exit (1);
}

static void
print_version(char *progname) {
  printf("%s (%s)\n", progname, PACKAGE_STRING);
}

static void *
mylloc(long size) {
  void *ptr;
  if(!(ptr = (void*)malloc(size))) {
    perror(_("malloc error"));
    exit(2);
  }
  return ptr;
}

static char *progname;

static long
get_val(char *val) {
  long ret;
  if (!(sscanf(val, "%ld", &ret) == 1)) {
    fprintf(stderr, _("%s: bad value\n"), progname);
    exit(3);
  }
  return ret;
}


static long
get_onoff(char *val) {
  if (!strncasecmp("on", val, 2))
    return 1;
  return 0;
}

int
main (int argc, char ** argv) {
  int c, fd, irq, status, show_irq, offset = 0, retval;
  char *filename, *p;
  struct stat statbuf;
  struct command *cmds, *cmdst;

  progname = argv[0];
  if ((p = strrchr(progname, '/')) != NULL)
	  progname = p+1;

  setlocale(LC_ALL, "");
  bindtextdomain(PACKAGE, LOCALEDIR);
  textdomain(PACKAGE);

  if (argc < 2) print_usage(progname);

  cmdst = cmds = mylloc(sizeof(struct command));
  cmds->next = 0;

  show_irq = 1;
  while ((c = getopt(argc, argv, "t:c:w:a:i:ho:C:sq:rT:vV")) != -1) {
    switch (c) {
    case 'h':
      print_usage(progname);
      break;
    case 'i':
      cmds->op = LPSETIRQ;
      cmds->val = get_val(optarg);
      cmds->next = mylloc(sizeof(struct command));
      cmds = cmds->next; cmds->next = 0;
      break;
    case 't':
      cmds->op = LPTIME;
      cmds->val = get_val(optarg);
      cmds->next = mylloc(sizeof(struct command));
      cmds = cmds->next; cmds->next = 0;
      break;
    case 'c':
      cmds->op = LPCHAR;
      cmds->val = get_val(optarg);
      cmds->next = mylloc(sizeof(struct command));
      cmds = cmds->next; cmds->next = 0;
      break;
    case 'w':
      cmds->op = LPWAIT;
      cmds->val = get_val(optarg);
      cmds->next = mylloc(sizeof(struct command));
      cmds = cmds->next; cmds->next = 0;
      break;
    case 'a':
      cmds->op = LPABORT;
      cmds->val = get_onoff(optarg);
      cmds->next = mylloc(sizeof(struct command));
      cmds = cmds->next; cmds->next = 0;
      break;
    case 'q':
      if (get_onoff(optarg)) {
        show_irq=1;
      } else {
        show_irq=0;
      }
#ifdef LPGETSTATUS
    case 'o':
      cmds->op = LPABORTOPEN;
      cmds->val = get_onoff(optarg);
      cmds->next = mylloc(sizeof(struct command));
      cmds = cmds->next; cmds->next = 0;
      break;
    case 'C':
      cmds->op = LPCAREFUL;
      cmds->val = get_onoff(optarg);
      cmds->next = mylloc(sizeof(struct command));
      cmds = cmds->next; cmds->next = 0;
      break;
    case 's':
      show_irq = 0;
      cmds->op = LPGETSTATUS;
      cmds->val = 0;
      cmds->next = mylloc(sizeof(struct command));
      cmds = cmds->next; cmds->next = 0;
      break;
#endif
#ifdef LPRESET
    case 'r':
      cmds->op = LPRESET;
      cmds->val = 0;
      cmds->next = mylloc(sizeof(struct command));
      cmds = cmds->next; cmds->next = 0;
      break;
#endif
#ifdef LPTRUSTIRQ
    case 'T':
      /* Note: this will do the wrong thing on 2.0.36 when compiled under 2.2.x */
      cmds->op = LPTRUSTIRQ;
      cmds->val = get_onoff(optarg);
      cmds->next = mylloc(sizeof(struct command));
      cmds = cmds->next; cmds->next = 0;
      break;
#endif
    case 'v':
    case 'V':
      print_version(progname);
      exit(0);
    default:
      print_usage(progname);
    }
  }

  if (optind != argc-1)
    print_usage(progname);

  filename = strdup(argv[optind]);
  fd = open(filename, O_WRONLY|O_NONBLOCK, 0);
  /* Need to open O_NONBLOCK in case ABORTOPEN is already set and
     printer is off or off-line or in an error condition.  Otherwise
     we would abort... */
  if (fd < 0) {
    perror(filename);
    return -1;
  }

  fstat(fd, &statbuf);

  if(!S_ISCHR(statbuf.st_mode)) {
    printf(_("%s: %s not an lp device.\n"), argv[0], filename);
    print_usage(progname);
  }

  /* Allow for binaries compiled under a new kernel to work on the old ones */
  /* The irq argument to ioctl isn't touched by the old kernels, but we don't */
  /*  want to cause the kernel to complain if we are using a new kernel */
  if (LPGETIRQ >= 0x0600 && ioctl(fd, LPGETIRQ, &irq) < 0 && errno == EINVAL)
    offset = 0x0600;	/* We don't understand the new ioctls */

  cmds = cmdst;
  while (cmds->next) {
#ifdef LPGETSTATUS
    if (cmds->op == LPGETSTATUS) {
      status = 0xdeadbeef;
      retval = ioctl(fd, LPGETSTATUS - offset, &status);
      if (retval < 0)
      	perror("LPGETSTATUS error");
      else {
        if (status == 0xdeadbeef)	/* a few 1.1.7x kernels will do this */
          status = retval;
	printf(_("%s status is %d"), filename, status);
	if (!(status & LP_PBUSY)) printf(_(", busy"));
	if (!(status & LP_PACK)) printf(_(", ready"));
	if ((status & LP_POUTPA)) printf(_(", out of paper"));
	if ((status & LP_PSELECD)) printf(_(", on-line"));
	if (!(status & LP_PERRORP)) printf(_(", error"));
	printf("\n");
      }
    } else
#endif /* LPGETSTATUS */
    if (ioctl(fd, cmds->op - offset, cmds->val) < 0) {
      perror("tunelp: ioctl");
    }
    cmdst = cmds;
    cmds = cmds->next;
    free(cmdst);
  }

  if (show_irq) {
    irq = 0xdeadbeef;
    retval = ioctl(fd, LPGETIRQ - offset, &irq);
    if (retval == -1) {
      perror(_("LPGETIRQ error"));
      exit(4);
    }
    if (irq == 0xdeadbeef)		/* up to 1.1.77 will do this */
      irq = retval;
    if (irq)
      printf(_("%s using IRQ %d\n"), filename, irq);
    else
      printf(_("%s using polling\n"), filename);
  }

  close(fd);

  return 0;
}
