Printing from Applications in Solaristm;

Norman A. Jacobs
Sun Microsystems, Inc.

 

 

Overview

Time and time again, application developers want to know how to integrate printing into their applications on Solaristm;. They generally want to perform two simple tasks. These tasks are generating a list of printers and actually submitting their output for print.

Interfaces

Unfortunately, Solaristm; doesn't contain any Application Programmer Interfaces(APIs) for printing. It does however contain three Call Level Interfaces (CLIs) that are printing related. These CLIs allow three basic operations to be performed. These operations include statusing, submitting, and cancelling. The CLIs are lpstat(1), lp(1), and cancel(1). Their complete definition can be found in the System V Interface Defintion (SVID) or more accurately in the Solaristm; online man pages.

Available Printers

Often application developers want to provide users with a list of available printers to choose from. To do this, they have to generate this list programatically. Generating a list programatically can only be done via one public interface in Solaristm;. This interface is the lpstat(1) interface. There are a number of options to lpstat the will operate and report across all of the configured printers. Many of them take an extraordinary amount of time to complete when a large number of printers are involved. This is because these options must contact the various print servers for these printers, get status, parse the results and present them to the user.

The most expedient method of gathering a list of printers is to execute the command "/usr/bin/lpstat -v". This command only performs a naming lookup and displays results. To use this programatically, an application can use the popen(3) function and parse the input stream.

Example: popen_printer_list.c

/*
 * Copyright (C) 1998, by Sun Microsystems, Inc.
 * All Rights Reserved
 */

#pragma ident "@(#)popen_printer_list.c 1.2 98/05/11 SMI"

#include <stdio.h>
#include <strings.h>

main(int ac, char *av[])
{
	FILE *fp;

	if ((fp = popen("/usr/bin/lpstat -v", "r+")) != NULL) {
		char buf[BUFSIZ];

		while (fgets(buf, sizeof (buf), fp) != NULL) {
			strtok(buf, ": "); /* eat system/device */
			strtok(NULL, ": "); /* eat for */
			printf("printer: %s\n", strtok(NULL, ": "));
		}
		fclose(fp);
	}
}

Job Submission

The most common printing related action an application developer needs to perform is print job submission. The only public interface that allows print job submission is the lp(1) interface. Again, like the lpstat(1) interface, it has literally dozens of options. Many of the options are of little value to an application performing job submission. A few of these options can be usefull, and even important to job submission from an application. In order to understand which options to use during job submission from an application, it is important to understand a little bit about how the lp(1) command performs it's submission.

When the lp command is invoked, the instance of the command parses the command line arguments and builds a print request. This request is actually derived from the command line options, configuration data, and the data to be print. Many of the lp(1) command line options set various request characteristics. These characteristics include datatype, filter options,. reporting actions, etc.

By default when an lp(1) request is made, the job data is printed by reference. What this means to an application submitting data to print is that it must tell lp(1) to copy the job data before printing if the data is kept in a temporary file. If the data is piped to lp(1), it will be placed in a temporary file automatically by lp(1).

Another atcion performed by the Solaristm; print server further down the line is a quick scan of the input data. It does this to attempt to auto-detect whether or the the print data is PostScript or not. If the data begins with "%!" or "^d%!" the print spooler lp(1) consideres the data to be PostScript. If the data is PostScript, but doesn't begin with "%!" or "^d%!" or the data is to print as simple text, but begins with either of these sequences, the application will need to add "-T postscript" or "-T simple" to get the desired result.

Example: popen_submit.c

/*
 * Copyright (C) 1998, by Sun Microsystems, Inc.
 * All Rights Reserved
 */
#pragma ident "@(#)popen_submit.c 1.2 98/05/11 SMI"

#include <stdio.h>
#include <strings.h>
/*
 * This file contains some relatively simple examples of how to submit print
 * jobs to the LP system programatically in Solaris. The examples are
 * complete enough to run, but should be tailored to your needs. Also,
 * you will note that snprintf(3c) is used to avoid buffer overflow problems.
 * This interface is not available until Solaris 2.6. Finally, it is important
 * to note that popen(3) can fail because file descriptors 0-255 are in use.
 * This is a file pointer limitation.  If your application uses a large
 * number of file descriptors, you may want to run lp via pipe(), fork()
 * and exec() or simply via system() depending on your needs.
 */


/*
 * Assuming that the command uses any user defined data, It is a good
 * idea to replace some shell meta characters that may breakup the command
 * and/or allow the user to run something of their own.  Something like this
 * is particularly important if the program is SUID.
 */
void remove_shell_meta_characters(char *command)
{
	char *ptr = command;

	while ((ptr = strpbrk(ptr, "`&;|>^$\\")) != NULL)
		*ptr = '_';
}

/*
 * This function returns a file pointer that can be used to send output
 * to. Once the file pointer is closed, the job is submitted.
 */
FILE * fp_submit(char *printer)
{
	char command[BUFSIZ];

	snprintf(command, sizeof (command), "/usr/bin/lp -s -d %s", printer);
	remove_shell_meta_characters(command);

	return (popen(command, "w+"));
}

/*
 * This function submits a file to print
 */
int filename_submit(char *printer, char *file)
{
	char command[BUFSIZ];

	snprintf(command, sizeof (command), "/usr/bin/lp -s -c -d %s %s",
		printer, file);
	remove_shell_meta_characters(command);

	return (system(command));
}

/*
 * sample job submission from an application
 */
main(int ac, char *av[])
{
	char *printer = av[1];
	char *file = av[2];
	FILE *fp;

	filename_submit(printer, file);

	if ((fp = fp_submit(printer)) != NULL) {
		fprintf(fp, "this is printing via a file pointer\n");
		fclose(fp);
	}
}
The examples found in this document can also be found in the PRINTtoys package available for download via the web.