blob: 3ea619306a84b9c91df254a93e644c4ee4c64a66 [file] [log] [blame]
Steve Kondikae271bc2015-11-15 02:50:53 +01001/****************************************************************************
micky3879b9f5e72025-07-08 18:04:53 -04002 * Copyright 2018-2021,2022 Thomas E. Dickey *
3 * Copyright 2006-2017,2018 Free Software Foundation, Inc. *
Steve Kondikae271bc2015-11-15 02:50:53 +01004 * *
5 * Permission is hereby granted, free of charge, to any person obtaining a *
6 * copy of this software and associated documentation files (the *
7 * "Software"), to deal in the Software without restriction, including *
8 * without limitation the rights to use, copy, modify, merge, publish, *
9 * distribute, distribute with modifications, sublicense, and/or sell *
10 * copies of the Software, and to permit persons to whom the Software is *
11 * furnished to do so, subject to the following conditions: *
12 * *
13 * The above copyright notice and this permission notice shall be included *
14 * in all copies or substantial portions of the Software. *
15 * *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
23 * *
24 * Except as contained in this notice, the name(s) of the above copyright *
25 * holders shall not be used in advertising or otherwise to promote the *
26 * sale, use or other dealings in this Software without prior written *
27 * authorization. *
28 ****************************************************************************/
29/*
micky3879b9f5e72025-07-08 18:04:53 -040030 * $Id: savescreen.c,v 1.62 2022/12/10 23:23:27 tom Exp $
Steve Kondikae271bc2015-11-15 02:50:53 +010031 *
32 * Demonstrate save/restore functions from the curses library.
33 * Thomas Dickey - 2007/7/14
34 */
35
micky3879b9f5e72025-07-08 18:04:53 -040036#define NEED_TIME_H
Steve Kondikae271bc2015-11-15 02:50:53 +010037#include <test.priv.h>
micky3879b9f5e72025-07-08 18:04:53 -040038#include <popup_msg.h>
39#include <parse_rgb.h>
Steve Kondikae271bc2015-11-15 02:50:53 +010040
41#if HAVE_SCR_DUMP
42
43#include <sys/types.h>
44#include <sys/stat.h>
45
micky3879b9f5e72025-07-08 18:04:53 -040046#if defined(__hpux)
47#define MyMarker 'X'
Steve Kondikae271bc2015-11-15 02:50:53 +010048#else
micky3879b9f5e72025-07-08 18:04:53 -040049#define MyMarker ACS_DIAMOND
Steve Kondikae271bc2015-11-15 02:50:53 +010050#endif
51
micky3879b9f5e72025-07-08 18:04:53 -040052#define MAX_ANSI 8
53
Steve Kondikae271bc2015-11-15 02:50:53 +010054static bool use_init = FALSE;
55static bool keep_dumps = FALSE;
56
micky3879b9f5e72025-07-08 18:04:53 -040057#if USE_WIDEC_SUPPORT
58/* In HPUX curses, cchar_t is opaque; other implementations are not */
59static wchar_t
60BaseChar(cchar_t data)
61{
62 wchar_t my_wchar[CCHARW_MAX];
63 wchar_t result = 0;
64 attr_t my_attr;
65 short my_pair;
66 if (getcchar(&data, my_wchar, &my_attr, &my_pair, NULL) == OK)
67 result = my_wchar[0];
68 return result;
69}
70#endif
71
Steve Kondikae271bc2015-11-15 02:50:53 +010072static int
73fexists(const char *name)
74{
75 struct stat sb;
76 return (stat(name, &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFREG);
77}
78
79static void
80setup_next(void)
81{
82 curs_set(1);
83 reset_shell_mode();
84}
85
86static void
87cleanup(char *files[])
88{
Steve Kondikae271bc2015-11-15 02:50:53 +010089 if (!keep_dumps) {
micky3879b9f5e72025-07-08 18:04:53 -040090 int n;
91
Steve Kondikae271bc2015-11-15 02:50:53 +010092 for (n = 0; files[n] != 0; ++n) {
93 unlink(files[n]);
94 }
95 }
96}
97
98static int
99load_screen(char *filename)
100{
101 int result;
102
103 if (use_init) {
104 if ((result = scr_init(filename)) != ERR)
105 result = scr_restore(filename);
106 } else {
107 result = scr_set(filename);
108 }
109 return result;
110}
111
112/*
113 * scr_restore() or scr_set() operates on curscr. If we read a character using
114 * getch() that will refresh stdscr, wiping out the result. To avoid that,
115 * copy the data back from curscr to stdscr.
116 */
117static void
118after_load(void)
119{
120 overwrite(curscr, stdscr);
121 doupdate();
122}
123
124static void
micky3879b9f5e72025-07-08 18:04:53 -0400125show_what(int color, int which, int last)
Steve Kondikae271bc2015-11-15 02:50:53 +0100126{
127 int y, x, n;
128 time_t now;
129 char *mytime;
130
131 getyx(stdscr, y, x);
132
133 move(0, 0);
micky3879b9f5e72025-07-08 18:04:53 -0400134 printw("Color %d. Saved %d of %d (? for help)", color, which, last + 1);
Steve Kondikae271bc2015-11-15 02:50:53 +0100135
136 now = time((time_t *) 0);
137 mytime = ctime(&now);
138 for (n = (int) strlen(mytime) - 1; n >= 0; --n) {
139 if (isspace(UChar(mytime[n]))) {
140 mytime[n] = '\0';
141 } else {
142 break;
143 }
144 }
145 mvprintw(0, (COLS - n - 2), " %s", mytime);
146
147 move(y, x);
148
149 refresh();
150}
151
152static int
micky3879b9f5e72025-07-08 18:04:53 -0400153get_command(int color, int which, int last)
Steve Kondikae271bc2015-11-15 02:50:53 +0100154{
155 int ch;
156
157 timeout(50);
158
159 do {
micky3879b9f5e72025-07-08 18:04:53 -0400160 show_what(color, which, last);
Steve Kondikae271bc2015-11-15 02:50:53 +0100161 ch = getch();
162 } while (ch == ERR);
163
164 return ch;
165}
166
micky3879b9f5e72025-07-08 18:04:53 -0400167static int
168dump_screen(char **files, int color, int which, int last, bool use_colors)
Steve Kondikae271bc2015-11-15 02:50:53 +0100169{
micky3879b9f5e72025-07-08 18:04:53 -0400170#if USE_WIDEC_SUPPORT
171 cchar_t mycc;
172#endif
173 char *filename = files[which];
174 bool dumped = FALSE;
Steve Kondikae271bc2015-11-15 02:50:53 +0100175
micky3879b9f5e72025-07-08 18:04:53 -0400176 if (filename != 0) {
177 dumped = TRUE;
178 show_what(color, ++which, last);
179 if (scr_dump(filename) == ERR) {
180 endwin();
181 printf("Cannot write screen-dump %s\n", filename);
182 cleanup(files);
183 ExitProgram(EXIT_SUCCESS);
184 }
185 if (use_colors) {
186 int cx, cy;
187 int pair = 1 + (which % MAX_ANSI);
188 /*
189 * Change the background color, to make it more obvious. But that
190 * changes the existing text-color. Copy the old values from the
191 * currently displayed screen.
192 */
193 bkgd((chtype) COLOR_PAIR(pair));
194 for (cy = 1; cy < LINES; ++cy) {
195 for (cx = 0; cx < COLS; ++cx) {
196 wmove(curscr, cy, cx);
197 wmove(stdscr, cy, cx);
198#if USE_WIDEC_SUPPORT
199 if (win_wch(curscr, &mycc) != ERR) {
200 int myxx = wcwidth(BaseChar(mycc));
201 if (myxx > 0) {
202 wadd_wchnstr(stdscr, &mycc, 1);
203 cx += (myxx - 1);
204 }
205 }
206#else
207 waddch(stdscr, winch(curscr));
208#endif
209 }
210 }
211 }
Steve Kondikae271bc2015-11-15 02:50:53 +0100212 }
micky3879b9f5e72025-07-08 18:04:53 -0400213 return dumped;
Steve Kondikae271bc2015-11-15 02:50:53 +0100214}
215
216static void
217editor_help(void)
218{
219 static const char *msgs[] =
220 {
221 "You are now in the screen-editor, which allows you to make some",
222 "lines on the screen, as well as save copies of the screen to a",
223 "temporary file",
224 "",
225 "Keys:",
226 " q quit",
227 " n run the screen-loader to show the saved screens",
228 " <space> dump a screen",
229 "",
230 " a toggle between '#' and graphic symbol for drawing",
231 " c change color drawn by line to next in palette",
232 " h,j,k,l or arrows to move around the screen, drawing",
micky3879b9f5e72025-07-08 18:04:53 -0400233 0
Steve Kondikae271bc2015-11-15 02:50:53 +0100234 };
micky3879b9f5e72025-07-08 18:04:53 -0400235 popup_msg(stdscr, msgs);
Steve Kondikae271bc2015-11-15 02:50:53 +0100236}
237
238static void
239replay_help(void)
240{
241 static const char *msgs[] =
242 {
243 "You are now in the screen-loader, which allows you to view",
244 "the dumped/restored screens.",
245 "",
246 "Keys:",
247 " q quit",
248 " <space> load the next screen",
249 " <backspace> load the previous screen",
micky3879b9f5e72025-07-08 18:04:53 -0400250 0
Steve Kondikae271bc2015-11-15 02:50:53 +0100251 };
micky3879b9f5e72025-07-08 18:04:53 -0400252 popup_msg(stdscr, msgs);
Steve Kondikae271bc2015-11-15 02:50:53 +0100253}
254
255static void
micky3879b9f5e72025-07-08 18:04:53 -0400256usage(int ok)
Steve Kondikae271bc2015-11-15 02:50:53 +0100257{
258 static const char *msg[] =
259 {
micky3879b9f5e72025-07-08 18:04:53 -0400260 "Usage: savescreen [-r] files"
261 ,""
262 ,USAGE_COMMON
263 ,"Options:"
264 ," -f file fill/initialize screen using text from this file"
265 ," -i use scr_init/scr_restore rather than scr_set"
266 ," -k keep the restored dump-files rather than removing them"
267 ," -r replay the screen-dump files"
Steve Kondikae271bc2015-11-15 02:50:53 +0100268 };
269 unsigned n;
270 for (n = 0; n < SIZEOF(msg); ++n) {
271 fprintf(stderr, "%s\n", msg[n]);
272 }
micky3879b9f5e72025-07-08 18:04:53 -0400273 ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
Steve Kondikae271bc2015-11-15 02:50:53 +0100274}
micky3879b9f5e72025-07-08 18:04:53 -0400275/* *INDENT-OFF* */
276VERSION_COMMON()
277/* *INDENT-ON* */
Steve Kondikae271bc2015-11-15 02:50:53 +0100278
279int
280main(int argc, char *argv[])
281{
282 int ch;
283 int which = 0;
284 int last;
micky3879b9f5e72025-07-08 18:04:53 -0400285 bool use_colors = FALSE;
Steve Kondikae271bc2015-11-15 02:50:53 +0100286 bool replaying = FALSE;
287 bool done = FALSE;
288 char **files;
289 char *fill_by = 0;
290#if USE_WIDEC_SUPPORT
291 cchar_t mycc;
micky3879b9f5e72025-07-08 18:04:53 -0400292 static const wchar_t mywc[2] =
293 {L'#', 0};
Steve Kondikae271bc2015-11-15 02:50:53 +0100294#endif
295
296 setlocale(LC_ALL, "");
297
micky3879b9f5e72025-07-08 18:04:53 -0400298 while ((ch = getopt(argc, argv, OPTS_COMMON "f:ikr")) != -1) {
Steve Kondikae271bc2015-11-15 02:50:53 +0100299 switch (ch) {
300 case 'f':
301 fill_by = optarg;
302 break;
303 case 'i':
304 use_init = TRUE;
305 break;
306 case 'k':
307 keep_dumps = TRUE;
308 break;
309 case 'r':
310 replaying = TRUE;
311 break;
micky3879b9f5e72025-07-08 18:04:53 -0400312 case OPTS_VERSION:
313 show_version(argv);
314 ExitProgram(EXIT_SUCCESS);
Steve Kondikae271bc2015-11-15 02:50:53 +0100315 default:
micky3879b9f5e72025-07-08 18:04:53 -0400316 usage(ch == OPTS_USAGE);
317 /* NOTREACHED */
Steve Kondikae271bc2015-11-15 02:50:53 +0100318 }
319 }
320
321 files = argv + optind;
322 last = argc - optind - 1;
323
324 if (replaying) {
325 while (last >= 0 && !fexists(files[last]))
326 --last;
327 }
328
329 initscr();
330 cbreak();
331 noecho();
332 keypad(stdscr, TRUE);
333 curs_set(0);
Steve Kondikae271bc2015-11-15 02:50:53 +0100334
micky3879b9f5e72025-07-08 18:04:53 -0400335 if (has_colors() && (start_color() == OK) && COLORS >= MAX_ANSI) {
336#if USE_WIDEC_SUPPORT
337 bool using_rgb = FALSE;
338#endif
339 static const struct {
340 int fg, bg;
341 } table[MAX_ANSI] = {
342#define DATA(fg,bg) { COLOR_##fg, COLOR_##bg }
343 DATA(RED, WHITE),
344 DATA(GREEN, WHITE),
345 DATA(YELLOW, BLACK),
346 DATA(BLUE, WHITE),
347 DATA(MAGENTA, WHITE),
348 DATA(MAGENTA, BLACK),
349 DATA(CYAN, WHITE),
350 DATA(CYAN, BLACK),
351#undef DATA
352 };
353 int n;
354 int pair = 1;
355
356 use_colors = TRUE;
Steve Kondikae271bc2015-11-15 02:50:53 +0100357 /*
micky3879b9f5e72025-07-08 18:04:53 -0400358 * Discounting color-pair 0 (no color), make the next 8 color pairs
359 * useful for leaving a visually distinct trail of characters on the
360 * screen.
Steve Kondikae271bc2015-11-15 02:50:53 +0100361 */
micky3879b9f5e72025-07-08 18:04:53 -0400362 for (n = 0; n < MAX_ANSI; ++n) {
363 init_pair((short) pair++, (short) table[n].fg, (short) table[n].bg);
364 }
365 /*
366 * After that, use color pairs for constructing a test-pattern, e.g.,
367 * imitating xterm's scripts.
368 */
369 if (fill_by == 0) {
370 if (COLORS <= 256) {
371 for (n = 0; n < COLORS; ++n)
372 init_pair((short) (n + MAX_ANSI), (short) n, (short) n);
373 }
374#if HAVE_TIGETSTR && USE_WIDEC_SUPPORT
375 else {
376 int r_max, g_max, b_max;
377
378 if (parse_rgb(&r_max, &g_max, &b_max) > 0) {
379 int rows = LINES - 1;
380 int cols = COLS - 1;
381 int b_delta = (b_max / rows);
382 int r_delta = (r_max / cols);
383 int g_delta = (g_max / cols);
384 int row = 0;
385 int b = 0;
386
387 using_rgb = TRUE;
388 while (row++ < rows) {
389 int col = 0;
390 int r = 0;
391 int g = g_max;
392 while (col++ < cols) {
393 int color = (((r * (g_max + 1)) + g) * (b_max + 1)
394 + b + MAX_ANSI);
395#if USE_EXTENDED_COLOR
396 init_extended_pair(pair, color, color);
397#else
398 init_pair(pair, color, color);
399#endif
400 pair++;
401 r += r_delta;
402 g -= g_delta;
403 }
404 b += b_delta;
405 }
406 }
407 }
408#endif
409 }
410 if ((fill_by == 0) && !replaying) {
411#if USE_WIDEC_SUPPORT
412 int cube = 0;
413#endif
414 /*
415 * Originally (before wide-characters) ncurses supported 16 colors.
416 */
417 if (COLORS >= 16 && COLORS <= 256) {
418 mvprintw(2, 0, "System colors:\n");
419 for (n = 0; n < 16; ++n) {
420 pair = n + MAX_ANSI;
421 addch((chtype) (' ' | COLOR_PAIR(pair)));
422 addch((chtype) (' ' | COLOR_PAIR(pair)));
423 if (((n + 1) % 8) == 0)
424 addch('\n');
425 }
426 }
427 /*
428 * Even with ncurses, you need wide-character support to have more
429 * than 16 colors.
430 */
431#if USE_WIDEC_SUPPORT
432 if (COLORS == 88) {
433 cube = 4;
434 } else if (COLORS == 256) {
435 cube = 6;
436 }
437 if (cube != 0) {
438 int r, g, b;
439 int cube0 = 16;
440 int cube1 = cube0 + (cube * cube * cube);
441
442 addch('\n');
443 printw("Color cube, %dx%dx%d:\n", cube, cube, cube);
444 for (g = 0; g < cube; g++) {
445 for (r = 0; r < cube; r++) {
446 for (b = 0; b < cube; b++) {
447 pair = MAX_ANSI
448 + 16
449 + (r * cube * cube) + (g * cube) + b;
450 setcchar(&mycc, mywc, 0, (short) pair, NULL);
451 add_wch(&mycc);
452 add_wch(&mycc);
453 }
454 addch(' ');
455 }
456 addch('\n');
457 }
458 addch('\n');
459 printw("Grayscale ramp:\n");
460 for (n = cube1; n < COLORS; ++n) {
461 pair = n + MAX_ANSI;
462 setcchar(&mycc, mywc, 0, (short) pair, NULL);
463 add_wch(&mycc);
464 add_wch(&mycc);
465 }
466 } else if ((COLORS > 256) && using_rgb) {
467 int rows = LINES - 1;
468 int cols = COLS - 1;
469 int row = 0;
470
471 pair = MAX_ANSI;
472 while (row++ < rows) {
473 int col = 0;
474 while (col++ < cols) {
475 setcchar(&mycc, mywc, 0, (short) pair, &pair);
476 add_wch(&mycc);
477 ++pair;
478 }
479 addch('\n');
480 }
481 addch('\n');
482 }
483#endif
Steve Kondikae271bc2015-11-15 02:50:53 +0100484 }
485 }
486
487 if (fill_by != 0) {
488 FILE *fp = fopen(fill_by, "r");
489 if (fp != 0) {
490 bool filled = FALSE;
491 move(1, 0);
492 while ((ch = fgetc(fp)) != EOF) {
493 if (addch(UChar(ch)) == ERR) {
494 filled = TRUE;
495 break;
496 }
497 }
498 fclose(fp);
499 if (!filled) {
500 while (addch(' ') != ERR) {
501 ;
502 }
503 }
504 move(0, 0);
505 } else {
micky3879b9f5e72025-07-08 18:04:53 -0400506 stop_curses();
Steve Kondikae271bc2015-11-15 02:50:53 +0100507 fprintf(stderr, "Cannot open \"%s\"\n", fill_by);
508 ExitProgram(EXIT_FAILURE);
509 }
510 }
511
512 if (replaying) {
513
514 /*
515 * Use the last file as the initial/current screen.
516 */
517 if (last < 0) {
micky3879b9f5e72025-07-08 18:04:53 -0400518 stop_curses();
Steve Kondikae271bc2015-11-15 02:50:53 +0100519 printf("No screen-dumps given\n");
520 ExitProgram(EXIT_FAILURE);
521 }
522
523 which = last;
524 if (load_screen(files[which]) == ERR) {
micky3879b9f5e72025-07-08 18:04:53 -0400525 stop_curses();
Steve Kondikae271bc2015-11-15 02:50:53 +0100526 printf("Cannot load screen-dump %s\n", files[which]);
527 ExitProgram(EXIT_FAILURE);
528 }
529 after_load();
530
531 while (!done && (ch = getch()) != ERR) {
532 switch (ch) {
533 case 'n':
534 /*
535 * If we got a "next" here, skip to the final screen before
536 * moving to the next process.
537 */
538 setup_next();
539 which = last;
540 done = TRUE;
541 break;
542 case 'q':
543 cleanup(files);
544 done = TRUE;
545 break;
546 case KEY_BACKSPACE:
547 case '\b':
548 if (--which < 0)
549 which = last;
550 break;
551 case ' ':
552 if (++which > last)
553 which = 0;
554 break;
micky3879b9f5e72025-07-08 18:04:53 -0400555 case HELP_KEY_1:
Steve Kondikae271bc2015-11-15 02:50:53 +0100556 replay_help();
557 break;
558 default:
559 beep();
560 continue;
561 }
562
563 if (ch == 'q') {
564 ;
565 } else if (scr_restore(files[which]) == ERR) {
566 endwin();
567 printf("Cannot load screen-dump %s\n", files[which]);
568 cleanup(files);
569 ExitProgram(EXIT_FAILURE);
570 } else {
571 wrefresh(curscr);
572 }
573 }
574 endwin();
575 } else {
576 int y = 0;
577 int x = 0;
578 int color = 0;
579 int altchars = 0;
micky3879b9f5e72025-07-08 18:04:53 -0400580 bool dirty = use_colors || (fill_by != 0);
Steve Kondikae271bc2015-11-15 02:50:53 +0100581
582 while (!done) {
micky3879b9f5e72025-07-08 18:04:53 -0400583 switch (get_command(color, which, last)) {
Steve Kondikae271bc2015-11-15 02:50:53 +0100584 case 'n':
micky3879b9f5e72025-07-08 18:04:53 -0400585 if (dirty && files[which]) {
586 dump_screen(files, color, which, last, use_colors);
587 }
Steve Kondikae271bc2015-11-15 02:50:53 +0100588 setup_next();
589 done = TRUE;
590 break;
591 case 'q':
592 cleanup(files);
593 done = TRUE;
594 break;
595 case ' ':
micky3879b9f5e72025-07-08 18:04:53 -0400596 if (dump_screen(files, color, which, last, use_colors)) {
597 which = (which + 1) % MAX_ANSI;
598 dirty = FALSE;
Steve Kondikae271bc2015-11-15 02:50:53 +0100599 } else {
micky3879b9f5e72025-07-08 18:04:53 -0400600 setup_next();
601 done = TRUE;
Steve Kondikae271bc2015-11-15 02:50:53 +0100602 }
603 break;
604 case KEY_LEFT:
605 case 'h':
606 if (--x < 0)
607 x = COLS - 1;
608 break;
609 case KEY_DOWN:
610 case 'j':
611 if (++y >= LINES)
612 y = 1;
613 break;
614 case KEY_UP:
615 case 'k':
616 if (--y < 1)
617 y = LINES - 1;
618 break;
619 case KEY_RIGHT:
620 case 'l':
621 if (++x >= COLS)
622 x = 0;
623 break;
624 case 'a':
625 altchars = !altchars;
626 break;
627 case 'c':
micky3879b9f5e72025-07-08 18:04:53 -0400628 if (use_colors) {
629 color = (color + 1) % MAX_ANSI;
630 }
Steve Kondikae271bc2015-11-15 02:50:53 +0100631 break;
micky3879b9f5e72025-07-08 18:04:53 -0400632 case HELP_KEY_1:
Steve Kondikae271bc2015-11-15 02:50:53 +0100633 editor_help();
634 break;
635 default:
636 beep();
637 continue;
638 }
639 if (!done) {
micky3879b9f5e72025-07-08 18:04:53 -0400640 chtype attr = A_REVERSE;
641 chtype ch2 = (altchars ? MyMarker : '#');
642 if (use_colors) {
643 attr |= (chtype) COLOR_PAIR(color);
644 }
Steve Kondikae271bc2015-11-15 02:50:53 +0100645 move(y, x);
micky3879b9f5e72025-07-08 18:04:53 -0400646 AddCh(ch2 | attr);
Steve Kondikae271bc2015-11-15 02:50:53 +0100647 move(y, x);
micky3879b9f5e72025-07-08 18:04:53 -0400648 dirty = TRUE;
Steve Kondikae271bc2015-11-15 02:50:53 +0100649 }
650 }
651 endwin();
652 }
653 ExitProgram(EXIT_SUCCESS);
654}
655
656#else
657int
micky3879b9f5e72025-07-08 18:04:53 -0400658main(void)
Steve Kondikae271bc2015-11-15 02:50:53 +0100659{
660 printf("This program requires the screen-dump functions\n");
661 ExitProgram(EXIT_FAILURE);
662}
663#endif