ftp.nice.ch/pub/next/unix/admin/pp.1.0.IHS.s.tar.gz#/pp/pp.c

This is pp.c in view mode; [Download] [Up]

/*
 * pp.c -- Do some special things to the printer (send some ioctls)
 *
 * $Header: /Users/marcel/src/pp/RCS/pp.c,v 1.5 96/01/24 19:01:27 marcel Exp $
 *
 * (c) 1996 Marcel Waldvogel, mwa@nice.ch
 *
 * History
 * =======
 * 960106 mwa	Created
 * 960122 mwa	Fine-tuned, beautified,
 *		added fall-back for "interval" to "timeout",
 *		eliminated the second open() call for "interval"
 *		(removed possibility for another timeout to happen),
 *		prepared for a more public release
 */

#include <objc/objc.h>
#include <stdio.h>
#include <libc.h>
#include <errno.h>

/* Compile with -DDRIVER_PRIVATE, to get extended status info from pp_extern.h */
#include <architecture/ARCH_INCLUDE.h>
#include ARCH_INCLUDE(bsd/dev/, pp_extern.h)

#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif

#define DEFDEV	"/dev/pp0"

typedef struct{
  int mask;
  char *name;
} BITSTAT;

BITSTAT driverbits[] = {
  PP_ST_INITIALIZED, "initialized",
  PP_ST_BUSY, "busy",
  PP_ST_NOPAPER, "paper out",
  PP_ST_NOTSELECTED, "!selected",
  PP_ST_TIMEOUT, "timeout",
  PP_ST_ERROR, "error",
  0, NULL
};

BITSTAT portbits[] = {
  0x80, "!busy",
  0x40, "!ack",
  0x20, "paper out",
  0x10, "!select",
  0x08, "!error",
  0x04, "rsvd2",
  0x02, "rsvd1",
  0x01, "rsvd0",
  0, NULL
};

BITSTAT controlbits[] = {
  0x80, "rsvd7",
  0x40, "rsvd6",
  0x20, "rsvd5",
  0x10, "irq enable",
  0x08, "select in",
  0x04, "!reset",
  0x02, "autofeed",
  0x01, "strobe",
  0, NULL
};

/** void usage(prog)
 * Print usage information.
 */
void usage(const char *prog)
{
  fprintf(stderr, "Usage: %s [-f device] <cmd> [<args> ...]\n", prog);
  fprintf(stderr, "Parallel port setup/status, default device is %s.\n", DEFDEV);
  fputs(
"<cmd> is one of: (can be abbreviated)\n"
"  status                       show printer status\n"
"  xstatus                      show extended status\n"
"  reset                        reset printer\n"
"  write                        write data from stdin and show status\n"
"  timeout <secs>               set timeout\n"
#if defined(PPIOCSRETRIES) && defined(PPIOCSRINTERVAL)
"  interval <retry> <interval>  set fine-grain timeout, <interval> fractional\n"
#else
"  interval <retry> <interval>  set timeout to <retry>*<interval>\n"
#endif
  , stderr);	// end of fputs()
  exit(1);
}

/** void pperror(prog, func, device)
 * Print decent error information about the current errno.
 */
void pperror(const char *prog, const char *func, const char *device)
{
  if (func == NULL)
    fprintf(stderr, "%s: %s: %s\n", prog, device, strerror(errno));
  else
  {
    if (device == NULL)
      fprintf(stderr, "%s: %s: %s\n", prog, func, strerror(errno));
    else
      fprintf(stderr, "%s: %s %s: %s\n", prog, func, device, strerror(errno));
  }
  exit(2);
}

/** void bitstat(stream, status, bs)
 * Decode a flag bitmap to a human-readable format.
 * If negate is != 0, then flags whose text start with that char
 * are output if the bit is not set (useful for negated bits).
 */
