1 /*
2  * Fonteditfs - A full-screen console font editor.
3  * Copyright (C) 2002, 2003 Uri Shaked <uri@keves.org>.
4  * Portions of the code were donated by amir shalem <amir@boom.org.il>.
5  * Homepage: http://fonteditfs.sourceforge.net/
6  *
7  * Licensed under the BSD license:
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met:
11  * 1. Redistributions of source code must retain the above copyright notice,
12  *    this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY ITS AUTHORS ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
20  * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
22  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * $Id: fnteditfs.c,v 1.4 2003/09/20 15:17:34 uri Exp $
29  * Main source file.
30  */
31 
32 #include <string.h>
33 #include <signal.h>
34 #include <curses.h>
35 #include <fcntl.h>
36 #include <limits.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <stdio.h>
40 #include <errno.h>
41 #include "font.h"
42 #include "stack.h"
43 
44 #define VERSION "1.2"
45 
46 char *helptext[] = {
47     "Left/Up/Right/Down - move",
48     "Space - Toggle",
49     "W - Wide editing mode",
50     "L - Load file",
51     "S - Save file",
52     "C - Save font to console",
53     "D - Load font from console",
54     "Q - Quit",
55     "R - redraw screen",
56     "P - Push current -> stack",
57     "O - pOp  stack -> current",
58     "PgUp - previous char",
59     "PgDown - next char",
60     "# - jump to specific char"
61 };
62 const int helplines = 14;
63 
64 char *title = "fnteditfs "VERSION" - A Full-Screen Console Font Editor";
65 
66 int charnum = 65;
67 int editx = 0, edity = 0;
68 int wide_editing = 0;
69 
70 WINDOW *helpwin, *editwin, *statusln;
71 char fontbuf[4096];
72 char fnamebuf[_POSIX_PATH_MAX+1];
73 Stack *char_stack;
74 
75 #define edit_line_refresh \
76     prefresh(pad, 0, x / (width - 10) * (width - 10) - 10, top, left, top, left + width)
77 
edit_line(char * buf,int bufsize,int top,int left,int width,int numeric)78 char *edit_line (char *buf, int bufsize, int top, int left, int width, int numeric) {
79     WINDOW *pad = newpad(1, bufsize);
80     int ch, buflen = strlen(buf);
81     int insertmode = 1;
82     int y = 0, x = buflen;
83     if (!pad)
84 	return (char*)0;
85     keypad(pad, 1);
86     wattrset(pad, A_BOLD);
87     wclear(pad);
88     waddstr(pad, buf);
89     edit_line_refresh;
90     while (1) {
91 	getyx(pad, y, x);
92 	ch = wgetch(pad);
93 	switch (ch) {
94         case -1:
95 	    return NULL;
96 	case KEY_IC: // insert
97 	    insertmode = !insertmode;
98 	    break;
99 	case KEY_DC: // delete
100 	    if (buflen == 0) {
101 		beep();
102 		edit_line_refresh;
103 		break;
104 	    }
105 	    if (x == buflen)
106 		wmove(pad, y, --x);
107 	    memcpy(&buf[x], &buf[x+1], buflen - x + 1);
108 	    buf[--buflen] = (char)0;
109 	    waddstr(pad, &buf[x]);
110 	    wclrtoeol(pad);
111 	    wmove(pad, y, x);
112 	    edit_line_refresh;
113 	    break;
114 	case KEY_BACKSPACE: // backspace
115 	    if (buflen == 0 || x == 0) {
116 		beep();
117 		edit_line_refresh;
118 		break;
119 	    }
120 	    if (x == buflen) {
121 		wmove(pad, y, --x);
122         	waddch(pad, ' ');
123 		wmove(pad, y, x);
124         	buflen--;
125 	    } else {
126 		wmove(pad, y, --x);
127 		memcpy(&buf[x], &buf[x+1], buflen - x + 1);
128 		buf[--buflen] = (char)0;
129 		waddstr(pad, &buf[x]);
130 		wclrtoeol(pad);
131 		wmove(pad, y, x);
132 	    }
133             edit_line_refresh;
134 	    break;
135 	case KEY_HOME: // home
136 	    x = 0;
137 	    wmove(pad, y, x);
138             edit_line_refresh;
139 	    break;
140 	case KEY_END: // end
141 	    x = buflen;
142 	    wmove(pad, y, x);
143             edit_line_refresh;
144 	    break;
145 	case KEY_LEFT: // left arrow
146 	    if (!x) {
147 		beep();
148                 edit_line_refresh;
149 		break;
150 	    }
151 	    x--;
152 	    wmove(pad, y, x);
153             edit_line_refresh;
154 	    break;
155 	case KEY_RIGHT: // right arrow
156 	    if (x == buflen) {
157 		beep();
158                 edit_line_refresh;
159 		break;
160 	    }
161 	    x++;
162 	    wmove(pad, y, x);
163             edit_line_refresh;
164 	    break;
165 	case 12: // ^L char
166 	    redrawwin(pad);
167             edit_line_refresh;
168 	    break;
169 	case '\r': // CR
170 	case '\n': // LF (line feed)
171 	    wattrset(pad, 0);
172 	    wmove(pad, 0, 0);
173 	    wclear(pad);
174             edit_line_refresh;
175 	    delwin(pad);
176 	    buf[buflen] = (char)0;
177 	    return (char*)buf;
178 	case 21: // ^U char
179 	    x = 0;
180 	    buflen = 0;
181 	    wclear(pad);
182             edit_line_refresh;
183 	    break;
184 	case 27: // ESC
185 	    wattrset(pad, 0);
186 	    wmove(pad, 0, 0);
187 	    wclear(pad);
188             edit_line_refresh;
189 	    delwin(pad);
190 	    return (char*) 0;
191 	default: // any other key
192 	    if ((ch > 255 || ch < 32) || (numeric && (ch < '0' || ch > '9'))) {
193 		beep();
194                 edit_line_refresh;
195 		break;
196 	    }
197 	    if (buflen >= bufsize - 1) { // end of buffer
198 		beep();
199                 edit_line_refresh;
200 		break;
201 	    }
202 	    if (x == buflen) {
203 	        buf[x] = ch;
204     		waddch(pad, ch);
205         	buflen++;
206 	    } else {
207 	        if (insertmode) {
208 		    memcpy(&buf[x+1], &buf[x], buflen - x);
209 	    	    buf[++buflen] = (char)0;
210 		    buf[x] = ch;
211 	    	    waddstr(pad, &buf[x]);
212 	    	    wmove(pad, 0, ++x);
213 		} else {
214 	    	    buf[x] = ch;
215             	    waddch(pad, ch);
216 		}
217 	    }
218             edit_line_refresh;
219     	}
220     }
221 }
222 
draw_char(WINDOW * win,int num,char buf[])223 void draw_char (WINDOW *win, int num, char buf[]) {
224     int i, ch;
225     wmove(win, 0, 12 / 2 - 5);
226     wprintw(win, wide_editing ? " Char %03d [WIDE] " : " Char %03d ", charnum);
227     for (i = 0; i < 128; i++) {
228 	if (i % 8 == 0)
229 	    wmove(win, i / 8 + 1, 2);
230 	ch = buf[i / 8] & (1 << (7 - i % 8)) ? ACS_BLOCK : ' ';
231 	waddch(win, ch);
232 	if (wide_editing)
233 	    waddch(win, ch);
234     }
235     wmove(win, edity + 1, editx * (wide_editing + 1) + 2);
236 }
237 
toggle_char_bit(WINDOW * win,int num,char * buf,int y,int x,int chwidth)238 void toggle_char_bit(WINDOW *win, int num, char *buf, int y, int x, int chwidth) {
239     int i;
240     buf[num * 16 + y] ^= (1 << (7 - x));
241     wmove(win, y + 1, x * chwidth + 2);
242     for (i = 0; i < chwidth; i++)
243 	waddch(win, buf[num * 16 + y] & (1 << (7 - x)) ? ACS_BLOCK : ' ');
244     wmove(win, y + 1, x * chwidth + 2);
245 }
246 
draw_screen()247 void draw_screen () {
248     int i, len = strlen(title);
249     attrset(A_STANDOUT);
250     move(0, 0);
251     for (i = 0; i < (80 - len) / 2; i++)
252 	addch(' ');
253     addstr(title);
254     for (i = (80 - len) / 2 + len; i < COLS; i++)
255 	addch(' ');
256     helpwin = newwin(helplines + 2, 30, 5, 5);
257     box(helpwin, ACS_VLINE, ACS_HLINE);
258     wmove(helpwin, 0, 30 / 2 - 3);
259     waddstr(helpwin, " Keys ");
260     for (i = 0; i < helplines; i++) {
261 	wmove(helpwin, 1 + i, 2);
262 	waddstr(helpwin, helptext[i]);
263     }
264     editwin = newwin(18, 12, 5, 45);
265     box(editwin, ACS_VLINE, ACS_HLINE);
266     draw_char(editwin, charnum, &fontbuf[charnum * 16]);
267     refresh();
268     statusln = newwin(1, COLS, LINES - 1, 0);
269     wrefresh(helpwin);
270     wrefresh(editwin);
271 }
272 
ask_move_char()273 void ask_move_char () {
274     char buf[4];
275     buf[0] = 0;
276     wclear(statusln);
277     mvwaddstr(statusln, 0, 0, "New character #:");
278     wrefresh(statusln);
279     if (edit_line(buf, sizeof(buf), LINES - 1, 17, 4, 1)) {
280 	charnum = atol(buf) % 256;
281         draw_char(editwin, charnum, &fontbuf[charnum * 16]);
282 	wrefresh(editwin);
283     }
284     wclear(statusln);
285     wrefresh(statusln);
286     wrefresh(editwin);
287 }
288 
interactive_load_file()289 void interactive_load_file () {
290     char buf[sizeof(fnamebuf)];
291     int rc;
292     strcpy(buf, fnamebuf);
293     wclear(statusln);
294     mvwaddstr(statusln, 0, 0, "Load from file:");
295     wrefresh(statusln);
296     if (!edit_line(buf, sizeof(buf), LINES - 1, 16, COLS - 16, 0)) {
297 	wclear(statusln);
298         mvwaddstr(statusln, 0, 0, "Load canceled.");
299         wrefresh(statusln);
300 	wrefresh(editwin);
301 	return;
302     }
303     strcpy(fnamebuf, buf);
304     // first try to load as uuencoded font
305     rc = font_uuload(buf, fontbuf, sizeof(fontbuf));
306     if (rc == -1) // otherwise, try normal load
307 	rc = font_load(buf, fontbuf, sizeof(fontbuf));
308     if (rc) {
309 	wclear(statusln);
310         mvwprintw(statusln, 0, 0, "Load error: %s", font_error);
311         wrefresh(statusln);
312 	wrefresh(editwin);
313 	return;
314     }
315     wclear(statusln);
316     mvwaddstr(statusln, 0, 0, "Font loaded.");
317     wrefresh(statusln);
318     draw_char(editwin, charnum, &fontbuf[charnum * 16]);
319     wrefresh(editwin);
320 }
321 
interactive_save_file()322 void interactive_save_file () {
323     char buf[sizeof(fnamebuf)];
324     int rc;
325     strcpy(buf, fnamebuf);
326     wclear(statusln);
327     mvwaddstr(statusln, 0, 0, "Save to file:");
328     wrefresh(statusln);
329     if (!edit_line(buf, sizeof(buf), LINES - 1, 14, COLS - 14, 0)) {
330 	wclear(statusln);
331         mvwaddstr(statusln, 0, 0, "Save canceled.");
332         wrefresh(statusln);
333 	wrefresh(editwin);
334 	return;
335     }
336     strcpy(fnamebuf, buf);
337     rc = font_save(buf, fontbuf, sizeof(fontbuf));
338     if (rc) {
339 	wclear(statusln);
340         mvwprintw(statusln, 0, 0, "Save error: %s", font_error);
341         wrefresh(statusln);
342 	wrefresh(editwin);
343 	return;
344     }
345     wclear(statusln);
346     mvwaddstr(statusln, 0, 0, "Font saved.");
347     wrefresh(statusln);
348     wrefresh(editwin);
349 }
350 
351 /**********************************/
352 /* operation system depended code */
353 /**********************************/
354 
355 #if defined(__FreeBSD__)
356 
357 #include <sys/ioctl.h>
358 #include <sys/consio.h>
359 
do_ioctl(const char * header,unsigned long request,char * buf)360 int do_ioctl(const char *header, unsigned long request, char *buf) {
361     int fd;
362 
363     if (ioctl(0, request, buf) < 0) {
364 	fd = open("/dev/console", O_RDONLY, 0);
365 	if (fd < 0) {
366 	    wclear(statusln);
367     	    mvwprintw(statusln, 0, 0, "%s failed to open /dev/console: %s", header, strerror(errno));
368 	    wrefresh(statusln);
369 	    wrefresh(editwin);
370 	    return -1;
371 	}
372 	if (ioctl(fd, request, buf) < 0) {
373 	    wclear(statusln);
374     	    mvwprintw(statusln, 0, 0, "%s failed to ioctl /dev/console: %s", header, strerror(errno));
375 	    wrefresh(statusln);
376 	    wrefresh(editwin);
377 	    close(fd);
378 	    return -1;
379 	}
380     }
381     return 0;
382 }
383 
save_font_to_console()384 void save_font_to_console () {
385     if (do_ioctl("Save error:", PIO_FONT8x16, fontbuf) < 0)
386 	return;
387 
388     wclear(statusln);
389     mvwaddstr(statusln, 0, 0, "Font saved to console.");
390     wrefresh(statusln);
391     wrefresh(editwin);
392 }
393 
load_font_from_console()394 void load_font_from_console () {
395     if (do_ioctl("Load error:", GIO_FONT8x16, fontbuf) < 0)
396 	return;
397 
398     wclear(statusln);
399     mvwaddstr(statusln, 0, 0, "Font loaded from console.");
400     wrefresh(statusln);
401     draw_char(editwin, charnum, &fontbuf[charnum * 16]);
402     wrefresh(editwin);
403 }
404 
405 #else
406 
save_font_to_console()407 void save_font_to_console () {
408     wclear(statusln);
409     mvwaddstr(statusln, 0, 0, "No console interaction is supported under this OS");
410     wrefresh(statusln);
411     wrefresh(editwin);
412 }
413 
load_font_from_console()414 void load_font_from_console () {
415     save_font_to_console();
416 }
417 
418 #endif /* OS is not supported */
419 
420 /*****************************************/
421 /* end of operation system depended code */
422 /*****************************************/
423 
input_loop()424 void input_loop () {
425     char *tmp;
426     int c;
427     while (1) {
428 	c = getch();
429 	switch (c) {
430 	    case KEY_UP:
431 		edity ? edity -- : (edity = 15);
432 		wmove(editwin, edity+1, editx*(wide_editing + 1)+2);
433 		wrefresh(editwin);
434 		break;
435 	    case KEY_LEFT:
436 		editx ? editx -- : (editx = 7);
437 		wmove(editwin, edity+1, editx*(wide_editing + 1)+2);
438 		wrefresh(editwin);
439 		break;
440 	    case KEY_DOWN:
441 		edity ++; edity %= 16;
442 		wmove(editwin, edity+1, editx*(wide_editing + 1)+2);
443 		wrefresh(editwin);
444 		break;
445 	    case KEY_RIGHT:
446 		editx ++; editx %= 8;
447 		wmove(editwin, edity+1, editx*(wide_editing + 1)+2);
448 		wrefresh(editwin);
449 		break;
450 	    case KEY_PPAGE:
451 		charnum ? charnum-- : (charnum = 255);
452 	        draw_char(editwin, charnum, &fontbuf[charnum * 16]);
453 		wrefresh(editwin);
454 		break;
455 	    case KEY_NPAGE:
456 		charnum++;
457 		charnum %= 256;
458 	        draw_char(editwin, charnum, &fontbuf[charnum * 16]);
459 		wrefresh(editwin);
460 		break;
461 	    case '#':
462 		ask_move_char();
463 		break;
464 	    case ' ':
465 		toggle_char_bit(editwin, charnum, fontbuf, edity, editx, wide_editing + 1);
466 		wrefresh(editwin);
467 		break;
468 	    case 'l': case 'L':
469 		interactive_load_file();
470 		break;
471 	    case 's': case 'S':
472 		interactive_save_file();
473 		break;
474 	    case 'c': case 'C':
475 		save_font_to_console();
476 		break;
477 	    case 'd': case 'D':
478 		load_font_from_console();
479 		break;
480 	    case 'p': case 'P':
481 		tmp = (char*)malloc(16);
482 		memcpy(tmp, &fontbuf[charnum * 16], 16);
483 		stack_push(char_stack, tmp);
484 	        wclear(statusln);
485 	        mvwprintw(statusln, 0, 0, "Character %d pushed to stack (new stack size %d).",
486 			charnum, stack_size(char_stack));
487 	        wrefresh(statusln);
488 		break;
489 	    case 'o': case 'O':
490 		tmp = (char*)stack_pop(char_stack);
491 	        wclear(statusln);
492 		if (!tmp) {
493 		    mvwprintw(statusln, 0, 0, "Stack is already empty, can't pop.",
494 			charnum, stack_size(char_stack));
495 		    wrefresh(statusln);
496 		    break;
497 		}
498 		memcpy(&fontbuf[charnum * 16], tmp, 16);
499 	        draw_char(editwin, charnum, &fontbuf[charnum * 16]);
500 	        wrefresh(editwin);
501 	        mvwprintw(statusln, 0, 0, "Character %d poped from stack (new stack size %d).",
502 			charnum, stack_size(char_stack));
503 	        wrefresh(statusln);
504 		break;
505 	    case 'w': case 'W':
506 		wide_editing = !wide_editing;
507 		delwin(editwin);
508 		editwin = newwin(18, wide_editing ? 20 : 12, 5, 45);
509 		box(editwin, ACS_VLINE, ACS_HLINE);
510 	        draw_char(editwin, charnum, &fontbuf[charnum * 16]);
511 		if (wide_editing) {
512 		    wrefresh(editwin);
513 		    break;
514 		}
515 	    case 'r': case 'R':
516 		redrawwin(stdscr);
517 		redrawwin(helpwin);
518 		redrawwin(editwin);
519 		refresh();
520 		wrefresh(helpwin);
521 	        wrefresh(editwin);
522 		break;
523 	    case 'q': case 'Q':
524 		return;
525 	}
526     }
527 }
528 
print_usage(char * argv[])529 void print_usage (char *argv[]) {
530     printf("Full screen font editor v" VERSION " by Uri Shaked <uri@keves.org>\n");
531     printf("Usage: %s fontfile\n", argv[0]);
532     exit(1);
533 }
534 
main(int argc,char * argv[])535 int main (int argc, char *argv[]) {
536     int rc;
537     if (argc > 2)
538 	print_usage(argv);
539     memset(fontbuf, 0, sizeof(fontbuf));
540     if (argc == 2) {
541         strcpy(fnamebuf, argv[1]);
542 	rc = font_uuload(argv[1], fontbuf, sizeof(fontbuf));
543 	if (rc == -1)
544 	    rc = font_load(argv[1], fontbuf, sizeof(fontbuf));
545 	if (rc) {
546 	    printf("Load font: %s: %s", argv[1], font_error);
547 	    return 1;
548 	}
549     } else
550 	fnamebuf[0] = 0;
551     initscr();
552     cbreak();
553     noecho();
554     intrflush(stdscr,FALSE);
555     keypad(stdscr, TRUE);
556     nonl();
557     char_stack = stack_init();
558     draw_screen();
559     // if no font file loaded, try loading console font.
560     if (fnamebuf[0] == 0)
561         load_font_from_console();
562     input_loop();
563     endwin();
564     return 0;
565 }
566