1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * Copyright (c) 2018, Joyent, Inc.
29 */
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <unistd.h>
34#include <string.h>
35#include <sys/ioctl.h>
36#include <sys/prnio.h>
37#include <fcntl.h>
38
39#define	COMMAND_SET_MAX	16	/* more than 16 command sets is not likely */
40#define	NP(x)	(x ? x : "")
41
42typedef struct {
43	char *manufacturer;
44	char *model;
45	char *description;
46	char *class;
47	char *command_set[COMMAND_SET_MAX];
48} printer_description_t;
49
50int
51get_printer_description(char *path, printer_description_t *info)
52{
53	int fd, rc;
54	struct prn_1284_device_id id;
55	char buf[BUFSIZ];
56	char *s, *iter = NULL;
57
58	/* open the device */
59	if ((fd = open(path, O_RDWR)) < 0)
60		return (fd);
61
62	/* get the 1284 device id */
63	memset(&id, 0, sizeof (id));
64	memset(&buf, 0, sizeof (buf));
65	id.id_len = sizeof (buf);
66	id.id_data = buf;
67
68	rc = ioctl(fd, PRNIOC_GET_1284_DEVID, &id);
69	/* close(fd); */
70	if (rc < 0)
71		return (rc);
72
73	memset(info, 0, sizeof (*info));
74
75	/* parse the 1284 device id string */
76	for (s = (char *)strtok_r(buf, ";\n", &iter); s != NULL;
77			s = (char *)strtok_r(NULL, ";\n", &iter)) {
78		char *t, *u, *iter2 = NULL;
79
80		if ((t = (char *)strtok_r(s, ":\n", &iter2)) == NULL)
81			continue;
82
83		if ((u = (char *)strtok_r(NULL, ":\n", &iter2)) == NULL)
84			continue;
85
86		if ((strcasecmp(t, "MFG") == 0) ||
87		    (strcasecmp(t, "MANUFACTURER") == 0))
88			info->manufacturer = strdup(u);
89		else if ((strcasecmp(t, "MDL") == 0) ||
90		    (strcasecmp(t, "MODEL") == 0))
91			info->model = strdup(u);
92		else if ((strcasecmp(t, "DES") == 0) ||
93		    (strcasecmp(t, "DESCRIPTION") == 0))
94			info->description = strdup(u);
95		else if ((strcasecmp(t, "CLS") == 0) ||
96		    (strcasecmp(t, "CLASS") == 0))
97			info->class = strdup(u);
98		else if ((strcasecmp(t, "CMD") == 0) ||
99		    (strcasecmp(t, "COMMAND SET") == 0)) {
100			/* this should be more dynamic, I got lazy */
101			char *v, *iter3 = NULL;
102			int i = 0;
103
104			for (v = (char *)strtok_r(u, ",\n", &iter3);
105				((v != NULL) && (i < COMMAND_SET_MAX));
106			v = (char *)strtok_r(NULL, ",\n", &iter3))
107				info->command_set[i++] = strdup(v);
108		}
109	}
110
111	return (0);
112}
113
114static void
115usage(char *name)
116{
117	char *program;
118
119	if ((program = strrchr(name, '/')) == NULL)
120		program = name;
121	else
122		program++;
123
124	printf("Usage: %s [-aMmdCc] (path) ...\n", program);
125}
126
127int
128main(int ac, char *av[])
129{
130	int rc;
131	int manufacturer = 0, model = 0, description = 0, command_set = 0,
132	    class = 0;
133
134	while ((rc = getopt(ac, av, "aMmdCc")) != EOF)
135		switch (rc) {
136		case 'a':
137			manufacturer++;
138			model++;
139			description++;
140			command_set++;
141			class++;
142			break;
143		case 'M':
144			manufacturer++;
145			break;
146		case 'm':
147			model++;
148			break;
149		case 'd':
150			description++;
151			break;
152		case 'C':
153			command_set++;
154			break;
155		case 'c':
156			class++;
157			break;
158		default:
159			usage(av[0]);
160			exit(1);
161		}
162
163	if (optind >= ac) {
164		usage(av[0]);
165		exit(1);
166	}
167
168	while (optind < ac) {
169		char *path = av[optind++];
170		printer_description_t info;
171
172		rc = get_printer_description(path, &info);
173		if (rc == 0) {
174			printf("%s:\n", path);
175			if (manufacturer != 0)
176				printf("\tManufacturer: %s\n",
177						NP(info.manufacturer));
178			if (model != 0)
179				printf("\tModel:        %s\n",
180						NP(info.model));
181			if (description != 0)
182				printf("\tDescription:  %s\n",
183						NP(info.description));
184			if (class != 0)
185				printf("\tClass:        %s\n",
186						NP(info.class));
187			if (command_set != 0) {
188				int i;
189
190				printf("\tCommand set:\n");
191				for (i = 0; info.command_set[i] != NULL; i++)
192					printf("\t\tcmd[%d]: %s\n", i,
193						info.command_set[i]);
194			}
195		} else
196			perror(path);
197	}
198	return (rc);
199}
200