File hal.c of Package cups-backends

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 *  Authors: Jeffrey Stedfast <fejj@novell.com>
 *  Authors: Klaus Singvogel <kssingvo@suse.de>
 *
 *  Copyright 2005 Novell, Inc. (www.novell.com)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
 *
 */


#include <cups/cups.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>

#include <dbus/dbus.h>
#include <hal/libhal.h>


static LibHalContext *
get_hal_ctx (void)
{
	DBusConnection *connection;
	LibHalContext *hal_ctx;
	DBusError error;
	
	if (!(hal_ctx = libhal_ctx_new ())) {
		fprintf (stderr, "ERROR: Unable to create HAL context\n");
		return NULL;
	}
	
	dbus_error_init (&error);
	if (!(connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error))) {
		fprintf (stderr, "ERROR: Unable to connect to DBUS: %s\n", error.message);
		libhal_ctx_free (hal_ctx);
		dbus_error_free (&error);
		return NULL;
	}
	
	libhal_ctx_set_dbus_connection (hal_ctx, connection);
	
	if (!libhal_ctx_init (hal_ctx, &error)) {
		fprintf (stderr, "ERROR: Unable to init HAL context: %s\n", error.message);
		libhal_ctx_free (hal_ctx);
		dbus_error_free (&error);
		return NULL;
	}
	
	return hal_ctx;
}


/*
 * 'list_devices()' - List all HAL printer devices.
 */

static void
list_devices (void)
{
	LibHalContext *hal_ctx;
	char **printers;
	int i, n;
	
	if (!(hal_ctx = get_hal_ctx ()))
		return;
	
	printers = libhal_find_device_by_capability (hal_ctx, "printer", &n, NULL);
	
	if (n == 0)
		printf("direct hal \"Unknown\" \"Hal printing backend\"\n"); 
	
	for (i = 0; i < n; i++) {
		char *vendor, *product, *description;
		char make_model[1024];
		
		/* We only want printers that have device nodes */
		if (!libhal_device_property_exists (hal_ctx, printers[i], "printer.device", NULL))
			continue;
		
		vendor = libhal_device_get_property_string (hal_ctx, printers[i], "printer.vendor", NULL);
		product = libhal_device_get_property_string (hal_ctx, printers[i], "printer.product", NULL);
		description = libhal_device_get_property_string (hal_ctx, printers[i], "printer.description", NULL);
		
		/* Try our hardest to get a good name */
		if (product != NULL) {
			if (vendor != NULL) {
				snprintf (make_model, sizeof (make_model), "%s %s", vendor, product);
			} else {
				strncpy (make_model, product, sizeof (make_model) - 1);
				make_model[sizeof (make_model) - 1] = '\0';
			}
		} else if (description != NULL) {
			strncpy (make_model, description, sizeof (make_model) - 1);
			make_model[sizeof (make_model) - 1] = '\0';
		} else if (vendor != NULL) {
			snprintf (make_model, sizeof (make_model), "%s printer", vendor);
		} else {
			strcpy (make_model, "Unknown");
		}
		
		printf ("direct hal://%s \"%s\" \"%s\"\n", printers[i], make_model,
			description != NULL ? description : make_model);
		
		libhal_free_string (vendor);
		libhal_free_string (product);
		libhal_free_string (description);
	}
	
	libhal_ctx_shutdown (hal_ctx, NULL);
	libhal_ctx_free (hal_ctx);
}

/*
 * 'get_device_file()' - Get a device file from a HAL device UDI
 */
static char *
get_device_file (const char *uri)
{
	LibHalContext *hal_ctx;
	const char *udi;
	char *device;
	
	if (strncmp (uri, "hal://", 6) != 0)
		return NULL;
	
	if (!(hal_ctx = get_hal_ctx ()))
		return NULL;
	
	udi = uri + 6;
	
	device = libhal_device_get_property_string (hal_ctx, udi, "printer.device", NULL);
	
	libhal_ctx_shutdown (hal_ctx, NULL);
	libhal_ctx_free (hal_ctx);
	
	return device;
}

/*
 * 'main()' - Send a file to the specified HAL printer device.
 *
 * Usage:
 *
 *    printer-uri job-id user title copies options [file]
 */

