]> ncurses.scripts.mit.edu Git - ncurses.git/blob - ncurses/base/lib_screen.c
ncurses 6.4 - patch 20240420
[ncurses.git] / ncurses / base / lib_screen.c
1 /****************************************************************************
2  * Copyright 2019-2021,2023 Thomas E. Dickey                                *
3  * Copyright 1998-2017,2018 Free Software Foundation, Inc.                  *
4  *                                                                          *
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
30 /****************************************************************************
31  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
32  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
33  *     and: Thomas E. Dickey                        1996 on                 *
34  *     and: Juergen Pfeifer                         2009                    *
35  ****************************************************************************/
36
37 #include <curses.priv.h>
38
39 #include <ctype.h>
40
41 #ifndef CUR
42 #define CUR SP_TERMTYPE
43 #endif
44
45 MODULE_ID("$Id: lib_screen.c,v 1.105 2023/04/28 20:58:54 tom Exp $")
46
47 #define MAX_SIZE 0x3fff         /* 16k is big enough for a window or pad */
48
49 #define MARKER '\\'
50 #define APPEND '+'
51 #define GUTTER '|'
52 #define L_CURL '{'
53 #define R_CURL '}'
54
55 #if USE_STRING_HACKS && HAVE_SNPRINTF
56 #define ARG_SLIMIT(name) size_t name,
57 #else
58 #define ARG_SLIMIT(name)        /* nothing */
59 #endif
60
61 #define CUR_SLIMIT _nc_SLIMIT(limit - (size_t) (target - base))
62 #define TOP_SLIMIT _nc_SLIMIT(sizeof(buffer))
63
64 /*
65  * Use 0x888888 as the magic number for new-format files, since it cannot be
66  * mistaken for the _cury/_curx pair of 16-bit numbers which start the old
67  * format.  It happens to be unused in the file 5.22 database (2015/03/07).
68  */
69 static const char my_magic[] =
70 {'\210', '\210', '\210', '\210', 0};
71
72 #if NCURSES_EXT_PUTWIN
73 typedef enum {
74     pINT                        /* int */
75     ,pSHORT                     /* short */
76     ,pBOOL                      /* bool */
77     ,pATTR                      /* attr_t */
78     ,pCHAR                      /* chtype */
79     ,pSIZE                      /* NCURSES_SIZE_T */
80 #if NCURSES_WIDECHAR
81     ,pCCHAR                     /* cchar_t */
82 #endif
83 } PARAM_TYPE;
84
85 typedef struct {
86     const char name[11];
87     attr_t attr;
88 } SCR_ATTRS;
89
90 typedef struct {
91     const char name[17];
92     PARAM_TYPE type;
93     size_t offset;
94 } SCR_PARAMS;
95
96 #define DATA(name) { { #name }, A_##name }
97 static const SCR_ATTRS scr_attrs[] =
98 {
99     DATA(NORMAL),
100     DATA(STANDOUT),
101     DATA(UNDERLINE),
102     DATA(REVERSE),
103     DATA(BLINK),
104     DATA(DIM),
105     DATA(BOLD),
106     DATA(ALTCHARSET),
107     DATA(INVIS),
108     DATA(PROTECT),
109     DATA(HORIZONTAL),
110     DATA(LEFT),
111     DATA(LOW),
112     DATA(RIGHT),
113     DATA(TOP),
114     DATA(VERTICAL),
115
116 #ifdef A_ITALIC
117     DATA(ITALIC),
118 #endif
119 };
120 #undef DATA
121
122 #define DATA(name, type) { { #name }, type, offsetof(WINDOW, name) }
123
124 static const SCR_PARAMS scr_params[] =
125 {
126     DATA(_cury, pSIZE),
127     DATA(_curx, pSIZE),
128     DATA(_maxy, pSIZE),
129     DATA(_maxx, pSIZE),
130     DATA(_begy, pSIZE),
131     DATA(_begx, pSIZE),
132     DATA(_flags, pSHORT),
133     DATA(_attrs, pATTR),
134     DATA(_bkgd, pCHAR),
135     DATA(_notimeout, pBOOL),
136     DATA(_clear, pBOOL),
137     DATA(_leaveok, pBOOL),
138     DATA(_scroll, pBOOL),
139     DATA(_idlok, pBOOL),
140     DATA(_idcok, pBOOL),
141     DATA(_immed, pBOOL),
142     DATA(_sync, pBOOL),
143     DATA(_use_keypad, pBOOL),
144     DATA(_delay, pINT),
145     DATA(_regtop, pSIZE),
146     DATA(_regbottom, pSIZE),
147     DATA(_pad._pad_y, pSIZE),
148     DATA(_pad._pad_x, pSIZE),
149     DATA(_pad._pad_top, pSIZE),
150     DATA(_pad._pad_left, pSIZE),
151     DATA(_pad._pad_bottom, pSIZE),
152     DATA(_pad._pad_right, pSIZE),
153     DATA(_yoffset, pSIZE),
154 #if NCURSES_WIDECHAR
155     DATA(_bkgrnd, pCCHAR),
156 #if NCURSES_EXT_COLORS
157     DATA(_color, pINT),
158 #endif
159 #endif
160 };
161 #undef DATA
162
163 static const NCURSES_CH_T blank = NewChar(BLANK_TEXT);
164
165 /*
166  * Allocate and read a line of text.  Caller must free it.
167  */
168 static char *
169 read_txt(FILE *fp)
170 {
171     size_t limit = 1024;
172     char *result = malloc(limit);
173     char *buffer;
174
175     if (result != 0) {
176         int ch = 0;
177         size_t used = 0;
178
179         clearerr(fp);
180         result[used] = '\0';
181         do {
182             if (used + 2 >= limit) {
183                 limit += 1024;
184                 buffer = realloc(result, limit);
185                 if (buffer == 0) {
186                     free(result);
187                     result = 0;
188                     break;
189                 }
190                 result = buffer;
191             }
192             ch = fgetc(fp);
193             if (ch == EOF)
194                 break;
195             result[used++] = (char) ch;
196             result[used] = '\0';
197         } while (ch != '\n');
198
199         if (ch == '\n') {
200             result[--used] = '\0';
201             TR(TRACE_IEVENT, ("READ:%s", result));
202         } else if (used == 0) {
203             free(result);
204             result = 0;
205         }
206     }
207     return result;
208 }
209
210 static char *
211 decode_attr(char *source, attr_t *target, int *color)
212 {
213     bool found = FALSE;
214
215     TR(TRACE_IEVENT, ("decode_attr   '%s'", source));
216
217     while (*source) {
218         if (source[0] == MARKER && source[1] == L_CURL) {
219             source += 2;
220             found = TRUE;
221         } else if (source[0] == R_CURL) {
222             source++;
223             found = FALSE;
224         } else if (found) {
225             size_t n;
226             char *next = source;
227
228             if (source[0] == GUTTER) {
229                 ++next;
230             } else if (*next == 'C') {
231                 int value = 0;
232                 unsigned pair;
233                 next++;
234                 while (isdigit(UChar(*next))) {
235                     value = value * 10 + (*next++ - '0');
236                 }
237                 *target &= ~A_COLOR;
238                 pair = (unsigned) ((value > 256)
239                                    ? COLOR_PAIR(255)
240                                    : COLOR_PAIR(value));
241                 *target |= pair;
242                 *color = value;
243             } else {
244                 while (isalnum(UChar(*next))) {
245                     ++next;
246                 }
247                 for (n = 0; n < SIZEOF(scr_attrs); ++n) {
248                     if ((size_t) (next - source) == strlen(scr_attrs[n].name)) {
249                         if (scr_attrs[n].attr) {
250                             *target |= scr_attrs[n].attr;
251                         } else {
252                             *target = A_NORMAL;
253                         }
254                         break;
255                     }
256                 }
257             }
258             source = next;
259         } else {
260             break;
261         }
262     }
263     return source;
264 }
265
266 static char *
267 decode_char(char *source, int *target)
268 {
269     int limit = 0;
270     int base = 16;
271     const char digits[] = "0123456789abcdef";
272
273     TR(TRACE_IEVENT, ("decode_char   '%s'", source));
274     *target = ' ';
275     switch (*source) {
276     case MARKER:
277         switch (*++source) {
278         case APPEND:
279             break;
280         case MARKER:
281             *target = MARKER;
282             ++source;
283             break;
284         case 's':
285             *target = ' ';
286             ++source;
287             break;
288         case '0':
289         case '1':
290         case '2':
291         case '3':
292             base = 8;
293             limit = 3;
294             break;
295         case 'u':
296             limit = 4;
297             ++source;
298             break;
299         case 'U':
300             limit = 8;
301             ++source;
302             break;
303         }
304         if (limit) {
305             *target = 0;
306             while (limit-- > 0) {
307                 char *find = strchr(digits, *source++);
308                 int ch = (find != 0) ? (int) (find - digits) : -1;
309                 *target *= base;
310                 if (ch >= 0 && ch < base) {
311                     *target += ch;
312                 }
313             }
314         }
315         break;
316     default:
317         *target = *source++;
318         break;
319     }
320     return source;
321 }
322
323 static char *
324 decode_chtype(char *source, chtype fillin, chtype *target)
325 {
326     attr_t attr = ChAttrOf(fillin);
327     int color = PAIR_NUMBER((int) attr);
328     int value;
329
330     TR(TRACE_IEVENT, ("decode_chtype '%s'", source));
331     source = decode_attr(source, &attr, &color);
332     source = decode_char(source, &value);
333     *target = (ChCharOf(value) | attr | (chtype) COLOR_PAIR(color));
334     /* FIXME - ignore combining characters */
335     return source;
336 }
337
338 #if NCURSES_WIDECHAR
339 static char *
340 decode_cchar(char *source, cchar_t *fillin, cchar_t *target)
341 {
342     int color;
343     attr_t attr = fillin->attr;
344     wchar_t chars[CCHARW_MAX];
345     int append = 0;
346     int value = 0;
347
348     TR(TRACE_IEVENT, ("decode_cchar  '%s'", source));
349     *target = blank;
350 #if NCURSES_EXT_COLORS
351     color = fillin->ext_color;
352 #else
353     color = (int) PAIR_NUMBER(attr);
354 #endif
355     source = decode_attr(source, &attr, &color);
356     memset(chars, 0, sizeof(chars));
357     source = decode_char(source, &value);
358     chars[0] = (wchar_t) value;
359     /* handle combining characters */
360     while (source[0] == MARKER && source[1] == APPEND) {
361         source += 2;
362         source = decode_char(source, &value);
363         if (++append < CCHARW_MAX) {
364             chars[append] = (wchar_t) value;
365         }
366     }
367     setcchar(target, chars, attr, (short) color, &color);
368     return source;
369 }
370 #endif
371
372 static int
373 read_win(WINDOW *win, FILE *fp)
374 {
375     int code = ERR;
376     size_t n;
377     int color;
378 #if NCURSES_WIDECHAR
379     NCURSES_CH_T prior;
380 #endif
381     chtype prior2;
382
383     memset(win, 0, sizeof(WINDOW));
384     for (;;) {
385         char *name;
386         char *value;
387         char *txt = read_txt(fp);
388
389         if (txt == 0)
390             break;
391         if (!strcmp(txt, "rows:")) {
392             free(txt);
393             code = OK;
394             break;
395         }
396         if ((value = strchr(txt, '=')) == 0) {
397             free(txt);
398             continue;
399         }
400         *value++ = '\0';
401         name = !strcmp(txt, "flag") ? value : txt;
402         for (n = 0; n < SIZEOF(scr_params); ++n) {
403             if (!strcmp(name, scr_params[n].name)) {
404                 void *data = (void *) ((char *) win + scr_params[n].offset);
405
406                 switch (scr_params[n].type) {
407                 case pATTR:
408                     (void) decode_attr(value, data, &color);
409                     break;
410                 case pBOOL:
411                     *(bool *) data = TRUE;
412                     break;
413                 case pCHAR:
414                     prior2 = ' ';
415                     decode_chtype(value, prior2, data);
416                     break;
417                 case pINT:
418                     *(int *) data = atoi(value);
419                     break;
420                 case pSHORT:
421                     *(short *) data = (short) atoi(value);
422                     break;
423                 case pSIZE:
424                     *(NCURSES_SIZE_T *) data = (NCURSES_SIZE_T) atoi(value);
425                     break;
426 #if NCURSES_WIDECHAR
427                 case pCCHAR:
428                     prior = blank;
429                     decode_cchar(value, &prior, data);
430                     break;
431 #endif
432                 }
433                 break;
434             }
435         }
436         free(txt);
437     }
438     return code;
439 }
440
441 static int
442 read_row(char *source, NCURSES_CH_T *prior, NCURSES_CH_T *target, int length)
443 {
444     while (*source != '\0' && length > 0) {
445 #if NCURSES_WIDECHAR
446         int len;
447
448         source = decode_cchar(source, prior, target);
449         len = _nc_wacs_width(target->chars[0]);
450         if (len > 1) {
451             int n;
452
453             SetWidecExt(CHDEREF(target), 0);
454             for (n = 1; n < len; ++n) {
455                 target[n] = target[0];
456                 SetWidecExt(CHDEREF(target), n);
457             }
458             target += (len - 1);
459             length -= (len - 1);
460         }
461 #else
462         source = decode_chtype(source, *prior, target);
463 #endif
464         *prior = *target;
465         ++target;
466         --length;
467     }
468     while (length-- > 0) {
469         *target++ = blank;
470     }
471     /* FIXME - see what error conditions should apply if I need to return ERR */
472     return 0;
473 }
474 #endif /* NCURSES_EXT_PUTWIN */
475
476 /*
477  * Originally, getwin/putwin used fread/fwrite, because they used binary data.
478  * The new format uses printable ASCII, which does not have as predictable
479  * sizes.  Consequently, we use buffered I/O, e.g., fgetc/fprintf, which need
480  * special handling if we want to read screen dumps from an older library.
481  */
482 static int
483 read_block(void *target, size_t length, FILE *fp)
484 {
485     int result = 0;
486     char *buffer = target;
487
488     clearerr(fp);
489     while (length-- != 0) {
490         int ch = fgetc(fp);
491         if (ch == EOF) {
492             result = -1;
493             break;
494         }
495         *buffer++ = (char) ch;
496     }
497     return result;
498 }
499
500 NCURSES_EXPORT(WINDOW *)
501 NCURSES_SP_NAME(getwin) (NCURSES_SP_DCLx FILE *filep)
502 {
503     WINDOW tmp, *nwin;
504     bool old_format = FALSE;
505
506     T((T_CALLED("getwin(%p)"), (void *) filep));
507
508     if (filep == 0) {
509         returnWin(0);
510     }
511
512     /*
513      * Read the first 4 bytes to determine first if this is an old-format
514      * screen-dump, or new-format.
515      */
516     if (read_block(&tmp, (size_t) 4, filep) < 0) {
517         returnWin(0);
518     }
519     /*
520      * If this is a new-format file, and we do not support it, give up.
521      */
522     if (!memcmp(&tmp, my_magic, (size_t) 4)) {
523 #if NCURSES_EXT_PUTWIN
524         if (read_win(&tmp, filep) < 0)
525 #endif
526             returnWin(0);
527     } else if (read_block(((char *) &tmp) + 4, sizeof(WINDOW) - 4, filep) < 0) {
528         returnWin(0);
529     } else {
530         old_format = TRUE;
531     }
532
533     /*
534      * Check the window-size:
535      */
536     if (tmp._maxy == 0 ||
537         tmp._maxy > MAX_SIZE ||
538         tmp._maxx == 0 ||
539         tmp._maxx > MAX_SIZE) {
540         returnWin(0);
541     }
542
543     if (IS_PAD(&tmp)) {
544         nwin = NCURSES_SP_NAME(newpad) (NCURSES_SP_ARGx
545                                         tmp._maxy + 1,
546                                         tmp._maxx + 1);
547     } else {
548         nwin = NCURSES_SP_NAME(newwin) (NCURSES_SP_ARGx
549                                         tmp._maxy + 1,
550                                         tmp._maxx + 1, 0, 0);
551     }
552
553     /*
554      * We deliberately do not restore the _parx, _pary, or _parent
555      * fields, because the window hierarchy within which they
556      * made sense is probably gone.
557      */
558     if (nwin != 0) {
559         int n;
560         size_t linesize = sizeof(NCURSES_CH_T) * (size_t) (tmp._maxx + 1);
561
562         nwin->_curx = tmp._curx;
563         nwin->_cury = tmp._cury;
564         nwin->_maxy = tmp._maxy;
565         nwin->_maxx = tmp._maxx;
566         nwin->_begy = tmp._begy;
567         nwin->_begx = tmp._begx;
568         nwin->_yoffset = tmp._yoffset;
569         nwin->_flags = tmp._flags & ~(_SUBWIN);
570
571         WINDOW_ATTRS(nwin) = WINDOW_ATTRS(&tmp);
572         nwin->_nc_bkgd = tmp._nc_bkgd;
573
574         nwin->_notimeout = tmp._notimeout;
575         nwin->_clear = tmp._clear;
576         nwin->_leaveok = tmp._leaveok;
577         nwin->_idlok = tmp._idlok;
578         nwin->_idcok = tmp._idcok;
579         nwin->_immed = tmp._immed;
580         nwin->_scroll = tmp._scroll;
581         nwin->_sync = tmp._sync;
582         nwin->_use_keypad = tmp._use_keypad;
583         nwin->_delay = tmp._delay;
584
585         nwin->_regtop = tmp._regtop;
586         nwin->_regbottom = tmp._regbottom;
587
588         if (IS_PAD(&tmp))
589             nwin->_pad = tmp._pad;
590
591         if (old_format) {
592             T(("reading old-format screen dump"));
593             for (n = 0; n <= nwin->_maxy; n++) {
594                 if (read_block(nwin->_line[n].text, linesize, filep) < 0) {
595                     delwin(nwin);
596                     returnWin(0);
597                 }
598             }
599         }
600 #if NCURSES_EXT_PUTWIN
601         else {
602             char *txt = 0;
603             bool success = TRUE;
604             NCURSES_CH_T prior = blank;
605
606             T(("reading new-format screen dump"));
607             for (n = 0; n <= nwin->_maxy; n++) {
608                 long row;
609                 char *next;
610
611                 if ((txt = read_txt(filep)) == 0) {
612                     T(("...failed to read string for row %d", n + 1));
613                     success = FALSE;
614                     break;
615                 }
616                 row = strtol(txt, &next, 10);
617                 if (row != (n + 1) || *next != ':') {
618                     T(("...failed to read row-number %d", n + 1));
619                     success = FALSE;
620                     break;
621                 }
622
623                 if (read_row(++next, &prior, nwin->_line[n].text, tmp._maxx
624                              + 1) < 0) {
625                     T(("...failed to read cells for row %d", n + 1));
626                     success = FALSE;
627                     break;
628                 }
629                 free(txt);
630                 txt = 0;
631             }
632
633             if (!success) {
634                 free(txt);
635                 delwin(nwin);
636                 returnWin(0);
637             }
638         }
639 #endif
640         touchwin(nwin);
641     }
642     returnWin(nwin);
643 }
644
645 #if NCURSES_SP_FUNCS
646 NCURSES_EXPORT(WINDOW *)
647 getwin(FILE *filep)
648 {
649     return NCURSES_SP_NAME(getwin) (CURRENT_SCREEN, filep);
650 }
651 #endif
652
653 #if NCURSES_EXT_PUTWIN
654 static void
655 encode_attr(char *target, ARG_SLIMIT(limit)
656             attr_t source,
657             attr_t prior,
658             int source_color,
659             int prior_color)
660 {
661 #if USE_STRING_HACKS && HAVE_SNPRINTF
662     char *base = target;
663 #endif
664     source &= ~A_CHARTEXT;
665     prior &= ~A_CHARTEXT;
666
667     *target = '\0';
668     if ((source != prior) || (source_color != prior_color)) {
669         size_t n;
670         bool first = TRUE;
671
672         *target++ = MARKER;
673         *target++ = L_CURL;
674
675         for (n = 0; n < SIZEOF(scr_attrs); ++n) {
676             if ((source & scr_attrs[n].attr) != 0 ||
677                 ((source & ALL_BUT_COLOR) == 0 &&
678                  (scr_attrs[n].attr == A_NORMAL))) {
679                 if (first) {
680                     first = FALSE;
681                 } else {
682                     *target++ = '|';
683                 }
684                 _nc_STRCPY(target, scr_attrs[n].name, limit);
685                 target += strlen(target);
686             }
687         }
688         if (source_color != prior_color) {
689             if (!first)
690                 *target++ = '|';
691             _nc_SPRINTF(target, CUR_SLIMIT "C%d", source_color);
692             target += strlen(target);
693         }
694
695         *target++ = R_CURL;
696         *target = '\0';
697     }
698 }
699
700 static void
701 encode_cell(char *target, ARG_SLIMIT(limit) CARG_CH_T source, CARG_CH_T previous)
702 {
703 #if USE_STRING_HACKS && HAVE_SNPRINTF
704     char *base = target;
705 #endif
706 #if NCURSES_WIDECHAR
707     size_t n;
708     int source_pair = GetPair(*source);
709     int previous_pair = GetPair(*previous);
710
711     *target = '\0';
712     if ((previous->attr != source->attr) || (previous_pair != source_pair)) {
713         encode_attr(target, CUR_SLIMIT
714                     source->attr,
715                     previous->attr,
716                     source_pair,
717                     previous_pair);
718     }
719     target += strlen(target);
720 #if NCURSES_EXT_COLORS
721     if (previous->ext_color != source->ext_color) {
722         _nc_SPRINTF(target, CUR_SLIMIT
723                     "%c%cC%d%c", MARKER, L_CURL, source->ext_color, R_CURL);
724     }
725 #endif
726     for (n = 0; n < SIZEOF(source->chars); ++n) {
727         unsigned uch = (unsigned) source->chars[n];
728         if (uch == 0)
729             continue;
730         if (n) {
731             *target++ = MARKER;
732             *target++ = APPEND;
733         }
734         *target++ = MARKER;
735         if (uch > 0xffff) {
736             _nc_SPRINTF(target, CUR_SLIMIT "U%08x", uch);
737         } else if (uch > 0xff) {
738             _nc_SPRINTF(target, CUR_SLIMIT "u%04x", uch);
739         } else if (uch < 32 || uch >= 127) {
740             _nc_SPRINTF(target, CUR_SLIMIT "%03o", uch & 0xff);
741         } else {
742             switch (uch) {
743             case ' ':
744                 _nc_STRCPY(target, "s", limit);
745                 break;
746             case MARKER:
747                 *target++ = MARKER;
748                 *target = '\0';
749                 break;
750             default:
751                 --target;
752                 _nc_SPRINTF(target, CUR_SLIMIT "%c", uch);
753                 break;
754             }
755         }
756         target += strlen(target);
757     }
758 #else
759     chtype ch = CharOfD(source);
760
761     *target = '\0';
762     if (AttrOfD(previous) != AttrOfD(source)) {
763         encode_attr(target, CUR_SLIMIT
764                     AttrOfD(source),
765                     AttrOfD(previous),
766                     GetPair(source),
767                     GetPair(previous));
768     }
769     target += strlen(target);
770     *target++ = MARKER;
771     if (ch < 32 || ch >= 127) {
772         _nc_SPRINTF(target, CUR_SLIMIT "%03o", UChar(ch));
773     } else {
774         switch (ch) {
775         case ' ':
776             _nc_STRCPY(target, "s", limit);
777             break;
778         case MARKER:
779             *target++ = MARKER;
780             *target = '\0';
781             break;
782         default:
783             --target;
784             _nc_SPRINTF(target, CUR_SLIMIT "%c", UChar(ch));
785             break;
786         }
787     }
788 #endif
789 }
790 #endif
791
792 NCURSES_EXPORT(int)
793 putwin(WINDOW *win, FILE *filep)
794 {
795     int code = ERR;
796
797     T((T_CALLED("putwin(%p,%p)"), (void *) win, (void *) filep));
798
799 #if NCURSES_EXT_PUTWIN
800     if (win != 0) {
801         const char *version = curses_version();
802         char buffer[1024];
803         NCURSES_CH_T last_cell;
804         int y;
805
806         memset(&last_cell, 0, sizeof(last_cell));
807
808         clearerr(filep);
809
810         /*
811          * Our magic number is technically nonprinting, but aside from that,
812          * all of the file is printable ASCII.
813          */
814 #define PUTS(s) if (fputs(s, filep) == EOF || ferror(filep)) returnCode(code)
815         PUTS(my_magic);
816         PUTS(version);
817         PUTS("\n");
818         for (y = 0; y < (int) SIZEOF(scr_params); ++y) {
819             const char *name = scr_params[y].name;
820             const char *data = (char *) win + scr_params[y].offset;
821             const void *dp = (const void *) data;
822             attr_t attr;
823
824             *buffer = '\0';
825             if (!strncmp(name, "_pad.", (size_t) 5) && !IS_PAD(win)) {
826                 continue;
827             }
828             switch (scr_params[y].type) {
829             case pATTR:
830                 attr = (*(const attr_t *) dp) & ~A_CHARTEXT;
831                 encode_attr(buffer, TOP_SLIMIT
832                             (*(const attr_t *) dp) & ~A_CHARTEXT,
833                             A_NORMAL,
834                             COLOR_PAIR((int) attr),
835                             0);
836                 break;
837             case pBOOL:
838                 if (!(*(const bool *) data)) {
839                     continue;
840                 }
841                 _nc_STRCPY(buffer, name, sizeof(buffer));
842                 name = "flag";
843                 break;
844             case pCHAR:
845                 attr = (*(const attr_t *) dp);
846                 encode_attr(buffer, TOP_SLIMIT
847                             * (const attr_t *) dp,
848                             A_NORMAL,
849                             COLOR_PAIR((int) attr),
850                             0);
851                 break;
852             case pINT:
853                 if (!(*(const int *) dp))
854                     continue;
855                 _nc_SPRINTF(buffer, TOP_SLIMIT
856                             "%d", *(const int *) dp);
857                 break;
858             case pSHORT:
859                 if (!(*(const short *) dp))
860                     continue;
861                 _nc_SPRINTF(buffer, TOP_SLIMIT
862                             "%d", *(const short *) dp);
863                 break;
864             case pSIZE:
865                 if (!(*(const NCURSES_SIZE_T *) dp))
866                     continue;
867                 _nc_SPRINTF(buffer, TOP_SLIMIT
868                             "%d", *(const NCURSES_SIZE_T *) dp);
869                 break;
870 #if NCURSES_WIDECHAR
871             case pCCHAR:
872                 encode_cell(buffer, TOP_SLIMIT
873                             (CARG_CH_T) dp, CHREF(last_cell));
874                 break;
875 #endif
876             }
877             /*
878              * Only write non-default data.
879              */
880             if (*buffer != '\0') {
881                 if (fprintf(filep, "%s=%s\n", name, buffer) <= 0
882                     || ferror(filep))
883                     returnCode(code);
884             }
885         }
886         /* Write row-data */
887         fprintf(filep, "rows:\n");
888         for (y = 0; y <= win->_maxy; y++) {
889             NCURSES_CH_T *data = win->_line[y].text;
890             int x;
891             if (fprintf(filep, "%d:", y + 1) <= 0
892                 || ferror(filep))
893                 returnCode(code);
894             for (x = 0; x <= win->_maxx; x++) {
895 #if NCURSES_WIDECHAR
896                 int len = _nc_wacs_width(data[x].chars[0]);
897                 encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell));
898                 last_cell = data[x];
899                 PUTS(buffer);
900                 if (len > 1)
901                     x += (len - 1);
902 #else
903                 encode_cell(buffer, TOP_SLIMIT CHREF(data[x]), CHREF(last_cell));
904                 last_cell = data[x];
905                 PUTS(buffer);
906 #endif
907             }
908             PUTS("\n");
909         }
910         code = OK;
911     }
912 #else
913     /*
914      * This is the original putwin():
915      * A straight binary dump is simple, but its format can depend on whether
916      * ncurses is compiled with wide-character support, and also may depend
917      * on the version of ncurses, e.g., if the WINDOW structure is extended.
918      */
919     if (win != 0) {
920         size_t len = (size_t) (win->_maxx + 1);
921         int y;
922
923         clearerr(filep);
924         if (fwrite(win, sizeof(WINDOW), (size_t) 1, filep) != 1
925             || ferror(filep))
926               returnCode(code);
927
928         for (y = 0; y <= win->_maxy; y++) {
929             if (fwrite(win->_line[y].text,
930                        sizeof(NCURSES_CH_T), len, filep) != len
931                 || ferror(filep)) {
932                 returnCode(code);
933             }
934         }
935         code = OK;
936     }
937 #endif
938     returnCode(code);
939 }
940
941 /*
942  * Replace a window covering the whole screen, i.e., newscr or curscr.
943  */
944 static WINDOW *
945 replace_window(WINDOW *target, FILE *source)
946 {
947     WINDOW *result = getwin(source);
948 #if NCURSES_EXT_FUNCS
949     if (result != NULL) {
950         if (getmaxx(result) != getmaxx(target)
951             || getmaxy(result) != getmaxy(target)) {
952             int code = wresize(result,
953                                1 + getmaxy(target),
954                                1 + getmaxx(target));
955             if (code != OK) {
956                 delwin(result);
957                 result = NULL;
958             }
959         }
960     }
961 #endif
962     delwin(target);
963     return result;
964 }
965
966 NCURSES_EXPORT(int)
967 NCURSES_SP_NAME(scr_restore) (NCURSES_SP_DCLx const char *file)
968 {
969     FILE *fp = 0;
970     int code = ERR;
971
972     T((T_CALLED("scr_restore(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
973
974     if (_nc_access(file, R_OK) >= 0
975         && (fp = safe_fopen(file, BIN_R)) != 0) {
976         NewScreen(SP_PARM) = replace_window(NewScreen(SP_PARM), fp);
977 #if !USE_REENTRANT
978         newscr = NewScreen(SP_PARM);
979 #endif
980         (void) fclose(fp);
981         if (NewScreen(SP_PARM) != 0) {
982             code = OK;
983         }
984     }
985     returnCode(code);
986 }
987
988 #if NCURSES_SP_FUNCS
989 NCURSES_EXPORT(int)
990 scr_restore(const char *file)
991 {
992     return NCURSES_SP_NAME(scr_restore) (CURRENT_SCREEN, file);
993 }
994 #endif
995
996 NCURSES_EXPORT(int)
997 scr_dump(const char *file)
998 {
999     int result;
1000     FILE *fp = 0;
1001
1002     T((T_CALLED("scr_dump(%s)"), _nc_visbuf(file)));
1003
1004     if (_nc_access(file, W_OK) < 0
1005         || (fp = safe_fopen(file, BIN_W)) == 0) {
1006         result = ERR;
1007     } else {
1008         (void) putwin(newscr, fp);
1009         (void) fclose(fp);
1010         result = OK;
1011     }
1012     returnCode(result);
1013 }
1014
1015 NCURSES_EXPORT(int)
1016 NCURSES_SP_NAME(scr_init) (NCURSES_SP_DCLx const char *file)
1017 {
1018     int code = ERR;
1019
1020     T((T_CALLED("scr_init(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
1021
1022     if (SP_PARM != 0 &&
1023 #ifdef USE_TERM_DRIVER
1024         InfoOf(SP_PARM).caninit
1025 #else
1026         !(exit_ca_mode && non_rev_rmcup)
1027 #endif
1028         ) {
1029         FILE *fp = 0;
1030
1031         if (_nc_access(file, R_OK) >= 0
1032             && (fp = safe_fopen(file, BIN_R)) != 0) {
1033             CurScreen(SP_PARM) = replace_window(CurScreen(SP_PARM), fp);
1034 #if !USE_REENTRANT
1035             curscr = CurScreen(SP_PARM);
1036 #endif
1037             (void) fclose(fp);
1038             if (CurScreen(SP_PARM) != 0) {
1039                 code = OK;
1040             }
1041         }
1042     }
1043     returnCode(code);
1044 }
1045
1046 #if NCURSES_SP_FUNCS
1047 NCURSES_EXPORT(int)
1048 scr_init(const char *file)
1049 {
1050     return NCURSES_SP_NAME(scr_init) (CURRENT_SCREEN, file);
1051 }
1052 #endif
1053
1054 NCURSES_EXPORT(int)
1055 NCURSES_SP_NAME(scr_set) (NCURSES_SP_DCLx const char *file)
1056 {
1057     int code = ERR;
1058
1059     T((T_CALLED("scr_set(%p,%s)"), (void *) SP_PARM, _nc_visbuf(file)));
1060
1061     if (NCURSES_SP_NAME(scr_init) (NCURSES_SP_ARGx file) == OK) {
1062         delwin(NewScreen(SP_PARM));
1063         NewScreen(SP_PARM) = dupwin(curscr);
1064 #if !USE_REENTRANT
1065         newscr = NewScreen(SP_PARM);
1066 #endif
1067         if (NewScreen(SP_PARM) != 0) {
1068             code = OK;
1069         }
1070     }
1071     returnCode(code);
1072 }
1073
1074 #if NCURSES_SP_FUNCS
1075 NCURSES_EXPORT(int)
1076 scr_set(const char *file)
1077 {
1078     return NCURSES_SP_NAME(scr_set) (CURRENT_SCREEN, file);
1079 }
1080 #endif