1/*
2
3Copyright 1988, 1993, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27*/
28
29/*
30 * atobm - ascii to bitmap filter
31 * Author:  Jim Fulton, MIT X Consortium
32 */
33
34#ifdef HAVE_CONFIG_H
35# include "config.h"
36#endif
37
38#include <stdio.h>
39#include <ctype.h>
40#include <X11/Xos.h>
41#include <X11/Xfuncproto.h>
42#include <stdlib.h>
43
44static char *ProgramName;
45
46static void doit(FILE *fp, const char *filename, const char *chars,
47		 int xhot, int yhot, const char *name);
48
49static void _X_NORETURN _X_COLD
50usage (const char *msg, int exitval)
51{
52    if (msg)
53	fprintf(stderr, "%s: %s\n", ProgramName, msg);
54    fprintf (stderr, "usage:  %s [-options ...] [filename]\n\n%s\n",
55	     ProgramName,
56             "where options include:\n"
57             "    -chars cc        chars to use for 0 and 1 bits, respectively\n"
58             "    -help            print this message\n"
59             "    -name variable   name to use in bitmap file\n"
60             "    -version         print version info\n"
61             "    -xhot number     x position of hotspot\n"
62             "    -yhot number     y position of hotspot\n");
63    exit(exitval);
64}
65
66static void _X_NORETURN _X_COLD
67missing_arg (const char *option)
68{
69    char msg[32];
70
71    snprintf(msg, sizeof(msg), "%s requires an argument", option);
72    usage(msg, 1);
73}
74
75static char *
76cify_name (char *name)
77{
78    int length = name ? strlen (name) : 0;
79    int i;
80
81    for (i = 0; i < length; i++) {	/* strncpy (result, begin, length); */
82	char c = name[i];
83	if (!((isascii(c) && isalnum(c)) || c == '_')) name[i] = '_';
84    }
85    return name;
86}
87
88static char *
89StripName(char *name)
90{
91  char *begin = strrchr(name, '/');
92  char *end, *result;
93  int length;
94
95  begin = (begin ? begin+1 : name);
96  end = strchr(begin, '.');	/* change to strrchr to allow longer names */
97  length = (end ? (end - begin) : strlen (begin));
98  result = (char *) malloc (length + 1);
99  strncpy (result, begin, length);
100  result [length] = '\0';
101  return (result);
102}
103
104int
105main (int argc, char *argv[])
106{
107    int i;
108    int xhot = -1, yhot = -1;
109    char *filename = NULL;
110    const char *chars = "-#";
111    const char *name = NULL;
112    FILE *fp;
113
114    ProgramName = argv[0];
115
116    for (i = 1; i < argc; i++) {
117	char *arg = argv[i];
118
119	if (arg[0] == '-') {
120	    switch (arg[1]) {
121	      case '\0':
122		filename = NULL;
123		continue;
124	      case 'c':
125		if (++i >= argc) missing_arg("-chars");
126		chars = argv[i];
127		continue;
128	      case 'h':
129		if (strcmp(arg, "-help") == 0) {
130		    usage(NULL, 0);
131		}
132		goto unknown;
133	      case 'n':
134		if (++i >= argc) missing_arg("-name");
135		name = argv[i];
136		continue;
137	      case 'v':
138		if (strcmp(arg, "-version") == 0) {
139		    puts(PACKAGE_STRING);
140		    exit(0);
141		}
142		goto unknown;
143	      case 'x':
144		if (++i >= argc) missing_arg("-xhot");
145		xhot = atoi (argv[i]);
146		continue;
147	      case 'y':
148		if (++i >= argc) missing_arg("-yhot");
149		yhot = atoi (argv[i]);
150		continue;
151	      default:
152	      unknown:
153		fprintf(stderr, "%s: unrecognized option '%s'\n",
154			ProgramName, argv[i]);
155		usage(NULL, 1);
156	    }
157	} else {
158	    filename = arg;
159	}
160    }
161
162    if (strlen (chars) != 2) {
163	fprintf (stderr,
164	 "%s:  bad character list \"%s\", must have exactly 2 characters\n",
165		 ProgramName, chars);
166	exit (1);
167    }
168
169    if (filename) {
170	fp = fopen (filename, "r");
171	if (!fp) {
172	    fprintf (stderr, "%s:  unable to open file \"%s\".\n",
173		     ProgramName, filename);
174	    exit (1);
175	}
176    } else {
177	fp = stdin;
178    }
179
180    if (!name)
181	name = filename ? cify_name (StripName (filename)) : "";
182    doit (fp, filename, chars, xhot, yhot, name);
183
184    if (filename) (void) fclose (fp);
185    exit (0);
186}
187
188struct _scan_list {
189    int allocated;
190    int used;
191    unsigned char *scanlines;
192    struct _scan_list *next;
193};
194
195#define NTOALLOC 16
196static inline struct _scan_list *
197_new_scan_list(int bytes_per_scanline) {
198    struct _scan_list *slist = (struct _scan_list *) calloc (1, sizeof(*slist));
199    if (!slist) {
200	return NULL;
201    }
202    slist->allocated = NTOALLOC * bytes_per_scanline;
203    slist->scanlines = (unsigned char *) calloc(slist->allocated, 1);
204    if (!slist->scanlines) {
205        free(slist);
206        return NULL;
207    }
208    slist->used = 0;
209    slist->next = NULL;
210
211    return slist;
212}
213
214static void
215doit (FILE *fp,
216      const char *filename,
217      const char *chars,
218      int xhot, int yhot,
219      const char *name)
220{
221    int i, j;
222    int last_character;
223    char buf[BUFSIZ];
224    char *cp, *newline;
225    int width = 0, height = 0;
226    int len;
227    int removespace = (((isascii(chars[0]) && isspace(chars[0])) ||
228			(isascii(chars[1]) && isspace(chars[1]))) ? 0 : 1);
229    int lineno = 0;
230    int bytes_per_scanline = 0;
231    struct _scan_list *head = NULL, *slist = NULL;
232    static unsigned char masktable[] = {
233	0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
234    int padded = 0;
235
236    while (1) {
237	buf[0] = '\0';
238	lineno++;
239	if (fgets (buf, sizeof buf, fp) == NULL) break;
240
241	cp = buf;
242	if (removespace) {
243	    for (cp = buf; *cp && isascii(*cp) && isspace(*cp); cp++) ;
244	}
245	if (*cp == '\n' || !*cp) continue;  /* empty line */
246
247	newline = strchr(cp, '\n');
248	if (!newline) {
249	    fprintf (stderr, "%s:  line %d too long.\n",
250		     ProgramName, lineno);
251	    return;
252	}
253
254	if (removespace) {
255	    for (; --newline > cp && isascii(*newline) && isspace(*newline); );
256	    newline++;
257	}
258
259	if (newline == cp) continue;
260
261	*newline = '\0';
262	len = strlen (cp);
263
264	if (head == NULL) {
265	    width = len;
266	    padded = ((width & 7) != 0);
267	    bytes_per_scanline = (len + 7) / 8;
268	    head = slist = _new_scan_list(bytes_per_scanline);
269
270            if (!slist) {
271                fprintf (stderr, "%s:  unable to allocate scan list\n", ProgramName);
272                return;
273            }
274	} else if (width != len) {
275	    fprintf (stderr,
276		     "%s:  line %d is %d characters wide instead of %d\n",
277		     ProgramName, lineno, len, width);
278	    goto bail;
279	}
280
281	if (slist->used + 1 >= slist->allocated) {
282	    struct _scan_list *old = slist;
283	    old->next = slist = _new_scan_list(bytes_per_scanline);
284
285	    if (!slist) {
286	        fprintf (stderr, "%s:  unable to allocate scan list\n", ProgramName);
287		goto bail;
288	    }
289	}
290
291	/* okay, parse the line and stick values into the scanline array */
292	for (i = 0; i < width; i++) {
293	    int ind = (i & 7);
294	    int on = 0;
295
296	    if (cp[i] == chars[1]) {
297		on = 1;
298	    } else if (cp[i] != chars[0]) {
299		fprintf (stderr, "%s:  bad character '%c' on line %d\n",
300			 ProgramName, cp[i], lineno);
301	    }
302
303	    if (on) slist->scanlines[slist->used] |= masktable[ind];
304	    if (ind == 7) slist->used++;
305	}
306	if (padded) slist->used++;
307	height++;
308    }
309
310    printf ("#define %s_width %d\n", name, width);
311    printf ("#define %s_height %d\n", name, height);
312    if (xhot >= 0) printf ("#define %s_x_hot %d\n", name, xhot);
313    if (yhot >= 0) printf ("#define %s_y_hot %d\n", name, yhot);
314    printf ("\n");
315    printf ("static unsigned char %s_bits[] = {\n", name);
316
317    j = 0;
318    last_character = height * bytes_per_scanline - 1;
319    for (slist = head; slist; slist = slist->next) {
320	for (i = 0; i < slist->used; i++) {
321	    printf (" 0x%02x", slist->scanlines[i]);
322	    if (j != last_character) putchar (',');
323	    if ((j % 12) == 11) putchar ('\n');
324	    j++;
325	}
326    }
327    printf (" };\n");
328  bail:
329    for (slist = head; slist != NULL; slist = head) {
330        head = slist->next;
331        free(slist->scanlines);
332        free(slist);
333    }
334    return;
335}
336