/* Printer control routines
   by Elliot Lee <sopwith@cuc.edu>

   It would be nice if we supported other protocols - if you feel so
   inspired, just write the classes for it (I really should use
   @protocol here...)
*/
#include "gPrint.h"

#define TRUE YES
#define FALSE NO

#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdarg.h>
#include <objc/objc-api.h>
#include <objc/objc.h>
#include <glib.h>

#define LPRM_PATH "/usr/bin/lprm"
#define LPC_S_PATH "/usr/sbin/lpc"
#define LPC_PATH "/usr/bin/lpc"
#define LPQ_PATH "/usr/bin/lpq"
#define PRINTCAP_PATH "/etc/printcap"
#define LPQ_FIRSTLINE_1 "Rank"
#define LPQ_FIRSTLINE_2 "Owner"

@implementation PrintJob
- initWithJobInfo:(int) initjobid
	     Size:(int) initsize
	 Filename:(char *) initfilename
	     User:(char *) inituser
	 aPrinter:(Printer *) initprinter
{
  jobid = initjobid;
  jobsize = initsize;
  filename = strdup(initfilename);
  user = strdup(inituser);
  printer = initprinter;
  return self;
}

- (int)getJobSize
{
  return jobsize;
}

- (int)getJobID
{
  return jobid;
}

- (const char *)getJobFilenames
{
  return filename;
}

- (const char *)getJobOwner
{
  return user;
}

- (Printer *)getPrinter
{
  return printer;
}

- free
{
  free(filename);
  free(user);
  return [super free];
}

- (int)cancel
{
  char jobstr[20], printqueueopt[80];

  g_snprintf(jobstr, 20, "%d", jobid);
  g_snprintf(printqueueopt, 80, "-P%s", [printer getQueueName]);
  return !doRunCmd(LPRM_PATH, LPRM_PATH, printqueueopt, jobstr, NULL);
}
@end

@implementation PrintSystem
- (BOOL)exists
{
    struct stat sbuf;
  
    return (((stat(LPC_PATH, &sbuf) != 0) && (stat(LPC_S_PATH, &sbuf) != 0))
	    || (stat(LPRM_PATH, &sbuf) != 0)
	    || (stat(LPQ_PATH, &sbuf) != 0)
	    || (stat(PRINTCAP_PATH, &sbuf) != 0)) ? FALSE : TRUE;
}

- addPrinter:(char *) queuename
{
  numprinters++;
  if(numprinters > nprtalloc) {
    nprtalloc += 5; /* Use a chunk size of 5, why not? :) */
    printers = realloc(printers, nprtalloc * sizeof(Printer *));
  }
  printers[numprinters - 1] = [[Printer new] initWithName:queuename];
  return self;
}

- init
{
  FILE *pcfile;
  char aline[512], *tptr;

  [super init];
  pcfile = fopen(PRINTCAP_PATH, "r");
  if(!pcfile) {
    [super free]; /* XXX is this legal? Use exceptions here
		     when we learn about them :) */
    return nil;
  }

  printers = NULL;
  numprinters = nprtalloc = 0;

  /* XXX this parsing sucks, fix it
     - if we get a line without a : in it it will bomb up prolly */
  while(fgets(aline, 512, pcfile)) {
    tptr = aline;
    while(*tptr && isspace(*tptr)) tptr++;
    if(isalnum(*tptr))
      [self addPrinter:strtok(tptr, ":")];
  }
  fclose(pcfile);

  return self;
}

- free
{
  int i;
  for(i = 0; i < numprinters; i++)
    [printers[i] free];
  free(printers);
  return [super free];
}
@end

@implementation Printer
- initWithName:(char *) initqueuename
{
  char *tmp;
  struct stat sbuf;
  
  [super init];

  lpc_path = NULL;
  if (stat(LPC_PATH, &sbuf) == 0)
    {
      lpc_path = LPC_PATH;
    }
  else if (stat(LPC_S_PATH, &sbuf) == 0)
    {
      lpc_path = LPC_S_PATH;
    }
  

  queuename = strdup(initqueuename);
  tmp = strchr(queuename, '|');
  if(tmp)
    *tmp = '\0';

  jobs = NULL;
  numjobs = njoballoc = 0;
  return self;
}

- (const char *) getQueueName
{
  return (const char *) queuename;
}

- free
{
  int i;
  free(queuename);
  for(i = 0; i < numjobs; i++) [jobs[i] free];
  free(jobs);
  numjobs = njoballoc = 0;
  jobs = NULL;

  return [super free];
}