int
main (int argc, char *argv[])
{
	int fp;                  /* Print file */
	int copies;              /* Number of copies to print */
	char *device;            /* Device file to open */
	int fd;                  /* Device file descriptor */
	ssize_t nwritten;        /* Number of bytes written */
	size_t nbytes;           /* Number of bytes read */
	size_t tbytes;           /* Total number of bytes written */
	char buffer[8192];       /* Output buffer */
	char *bufptr;	         /* Pointer into buffer */
	struct termios opts;     /* Parallel port options */
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
	struct sigaction action; /* Actions for POSIX signals */
#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
#if defined(__linux) && defined(LP_POUTPA)
	unsigned char status;    /* Port status (off-line, out-of-paper, etc.) */
#endif /* __linux */
	
	
	/*
	 * Make sure status messages are not buffered...
	 */
	
	setbuf (stderr, NULL);
	
	/*
	 * Ignore SIGPIPE signals...
	 */
	
#ifdef HAVE_SIGSET
	sigset (SIGPIPE, SIG_IGN);
#elif defined(HAVE_SIGACTION)
	memset (&action, 0, sizeof (action));
	action.sa_handler = SIG_IGN;
	sigaction (SIGPIPE, &action, NULL);
#else
	signal (SIGPIPE, SIG_IGN);
#endif /* HAVE_SIGSET */
	
	/*
	 * Check command-line...
	 */
	
	if (argc == 1) {
		list_devices ();
		return 0;
	} else if (argc < 6 || argc > 7) {
		fputs ("Usage: URI job-id user title copies options [file]\n", stderr);
		return 1;
	}
	
	/*
	 * If we have 7 arguments, print the file named on the command-line.
	 * Otherwise, send stdin instead...
	 */
	
	if (argc == 6) {
		fp = 0;
		copies = 1;
	} else {
		if ((fp = open (argv[6], O_RDONLY)) == -1) {
			perror ("ERROR: unable to open print file");
			return 1;
		}
		
		copies = atoi (argv[4]);
	}
	
	if (!(device = get_device_file (argv[0]))) {
		fprintf (stderr, "ERROR: Unable to open HAL device \"%s\"\n", argv[0]);
		return 1;
	}
	
	do {
		if ((fd = open (device, O_RDWR | O_EXCL)) == -1) {
			if (errno == EBUSY) {
				fputs ("INFO: Device busy; will retry in 30 seconds...\n", stderr);
				sleep (30);
			} else if (errno == ENXIO || errno == EIO || errno == ENOENT) {
				fputs ("INFO: Printer not connected; will retry in 30 seconds...\n", stderr);
				sleep (30);
			} else {
				fprintf (stderr, "ERROR: Unable to open device \"%s\": %s\n",
					 argv[0], strerror (errno));
				return 1;
			}
		}
	} while (fd == -1);
	
	libhal_free_string (device);
	
	/*
	 * Set any options provided...
	 */
	
	tcgetattr (fd, &opts);
	
	opts.c_lflag &= ~(ICANON | ECHO | ISIG);	/* Raw mode */
	
	/**** No options supported yet ****/
	
	tcsetattr (fd, TCSANOW, &opts);
	
	/*
	 * Now that we are "connected" to the port, ignore SIGTERM so that we
	 * can finish out any page data the driver sends (e.g. to eject the
	 * current page...  Only ignore SIGTERM if we are printing data from
	 * stdin (otherwise you can't cancel raw jobs...)
	 */
	
	if (argc < 7) {
#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
		sigset (SIGTERM, SIG_IGN);
#elif defined(HAVE_SIGACTION)
		memset (&action, 0, sizeof (action));
		
		sigemptyset (&action.sa_mask);
		action.sa_handler = SIG_IGN;
		sigaction (SIGTERM, &action, NULL);
#else
		signal (SIGTERM, SIG_IGN);
#endif /* HAVE_SIGSET */
	}
	
#if defined(__linux) && defined(LP_POUTPA)
	if (ioctl (fd, LPGETSTATUS, &status) == 0) {
		fprintf (stderr, "DEBUG: LPGETSTATUS returned a port status of %02X...\n", status);
		
		if (!(status & LP_POUTPA))
			fputs ("WARNING: Media tray empty!\n", stderr);
		else if (!(status & LP_PERRORP))
			fputs ("WARNING: Printer fault!\n", stderr);
		else if (!(status & LP_PSELECD))
			fputs ("WARNING: Printer off-line.\n", stderr);
	}
#endif /* __linux && LP_POUTPA */
	
	/*
	 * Finally, send the print file...
	 */
	
	while (copies > 0) {
		copies--;
		
		if (fp != 0) {
			fputs ("PAGE: 1 1\n", stderr);
			lseek (fp, 0, SEEK_SET);
		}
		
		tbytes = 0;
		while ((nbytes = read (fp, buffer, sizeof (buffer))) > 0) {
			/*
			 * Write the print data to the printer...
			 */
			
			tbytes += nbytes;
			bufptr = buffer;
			
			while (nbytes > 0) {
				if ((nwritten = write (fd, bufptr, nbytes)) == -1)
					if (errno == ENOTTY)
						nwritten = write (fd, bufptr, nbytes);
				
				if (nwritten == -1) {
					perror ("ERROR: Unable to send print file to printer");
					break;
				}
				
				nbytes -= nwritten;
				bufptr += nwritten;
			}
			
			if (nwritten == -1)
				break;
			
			if (argc > 6)
				fprintf (stderr, "INFO: Sending print file, %lu bytes...\n", (unsigned long) tbytes);
		}
	}
	
	/*
	 * Close the socket connection and input file and return...
	 */
	
	close (fd);
	if (fp != 0)
		close (fp);
	
	fputs ("INFO: Ready to print.\n", stderr);
	
	return 0;
}
openSUSE Build Service is sponsored by