void bitstat(FILE *stream, int status, const BITSTAT *bs, char negate)
{
  int first = 1;
  int foundbits = 0;

  fputc('<', stream);
  while (bs->mask != 0)
  {
    foundbits |= bs->mask;
    
    // If you wanted alternate behavior (i.e. on multibit-stuff ALL bits
    // needed to be set, change the "!= 0" to "== bs->mask")
    if ((bs->mask & status) != 0 && (!negate || bs->name[0] != negate))
    {
      if (!first)
        fputc(',', stream);
      else
        first = 0;
      fprintf(stream, bs->name);
    }
    if ((bs->mask & status) == 0 && negate && bs->name[0] == negate)
    {
      if (!first)
        fputc(',', stream);
      else
        first = 0;
      fprintf(stream, bs->name + 1);
    }
    bs++;
  }

  // Did we output text for all bits?
  if ((status & foundbits) != status)
  {
    if (!first)
      fputc(',', stream);
    fprintf(stream, "?");
  }
  fputc('>', stream);
}

/** void ppshowstatus(prog, device, fd)
 * Prints status information for the given file descriptor
 * (prog and device are used for error messages only)
 */
void ppshowstatus(const char *prog, const char *device, int fd)
{
  int status;
  
  // Official status
  if (ioctl(fd, PPIOCSW, &status) < 0)
    pperror(prog, "PPIOCSW", device);
  printf("Driver status    = 0x%x ", status);
  bitstat(stdout, status, driverbits, '!');
  printf("\n");

#ifdef PPIOCGSREG /* Defined, when compiled with -DDRIVER_PRIVATE */
  if (ioctl(fd, PPIOCGSREG, &status) < 0)
    pperror(prog, "PPIOCGSREG", device);
  printf("Port status      = 0x%x ", status);
  bitstat(stdout, status, portbits, '!');
  printf("\n");
#endif /* PPIOCGSREG */

#if defined(PPIOCGRINTERVAL) && defined(PPIOCGRETRIES)
  /* Defined, when compiled with -DDRIVER_PRIVATE */
  {
    int count, interval;
    if (ioctl(fd, PPIOCGRINTERVAL, &interval) < 0)
      pperror(prog, "PPIOCGRINTERVAL", device);
    if (ioctl(fd, PPIOCGRETRIES, &count) < 0)
      pperror(prog, "PPIOCGRETRIES", device);
    printf("Timeout          = %ds (retries=%d, interval=%gs)\n",
           (interval * count +500) / 1000, count, (float)interval/1000.0);
  }
#endif /* PPIOCGRINTERVAL && PPIOCGRETRIES */
}

/** void ppshowextendedstatus(prog, device, fd)
 * Print extended status information for the given file descriptor
 * (prog and device are used for error messages only)
 */