- addJob:(PrintJob *) ajob
{
  numjobs++;
  if(numjobs > njoballoc) {
    njoballoc += 5; /* Use a chunk size of 5, why not? :) */
    jobs = realloc(jobs, njoballoc * sizeof(PrintJob *));
  }
  jobs[numjobs - 1] = ajob;
  return self;
}

- chomp:(char *)astring
{
  int i = strlen(astring );
  while(isspace(astring[--i])) astring[i] = '\0';
  return self;
}

- getJobs
{
  int i;
  char execstr[512], aline[1024];
  int ajobid, jobsize;
  char jobfn[512], jobowner[512];

  FILE *lpqout;
  for(i = 0; i < numjobs; i++) [jobs[i] free];
  free(jobs);
  numjobs = njoballoc = 0;
  jobs = NULL;

  g_snprintf(execstr, sizeof(execstr), "%s -P%s", LPQ_PATH, queuename);

  if ((lpqout = popen(execstr, "r")) == NULL) {
    fprintf(stderr, "gulp: Couldn't run `%s'\n", execstr);
    exit(1);

    return self;
  }
  
  while(fgets(aline, sizeof(aline), lpqout))
    if (strstr(aline, LPQ_FIRSTLINE_1) && strstr(aline, LPQ_FIRSTLINE_2))
      break;
  
  bzero(jobfn, sizeof(jobfn));
  bzero(jobowner, sizeof(jobfn));
  /* OK, the next line we get here when going into this loop will be
     the actual data that we're wanting... */
  while(fscanf(lpqout,
	       "%*d%*[a-zA-Z]%*[ ]%512[^ ]%*[ ]%d%*[ ]%37c%*[ ]%d bytes\n",
	       jobowner, &ajobid, jobfn, &jobsize) > 0) {
    [self chomp:jobfn];
    [self addJob:[[PrintJob new] initWithJobInfo:ajobid
				 Size:jobsize
				 Filename:jobfn
				 User:jobowner
				 aPrinter:self]];
    bzero(jobfn, sizeof(jobfn));
    bzero(jobowner, sizeof(jobfn));
  }

  pclose(lpqout);

  return self;
}

- (int)doLPC:(char *)cmd
{
  return doRunCmd(lpc_path, lpc_path, cmd, queuename, NULL);
}

- (int)up
{
  return [self doLPC:"up"];
}

- (int)down
{
  return [self doLPC:"down"];
}

- (int)clean
{
  return [self doLPC:"clean"];
}

- (int)restart
{
  return [self doLPC:"restart"];
}

- (int)start
{
  return [self doLPC:"start"];
}

- (int)stop
{
  return [self doLPC:"stop"];
}

- (BOOL)isUp
{
  FILE *pout;
  char execstr[512];
  BOOL retval = FALSE;

  g_snprintf(execstr, sizeof(execstr), "%s status %s",
	     lpc_path, queuename);
  if ((pout = popen(execstr, "r")) != NULL) {
    fgets(execstr, sizeof(execstr), pout); /* Discard queuename line */
    if(fgets(execstr, sizeof(execstr), pout) != NULL)
      if(strstr(execstr, "enabled"))
	retval = TRUE;
    pclose(pout);
  }
  return retval;
}

- (BOOL)isClean
{
  FILE *pout;
  char execstr[512];
  BOOL retval = FALSE;

  g_snprintf(execstr, sizeof(execstr), "%s status %s",
	     lpc_path, queuename);
  if ((pout = popen(execstr, "r")) != NULL) {
    fgets(execstr, sizeof(execstr), pout); /* Discard queuename line */
    fgets(execstr, sizeof(execstr), pout); /* Discard queueing line */
    fgets(execstr, sizeof(execstr), pout); /* Discard printing line */
    if(fgets(execstr, sizeof(execstr), pout) != NULL)
      if(strstr(execstr, "no entries"))
	retval = TRUE;
    pclose(pout);
  }
  return retval;
}

- (BOOL)isStarted
{
  FILE *pout;
  char execstr[512];
  BOOL retval = FALSE;
  g_snprintf(execstr, sizeof(execstr), "%s status %s",
	     lpc_path, queuename);
  if ((pout = popen(execstr, "r")) != NULL) {
    fgets(execstr, sizeof(execstr), pout); /* Discard queuename line */
    fgets(execstr, sizeof(execstr), pout); /* Discard queueing line */
    if(fgets(execstr, sizeof(execstr), pout) != NULL)
      if(strstr(execstr, "enabled"))
	retval = TRUE;
    pclose(pout);
  }
  return retval;
}
@end