void ppshowextendedstatus(const char *prog, const char *device, int fd)
{
  int status;
  
  // The "easy" part
#define GET(ctl, format) \
  if (ioctl(fd, ctl, &status) < 0) \
    pperror(prog, #ctl, device); \
  printf(format, status);

  // I would have liked to put the #ifdef into the macro,
  // but this doesn't seem to work. Does anybody know how?
#ifdef PPIOCGIHDELAY
  GET(PPIOCGIHDELAY,  "Handler delay    = %d usec\n");
#endif
#ifdef PPIOCGIOTDELAY
  GET(PPIOCGIOTDELAY, "I/O task delay   = %d usec\n");
#endif
#ifdef PPIOCGMINPHYS
  GET(PPIOCGMINPHYS,  "Max chunk size   = %d\n");
#endif
#ifdef PPIOCGBSIZE
  GET(PPIOCGBSIZE,    "Block size       = %d\n");
#endif
#ifdef PPIOCGCREG
  GET(PPIOCGCREG,     "Control register = 0x%2x ");
  bitstat(stdout, status, controlbits, '!');
  printf("\n");
#endif
#ifdef PPIOCGCREGDEF
  GET(PPIOCGCREGDEF,  "Ctl register def = 0x%2x ");
  bitstat(stdout, status, controlbits, '!');
  printf("\n");
#endif

#undef GET
}

/** void ppstatus(prog, device, extended)
 * Opens the device and prints normal or extended status, depending
 * on the flag.
 */
void ppstatus(const char *prog, const char *device, BOOL extended)
{
  int fd;

  fd = open(device, O_RDONLY | O_NDELAY); // O_NDELAY is (currently) ignored
  if (fd < 0)
    pperror(prog, "open", device);

  ppshowstatus(prog, device, fd);
  if (extended)
    ppshowextendedstatus(prog, device, fd);

  close(fd);
}

/** void ppreset(prog, device)
 * Resets the printer
 */
void ppreset(const char *prog, const char *device)
{
  int fd;

  fd = open(device, O_RDONLY | O_NDELAY);
  if (fd < 0)
    pperror(prog, "open", device);
  if (ioctl(fd, PPIOCINIT) < 0)
    pperror(prog, "PPIOCINIT", device);
  close(fd);
}

/** void ppset(prog, device, ctl, ioctlname, value)
 * Does an ioctl on the device, setting the parameter to "value"
 */
void ppset(const char *prog, const char *device, int ctl, const char *text,
	   int value)
{
  int fd;

  fd = open(device, O_RDONLY | O_NDELAY);
  if (fd < 0)
    pperror(prog, "open", device);
  if (ioctl(fd, ctl, &value) < 0)
    pperror(prog, text, device);
  close(fd);
}

/** void ppinterval(prog, device, retry, interval)
 * Sets the timeout and retry intervals reasonably
 * (tries to fall back to documented stuff if needed)
 */
void ppinterval(const char *prog, const char *device, int retry, int interval)
{
  int fd, timeout;

  fd = open(device, O_RDONLY | O_NDELAY);
  if (fd < 0)
    pperror(prog, "open", device);

  // First try it the "conventional" way, so that we have a timeout at all
  timeout = (retry * interval + 500) / 1000;
  if (ioctl(fd, PPIOCSETTIMO, &retry) < 0)
    pperror(prog, "PPIOCSETTIMO", device);

#if defined(PPIOCSRETRIES) && defined(PPIOCSRINTERVAL)
  // Now try to set everything the way we really would like it
  if (ioctl(fd, PPIOCSRETRIES, &retry) < 0)
    pperror(prog, "PPIOCSRETRIES", device);
  if (ioctl(fd, PPIOCSRINTERVAL, &interval) < 0)
    pperror(prog, "PPIOCSRINTERVAL", device);
#endif

  close(fd);
}

/** void ppwrite(prog, device)
 * Reads character by character from stdin and sends it to the printer,
 * giving full detail error messages in case anything should fail.
 * Mainly used for debugging.
 */
void ppwrite(const char *prog, const char *device)
{
  int fd;
  char c;
  
  fd = open(device, O_WRONLY);
  if (fd < 0)
    pperror(prog, "open", device);

  // Read from stdin
  while (read(STDIN_FILENO, &c, 1) == 1)
  {
    switch (write(fd, &c, 1))
    {
    case 0:
      puts("EOF on write?");
      ppshowstatus(prog, device, fd);
      ppshowextendedstatus(prog, device, fd);
      close(fd);
      exit(3);
    case 1:
      break;
    default:
      perror("write");
      ppshowstatus(prog, device, fd);
      ppshowextendedstatus(prog, device, fd);
      close(fd);
      exit(3);
    }
  }
  ppshowstatus(prog, device, fd);
  ppshowextendedstatus(prog, device, fd);
  
  close(fd);
}

/** void main(argc, argv)
 * Handles all options and dispatches the commands
 */
void main(int argc, char *argv[])
{
  const char *device = DEFDEV;
  int  argp = 1;

  // At least two more arguments
  if (argc > argp + 1 && strcmp(argv[argp], "-f") == 0)
  {
    argp++;
    device = argv[argp++];
  }

  // No more arguments?
  if (argc <= argp)
    usage(argv[0]);

  switch (argv[argp][0])
  {
  case 'r':			// reset
    ppreset(argv[0], device);
    break;
  case 's':			// status
  case 'x':			// xstatus
    // Show either normal or extended status
    ppstatus(argv[0], device, argv[argp][0] == 'x');
    break;
  case 't':			// timeout
    argp++;
    if (argc <= argp)
      usage(argv[0]);
    ppset(argv[0], device, PPIOCSETTIMO, "PPIOCSETTIMO", atoi(argv[argp]));
    break;
  case 'w':			// write
    ppwrite(argv[0], device);
    break;
  case 'i':			// interval
    argp += 2;
    if (argc <= argp)
      usage(argv[0]);
    ppinterval(argv[0], device, atoi(argv[argp - 1]),
                                (int)(atof(argv[argp])*1000.0+0.5));
    break;
  default:
    usage(argv[0]);
  }
  exit(0);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.