150276Speter/****************************************************************************
2184989Srafan * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc.              *
350276Speter *                                                                          *
450276Speter * Permission is hereby granted, free of charge, to any person obtaining a  *
550276Speter * copy of this software and associated documentation files (the            *
650276Speter * "Software"), to deal in the Software without restriction, including      *
750276Speter * without limitation the rights to use, copy, modify, merge, publish,      *
850276Speter * distribute, distribute with modifications, sublicense, and/or sell       *
950276Speter * copies of the Software, and to permit persons to whom the Software is    *
1050276Speter * furnished to do so, subject to the following conditions:                 *
1150276Speter *                                                                          *
1250276Speter * The above copyright notice and this permission notice shall be included  *
1350276Speter * in all copies or substantial portions of the Software.                   *
1450276Speter *                                                                          *
1550276Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
1650276Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
1750276Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
1850276Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
1950276Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
2050276Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
2150276Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
2250276Speter *                                                                          *
2350276Speter * Except as contained in this notice, the name(s) of the above copyright   *
2450276Speter * holders shall not be used in advertising or otherwise to promote the     *
2550276Speter * sale, use or other dealings in this Software without prior written       *
2650276Speter * authorization.                                                           *
2750276Speter ****************************************************************************/
2850276Speter
2950276Speter/****************************************************************************
3050276Speter *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
3150276Speter *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32166124Srafan *     and: Thomas E. Dickey                        1996-on                 *
3350276Speter ****************************************************************************/
3450276Speter
3550276Speter/*
3650276Speter *	write_entry.c -- write a terminfo structure onto the file system
3750276Speter */
3850276Speter
3950276Speter#include <curses.priv.h>
40166124Srafan#include <hashed_db.h>
4150276Speter
4250276Speter#include <sys/stat.h>
4350276Speter
4450276Speter#include <tic.h>
4550276Speter#include <term_entry.h>
4650276Speter
4750276Speter#ifndef S_ISDIR
4850276Speter#define S_ISDIR(mode) ((mode & S_IFMT) == S_IFDIR)
4950276Speter#endif
5050276Speter
51166124Srafan#if 1
5250276Speter#define TRACE_OUT(p) DEBUG(2, p)
5350276Speter#else
5462449Speter#define TRACE_OUT(p)		/*nothing */
5550276Speter#endif
5650276Speter
57184989SrafanMODULE_ID("$Id: write_entry.c,v 1.72 2008/08/03 19:24:00 tom Exp $")
5850276Speter
5950276Speterstatic int total_written;
6050276Speter
61166124Srafanstatic int make_db_root(const char *);
62166124Srafanstatic int write_object(TERMTYPE *, char *, unsigned *, unsigned);
6350276Speter
64166124Srafan#if !USE_HASHED_DB
6562449Speterstatic void
66166124Srafanwrite_file(char *filename, TERMTYPE *tp)
6750276Speter{
68166124Srafan    char buffer[MAX_ENTRY_SIZE];
69166124Srafan    unsigned limit = sizeof(buffer);
70166124Srafan    unsigned offset = 0;
71166124Srafan
7262449Speter    FILE *fp = (_nc_access(filename, W_OK) == 0) ? fopen(filename, "wb") : 0;
7362449Speter    if (fp == 0) {
7462449Speter	perror(filename);
7562449Speter	_nc_syserr_abort("can't open %s/%s", _nc_tic_dir(0), filename);
7662449Speter    }
7762449Speter    DEBUG(1, ("Created %s", filename));
7850276Speter
79166124Srafan    if (write_object(tp, buffer, &offset, limit) == ERR
80166124Srafan	|| fwrite(buffer, sizeof(char), offset, fp) != offset) {
8162449Speter	_nc_syserr_abort("error writing %s/%s", _nc_tic_dir(0), filename);
8262449Speter    }
83166124Srafan
8462449Speter    fclose(fp);
8550276Speter}
8650276Speter
8750276Speter/*
88166124Srafan * Check for access rights to destination directories
89166124Srafan * Create any directories which don't exist.
9050276Speter *
91166124Srafan * Note:  there's no reason to return the result of make_db_root(), since
92166124Srafan * this function is called only in instances where that has to succeed.
9350276Speter */
94166124Srafanstatic void
95166124Srafancheck_writeable(int code)
96166124Srafan{
97166124Srafan    static const char dirnames[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
98166124Srafan    static bool verified[sizeof(dirnames)];
99166124Srafan
100174993Srafan    char dir[sizeof(LEAF_FMT)];
101166124Srafan    char *s = 0;
102166124Srafan
103166124Srafan    if (code == 0 || (s = strchr(dirnames, code)) == 0)
104174993Srafan	_nc_err_abort("Illegal terminfo subdirectory \"" LEAF_FMT "\"", code);
105166124Srafan
106166124Srafan    if (verified[s - dirnames])
107166124Srafan	return;
108166124Srafan
109174993Srafan    sprintf(dir, LEAF_FMT, code);
110166124Srafan    if (make_db_root(dir) < 0) {
111166124Srafan	_nc_err_abort("%s/%s: permission denied", _nc_tic_dir(0), dir);
112166124Srafan    }
113166124Srafan
114166124Srafan    verified[s - dirnames] = TRUE;
115166124Srafan}
116166124Srafan#endif /* !USE_HASHED_DB */
117166124Srafan
11862449Speterstatic int
119166124Srafanmake_db_path(char *dst, const char *src, unsigned limit)
12050276Speter{
121166124Srafan    int rc = -1;
122166124Srafan    const char *top = _nc_tic_dir(0);
12350276Speter
124166124Srafan    if (src == top || _nc_is_abs_path(src)) {
125166124Srafan	if (strlen(src) + 1 <= limit) {
126166124Srafan	    (void) strcpy(dst, src);
127166124Srafan	    rc = 0;
128166124Srafan	}
12962449Speter    } else {
130166124Srafan	if (strlen(top) + strlen(src) + 2 <= limit) {
131166124Srafan	    (void) sprintf(dst, "%s/%s", top, src);
132166124Srafan	    rc = 0;
133166124Srafan	}
13462449Speter    }
135166124Srafan#if USE_HASHED_DB
136166124Srafan    if (rc == 0) {
137166124Srafan	if (_nc_is_dir_path(dst)) {
138166124Srafan	    rc = -1;
139166124Srafan	} else {
140166124Srafan	    unsigned have = strlen(dst);
141166124Srafan	    if (have > 3 && strcmp(dst + have - 3, DBM_SUFFIX)) {
142166124Srafan		if (have + 3 <= limit)
143166124Srafan		    strcat(dst, DBM_SUFFIX);
144166124Srafan		else
145166124Srafan		    rc = -1;
146166124Srafan	    }
147166124Srafan	}
148166124Srafan    }
149166124Srafan#endif
150166124Srafan    return rc;
151166124Srafan}
15250276Speter
153166124Srafan/*
154166124Srafan * Make a database-root if it doesn't exist.
155166124Srafan */
156166124Srafanstatic int
157166124Srafanmake_db_root(const char *path)
158166124Srafan{
159166124Srafan    int rc;
160166124Srafan    char fullpath[PATH_MAX];
161166124Srafan
162166124Srafan    if ((rc = make_db_path(fullpath, path, sizeof(fullpath))) == 0) {
163166124Srafan#if USE_HASHED_DB
164166124Srafan	DB *capdbp;
165166124Srafan
166166124Srafan	if ((capdbp = _nc_db_open(fullpath, TRUE)) == NULL)
167166124Srafan	    rc = -1;
168166124Srafan	else if (_nc_db_close(capdbp) < 0)
169166124Srafan	    rc = -1;
170166124Srafan#else
171166124Srafan	struct stat statbuf;
172166124Srafan
173166124Srafan	if ((rc = stat(path, &statbuf)) < 0) {
174166124Srafan	    rc = mkdir(path, 0777);
175166124Srafan	} else if (_nc_access(path, R_OK | W_OK | X_OK) < 0) {
17662449Speter	    rc = -1;		/* permission denied */
17762449Speter	} else if (!(S_ISDIR(statbuf.st_mode))) {
17862449Speter	    rc = -1;		/* not a directory */
17950276Speter	}
180166124Srafan#endif
18162449Speter    }
18262449Speter    return rc;
18350276Speter}
18450276Speter
185166124Srafan/*
186166124Srafan * Set the write directory for compiled entries.
187166124Srafan */
18876726SpeterNCURSES_EXPORT(void)
18962449Speter_nc_set_writedir(char *dir)
19050276Speter{
19150276Speter    const char *destination;
19250276Speter    char actual[PATH_MAX];
19350276Speter
19466963Speter    if (dir == 0
19576726Speter	&& use_terminfo_vars())
19666963Speter	dir = getenv("TERMINFO");
19766963Speter
19850276Speter    if (dir != 0)
19950276Speter	(void) _nc_tic_dir(dir);
20050276Speter
20150276Speter    destination = _nc_tic_dir(0);
202166124Srafan    if (make_db_root(destination) < 0) {
20362449Speter	char *home = _nc_home_terminfo();
20450276Speter
20550276Speter	if (home != 0) {
20650276Speter	    destination = home;
207166124Srafan	    if (make_db_root(destination) < 0)
20850276Speter		_nc_err_abort("%s: permission denied (errno %d)",
20976726Speter			      destination, errno);
21050276Speter	}
21150276Speter    }
21250276Speter
21350276Speter    /*
21450276Speter     * Note: because of this code, this logic should be exercised
21550276Speter     * *once only* per run.
21650276Speter     */
217166124Srafan#if USE_HASHED_DB
218166124Srafan    make_db_path(actual, destination, sizeof(actual));
219166124Srafan#else
22050276Speter    if (chdir(_nc_tic_dir(destination)) < 0
22162449Speter	|| getcwd(actual, sizeof(actual)) == 0)
22250276Speter	_nc_err_abort("%s: not a directory", destination);
223166124Srafan#endif
22450276Speter    _nc_keep_tic_dir(strdup(actual));
22550276Speter}
22650276Speter
22750276Speter/*
22850276Speter *	Save the compiled version of a description in the filesystem.
22950276Speter *
23050276Speter *	make a copy of the name-list
23150276Speter *	break it up into first-name and all-but-last-name
23250276Speter *	creat(first-name)
23350276Speter *	write object information to first-name
23450276Speter *	close(first-name)
23550276Speter *      for each name in all-but-last-name
23650276Speter *	    link to first-name
23750276Speter *
23850276Speter *	Using 'time()' to obtain a reference for file timestamps is unreliable,
23950276Speter *	e.g., with NFS, because the filesystem may have a different time
24050276Speter *	reference.  We check for pre-existence of links by latching the first
24150276Speter *	timestamp from a file that we create.
24250276Speter *
24350276Speter *	The _nc_warning() calls will report a correct line number only if
24450276Speter *	_nc_curr_line is properly set before the write_entry() call.
24550276Speter */
24650276Speter
247166124SrafanNCURSES_EXPORT(void)
248166124Srafan_nc_write_entry(TERMTYPE *const tp)
24950276Speter{
250166124Srafan#if USE_HASHED_DB
251166124Srafan
252166124Srafan    char buffer[MAX_ENTRY_SIZE + 1];
253166124Srafan    unsigned limit = sizeof(buffer);
254166124Srafan    unsigned offset = 0;
255166124Srafan
256166124Srafan#else /* !USE_HASHED_DB */
257166124Srafan
25862449Speter    struct stat statbuf;
25962449Speter    char filename[PATH_MAX];
26062449Speter    char linkname[PATH_MAX];
26150276Speter#if USE_SYMLINKS
26262449Speter    char symlinkname[PATH_MAX];
26397049Speter#if !HAVE_LINK
26497049Speter#undef HAVE_LINK
26597049Speter#define HAVE_LINK 1
26697049Speter#endif
26750276Speter#endif /* USE_SYMLINKS */
268166124Srafan
26962449Speter    static int call_count;
27062449Speter    static time_t start_time;	/* time at start of writes */
27150276Speter
272166124Srafan#endif /* USE_HASHED_DB */
27350276Speter
274166124Srafan    char name_list[MAX_TERMINFO_LENGTH];
275166124Srafan    char *first_name, *other_names;
276166124Srafan    char *ptr;
277166124Srafan
278184989Srafan    assert(strlen(tp->term_names) != 0);
279184989Srafan    assert(strlen(tp->term_names) < sizeof(name_list));
280184989Srafan
28162449Speter    (void) strcpy(name_list, tp->term_names);
28262449Speter    DEBUG(7, ("Name list = '%s'", name_list));
28350276Speter
28462449Speter    first_name = name_list;
28550276Speter
28662449Speter    ptr = &name_list[strlen(name_list) - 1];
28762449Speter    other_names = ptr + 1;
28850276Speter
28962449Speter    while (ptr > name_list && *ptr != '|')
29062449Speter	ptr--;
29150276Speter
29262449Speter    if (ptr != name_list) {
29362449Speter	*ptr = '\0';
29450276Speter
29562449Speter	for (ptr = name_list; *ptr != '\0' && *ptr != '|'; ptr++)
29662449Speter	    continue;
29750276Speter
29862449Speter	if (*ptr == '\0')
29962449Speter	    other_names = ptr;
30062449Speter	else {
30162449Speter	    *ptr = '\0';
30262449Speter	    other_names = ptr + 1;
30350276Speter	}
30462449Speter    }
30550276Speter
30662449Speter    DEBUG(7, ("First name = '%s'", first_name));
30762449Speter    DEBUG(7, ("Other names = '%s'", other_names));
30850276Speter
30962449Speter    _nc_set_type(first_name);
31050276Speter
311166124Srafan#if USE_HASHED_DB
312166124Srafan    if (write_object(tp, buffer + 1, &offset, limit - 1) != ERR) {
313166124Srafan	DB *capdb = _nc_db_open(_nc_tic_dir(0), TRUE);
314166124Srafan	DBT key, data;
315166124Srafan
316166124Srafan	if (capdb != 0) {
317166124Srafan	    buffer[0] = 0;
318166124Srafan
319166124Srafan	    memset(&key, 0, sizeof(key));
320166124Srafan	    key.data = tp->term_names;
321166124Srafan	    key.size = strlen(tp->term_names);
322166124Srafan
323166124Srafan	    memset(&data, 0, sizeof(data));
324166124Srafan	    data.data = buffer;
325166124Srafan	    data.size = offset + 1;
326166124Srafan
327166124Srafan	    _nc_db_put(capdb, &key, &data);
328166124Srafan
329166124Srafan	    buffer[0] = 2;
330166124Srafan
331166124Srafan	    key.data = name_list;
332166124Srafan	    key.size = strlen(name_list);
333166124Srafan
334166124Srafan	    strcpy(buffer + 1, tp->term_names);
335166124Srafan	    data.size = strlen(tp->term_names) + 1;
336166124Srafan
337166124Srafan	    _nc_db_put(capdb, &key, &data);
338166124Srafan
339166124Srafan	    while (*other_names != '\0') {
340166124Srafan		ptr = other_names++;
341166124Srafan		while (*other_names != '|' && *other_names != '\0')
342166124Srafan		    other_names++;
343166124Srafan
344166124Srafan		if (*other_names != '\0')
345166124Srafan		    *(other_names++) = '\0';
346166124Srafan
347166124Srafan		key.data = ptr;
348166124Srafan		key.size = strlen(ptr);
349166124Srafan
350166124Srafan		_nc_db_put(capdb, &key, &data);
351166124Srafan	    }
352166124Srafan	    _nc_db_close(capdb);
353166124Srafan	}
354166124Srafan    }
355166124Srafan#else /* !USE_HASHED_DB */
356166124Srafan    if (call_count++ == 0) {
357166124Srafan	start_time = 0;
358166124Srafan    }
359166124Srafan
360184989Srafan    if (strlen(first_name) >= sizeof(filename) - 3)
36162449Speter	_nc_warning("terminal name too long.");
36250276Speter
363174993Srafan    sprintf(filename, LEAF_FMT "/%s", first_name[0], first_name);
36450276Speter
36562449Speter    /*
36662449Speter     * Has this primary name been written since the first call to
36762449Speter     * write_entry()?  If so, the newer write will step on the older,
36862449Speter     * so warn the user.
36962449Speter     */
37062449Speter    if (start_time > 0 &&
37162449Speter	stat(filename, &statbuf) >= 0
37262449Speter	&& statbuf.st_mtime >= start_time) {
37362449Speter	_nc_warning("name multiply defined.");
37462449Speter    }
37550276Speter
37662449Speter    check_writeable(first_name[0]);
37762449Speter    write_file(filename, tp);
37850276Speter
37962449Speter    if (start_time == 0) {
38062449Speter	if (stat(filename, &statbuf) < 0
38162449Speter	    || (start_time = statbuf.st_mtime) == 0) {
38262449Speter	    _nc_syserr_abort("error obtaining time from %s/%s",
38376726Speter			     _nc_tic_dir(0), filename);
38450276Speter	}
38562449Speter    }
38662449Speter    while (*other_names != '\0') {
38762449Speter	ptr = other_names++;
388184989Srafan	assert(ptr < buffer + sizeof(buffer) - 1);
38962449Speter	while (*other_names != '|' && *other_names != '\0')
39062449Speter	    other_names++;
39150276Speter
39262449Speter	if (*other_names != '\0')
39362449Speter	    *(other_names++) = '\0';
39450276Speter
39562449Speter	if (strlen(ptr) > sizeof(linkname) - 3) {
39662449Speter	    _nc_warning("terminal alias %s too long.", ptr);
39762449Speter	    continue;
39862449Speter	}
39962449Speter	if (strchr(ptr, '/') != 0) {
40062449Speter	    _nc_warning("cannot link alias %s.", ptr);
40162449Speter	    continue;
40262449Speter	}
40350276Speter
40462449Speter	check_writeable(ptr[0]);
405174993Srafan	sprintf(linkname, LEAF_FMT "/%s", ptr[0], ptr);
40650276Speter
40762449Speter	if (strcmp(filename, linkname) == 0) {
40862449Speter	    _nc_warning("self-synonym ignored");
40962449Speter	} else if (stat(linkname, &statbuf) >= 0 &&
41076726Speter		   statbuf.st_mtime < start_time) {
41162449Speter	    _nc_warning("alias %s multiply defined.", ptr);
41262449Speter	} else if (_nc_access(linkname, W_OK) == 0)
41350276Speter#if HAVE_LINK
41462449Speter	{
41562449Speter	    int code;
41650276Speter#if USE_SYMLINKS
41762449Speter	    strcpy(symlinkname, "../");
41862449Speter	    strncat(symlinkname, filename, sizeof(symlinkname) - 4);
41962449Speter	    symlinkname[sizeof(symlinkname) - 1] = '\0';
42050276Speter#endif /* USE_SYMLINKS */
42150276Speter#if HAVE_REMOVE
42262449Speter	    code = remove(linkname);
42350276Speter#else
42462449Speter	    code = unlink(linkname);
42550276Speter#endif
42662449Speter	    if (code != 0 && errno == ENOENT)
42762449Speter		code = 0;
42850276Speter#if USE_SYMLINKS
42962449Speter	    if (symlink(symlinkname, linkname) < 0)
43050276Speter#else
43162449Speter	    if (link(filename, linkname) < 0)
43250276Speter#endif /* USE_SYMLINKS */
43362449Speter	    {
43462449Speter		/*
43562449Speter		 * If there wasn't anything there, and we cannot
43662449Speter		 * link to the target because it is the same as the
43762449Speter		 * target, then the source must be on a filesystem
43862449Speter		 * that uses caseless filenames, such as Win32, etc.
43962449Speter		 */
44062449Speter		if (code == 0 && errno == EEXIST)
44162449Speter		    _nc_warning("can't link %s to %s", filename, linkname);
44276726Speter		else if (code == 0 && (errno == EPERM || errno == ENOENT))
44362449Speter		    write_file(linkname, tp);
44476726Speter		else {
44576726Speter#if MIXEDCASE_FILENAMES
44662449Speter		    _nc_syserr_abort("can't link %s to %s", filename, linkname);
44776726Speter#else
44876726Speter		    _nc_warning("can't link %s to %s (errno=%d)", filename,
44976726Speter				linkname, errno);
45076726Speter#endif
45176726Speter		}
45262449Speter	    } else {
45362449Speter		DEBUG(1, ("Linked %s", linkname));
45462449Speter	    }
45562449Speter	}
45650276Speter#else /* just make copies */
45762449Speter	    write_file(linkname, tp);
45850276Speter#endif /* HAVE_LINK */
45962449Speter    }
460166124Srafan#endif /* USE_HASHED_DB */
46150276Speter}
46250276Speter
463166124Srafanstatic unsigned
464166124Srafanfake_write(char *dst,
465166124Srafan	   unsigned *offset,
466166124Srafan	   unsigned limit,
467166124Srafan	   char *src,
468166124Srafan	   unsigned want,
469166124Srafan	   unsigned size)
470166124Srafan{
471166124Srafan    int have = (limit - *offset);
472166124Srafan
473166124Srafan    want *= size;
474166124Srafan    if (have > 0) {
475166124Srafan	if ((int) want > have)
476166124Srafan	    want = have;
477166124Srafan	memcpy(dst + *offset, src, want);
478166124Srafan	*offset += want;
479166124Srafan    } else {
480166124Srafan	want = 0;
481166124Srafan    }
482166124Srafan    return (int) (want / size);
483166124Srafan}
484166124Srafan
485166124Srafan#define Write(buf, size, count) fake_write(buffer, offset, limit, (char *) buf, count, size)
486166124Srafan
48762449Speter#undef LITTLE_ENDIAN		/* BSD/OS defines this as a feature macro */
48850276Speter#define HI(x)			((x) / 256)
48950276Speter#define LO(x)			((x) % 256)
49050276Speter#define LITTLE_ENDIAN(p, x)	(p)[0] = LO(x), (p)[1] = HI(x)
49150276Speter
492166124Srafan#define WRITE_STRING(str) (Write(str, sizeof(char), strlen(str) + 1) == strlen(str) + 1)
49350276Speter
49462449Speterstatic int
49597049Spetercompute_offsets(char **Strings, unsigned strmax, short *offsets)
49650276Speter{
49750276Speter    size_t nextfree = 0;
49897049Speter    unsigned i;
49950276Speter
50050276Speter    for (i = 0; i < strmax; i++) {
50150276Speter	if (Strings[i] == ABSENT_STRING) {
50250276Speter	    offsets[i] = -1;
50350276Speter	} else if (Strings[i] == CANCELLED_STRING) {
50450276Speter	    offsets[i] = -2;
50550276Speter	} else {
50650276Speter	    offsets[i] = nextfree;
50750276Speter	    nextfree += strlen(Strings[i]) + 1;
508174993Srafan	    TRACE_OUT(("put Strings[%d]=%s(%d)", (int) i,
509184989Srafan		       _nc_visbuf(Strings[i]), (int) nextfree));
51050276Speter	}
51150276Speter    }
51250276Speter    return nextfree;
51350276Speter}
51450276Speter
51562449Speterstatic void
51697049Speterconvert_shorts(unsigned char *buf, short *Numbers, unsigned count)
51750276Speter{
51897049Speter    unsigned i;
51950276Speter    for (i = 0; i < count; i++) {
52062449Speter	if (Numbers[i] == ABSENT_NUMERIC) {	/* HI/LO won't work */
52162449Speter	    buf[2 * i] = buf[2 * i + 1] = 0377;
52262449Speter	} else if (Numbers[i] == CANCELLED_NUMERIC) {	/* HI/LO won't work */
52362449Speter	    buf[2 * i] = 0376;
52462449Speter	    buf[2 * i + 1] = 0377;
52550276Speter	} else {
52662449Speter	    LITTLE_ENDIAN(buf + 2 * i, Numbers[i]);
52750276Speter	    TRACE_OUT(("put Numbers[%d]=%d", i, Numbers[i]));
52850276Speter	}
52950276Speter    }
53050276Speter}
53150276Speter
53250276Speter#define even_boundary(value) \
533166124Srafan	    ((value) % 2 != 0 && Write(&zero, sizeof(char), 1) != 1)
53450276Speter
535166124Srafan#if NCURSES_XNAMES
536166124Srafanstatic unsigned
537166124Srafanextended_Booleans(TERMTYPE *tp)
538166124Srafan{
539166124Srafan    unsigned short result = 0;
540166124Srafan    unsigned short i;
541166124Srafan
542166124Srafan    for (i = 0; i < tp->ext_Booleans; ++i) {
543166124Srafan	if (tp->Booleans[BOOLCOUNT + i] == TRUE)
544166124Srafan	    result = (i + 1);
545166124Srafan    }
546166124Srafan    return result;
547166124Srafan}
548166124Srafan
549166124Srafanstatic unsigned
550166124Srafanextended_Numbers(TERMTYPE *tp)
551166124Srafan{
552166124Srafan    unsigned short result = 0;
553166124Srafan    unsigned short i;
554166124Srafan
555166124Srafan    for (i = 0; i < tp->ext_Numbers; ++i) {
556166124Srafan	if (tp->Numbers[NUMCOUNT + i] != ABSENT_NUMERIC)
557166124Srafan	    result = (i + 1);
558166124Srafan    }
559166124Srafan    return result;
560166124Srafan}
561166124Srafan
562166124Srafanstatic unsigned
563166124Srafanextended_Strings(TERMTYPE *tp)
564166124Srafan{
565166124Srafan    unsigned short result = 0;
566166124Srafan    unsigned short i;
567166124Srafan
568166124Srafan    for (i = 0; i < tp->ext_Strings; ++i) {
569166124Srafan	if (tp->Strings[STRCOUNT + i] != ABSENT_STRING)
570166124Srafan	    result = (i + 1);
571166124Srafan    }
572166124Srafan    return result;
573166124Srafan}
574166124Srafan
575166124Srafan/*
576166124Srafan * _nc_align_termtype() will extend entries that are referenced in a use=
577166124Srafan * clause - discard the unneeded data.
578166124Srafan */
579166124Srafanstatic bool
580166124Srafanextended_object(TERMTYPE *tp)
581166124Srafan{
582166124Srafan    bool result = FALSE;
583166124Srafan
584166124Srafan    if (_nc_user_definable) {
585166124Srafan	result = ((extended_Booleans(tp)
586166124Srafan		   + extended_Numbers(tp)
587166124Srafan		   + extended_Strings(tp)) != 0);
588166124Srafan    }
589166124Srafan    return result;
590166124Srafan}
591166124Srafan#endif
592166124Srafan
59362449Speterstatic int
594166124Srafanwrite_object(TERMTYPE *tp, char *buffer, unsigned *offset, unsigned limit)
59550276Speter{
59662449Speter    char *namelist;
59762449Speter    size_t namelen, boolmax, nummax, strmax;
59862449Speter    char zero = '\0';
59962449Speter    size_t i;
60062449Speter    short nextfree;
60162449Speter    short offsets[MAX_ENTRY_SIZE / 2];
60262449Speter    unsigned char buf[MAX_ENTRY_SIZE];
60362449Speter    unsigned last_bool = BOOLWRITE;
60462449Speter    unsigned last_num = NUMWRITE;
60562449Speter    unsigned last_str = STRWRITE;
60650276Speter
60762449Speter#if NCURSES_XNAMES
60862449Speter    /*
60962449Speter     * Normally we limit the list of values to exclude the "obsolete"
61062449Speter     * capabilities.  However, if we are accepting extended names, add
61162449Speter     * these as well, since they are used for supporting translation
61262449Speter     * to/from termcap.
61362449Speter     */
61462449Speter    if (_nc_user_definable) {
61562449Speter	last_bool = BOOLCOUNT;
61662449Speter	last_num = NUMCOUNT;
61762449Speter	last_str = STRCOUNT;
61862449Speter    }
61962449Speter#endif
62050276Speter
62162449Speter    namelist = tp->term_names;
62262449Speter    namelen = strlen(namelist) + 1;
62350276Speter
62462449Speter    boolmax = 0;
62562449Speter    for (i = 0; i < last_bool; i++) {
62662449Speter	if (tp->Booleans[i] == TRUE)
62762449Speter	    boolmax = i + 1;
62862449Speter    }
62950276Speter
63062449Speter    nummax = 0;
63162449Speter    for (i = 0; i < last_num; i++) {
63262449Speter	if (tp->Numbers[i] != ABSENT_NUMERIC)
63362449Speter	    nummax = i + 1;
63462449Speter    }
63550276Speter
63662449Speter    strmax = 0;
63762449Speter    for (i = 0; i < last_str; i++) {
63862449Speter	if (tp->Strings[i] != ABSENT_STRING)
63962449Speter	    strmax = i + 1;
64062449Speter    }
64150276Speter
64262449Speter    nextfree = compute_offsets(tp->Strings, strmax, offsets);
64350276Speter
64462449Speter    /* fill in the header */
64562449Speter    LITTLE_ENDIAN(buf, MAGIC);
64662449Speter    LITTLE_ENDIAN(buf + 2, min(namelen, MAX_NAME_SIZE + 1));
64762449Speter    LITTLE_ENDIAN(buf + 4, boolmax);
64862449Speter    LITTLE_ENDIAN(buf + 6, nummax);
64962449Speter    LITTLE_ENDIAN(buf + 8, strmax);
65062449Speter    LITTLE_ENDIAN(buf + 10, nextfree);
65150276Speter
65262449Speter    /* write out the header */
653166124Srafan    TRACE_OUT(("Header of %s @%d", namelist, *offset));
654166124Srafan    if (Write(buf, 12, 1) != 1
655166124Srafan	|| Write(namelist, sizeof(char), namelen) != namelen)
65662449Speter	  return (ERR);
65750276Speter
65862449Speter    for (i = 0; i < boolmax; i++)
65962449Speter	if (tp->Booleans[i] == TRUE)
66062449Speter	    buf[i] = TRUE;
66162449Speter	else
66262449Speter	    buf[i] = FALSE;
663166124Srafan    if (Write(buf, sizeof(char), boolmax) != boolmax)
66462449Speter	  return (ERR);
66550276Speter
66662449Speter    if (even_boundary(namelen + boolmax))
66762449Speter	return (ERR);
66850276Speter
669166124Srafan    TRACE_OUT(("Numerics begin at %04x", *offset));
67050276Speter
67162449Speter    /* the numerics */
67262449Speter    convert_shorts(buf, tp->Numbers, nummax);
673166124Srafan    if (Write(buf, 2, nummax) != nummax)
67462449Speter	return (ERR);
67550276Speter
676166124Srafan    TRACE_OUT(("String offsets begin at %04x", *offset));
67750276Speter
67862449Speter    /* the string offsets */
67962449Speter    convert_shorts(buf, offsets, strmax);
680166124Srafan    if (Write(buf, 2, strmax) != strmax)
68162449Speter	return (ERR);
68250276Speter
683166124Srafan    TRACE_OUT(("String table begins at %04x", *offset));
68462449Speter
68562449Speter    /* the strings */
68662449Speter    for (i = 0; i < strmax; i++)
68762449Speter	if (VALID_STRING(tp->Strings[i]))
68862449Speter	    if (!WRITE_STRING(tp->Strings[i]))
68962449Speter		return (ERR);
69062449Speter
69150276Speter#if NCURSES_XNAMES
692166124Srafan    if (extended_object(tp)) {
69362449Speter	unsigned extcnt = NUM_EXT_NAMES(tp);
69450276Speter
69562449Speter	if (even_boundary(nextfree))
69662449Speter	    return (ERR);
69750276Speter
698184989Srafan	nextfree = compute_offsets(tp->Strings + STRCOUNT,
699184989Srafan				   tp->ext_Strings,
700184989Srafan				   offsets);
70162449Speter	TRACE_OUT(("after extended string capabilities, nextfree=%d", nextfree));
702184989Srafan
703184989Srafan	if (tp->ext_Strings >= SIZEOF(offsets))
704184989Srafan	    return (ERR);
705184989Srafan
706184989Srafan	nextfree += compute_offsets(tp->ext_Names,
707184989Srafan				    extcnt,
708184989Srafan				    offsets + tp->ext_Strings);
70962449Speter	TRACE_OUT(("after extended capnames, nextfree=%d", nextfree));
71062449Speter	strmax = tp->ext_Strings + extcnt;
71150276Speter
71262449Speter	/*
71362449Speter	 * Write the extended header
71462449Speter	 */
71562449Speter	LITTLE_ENDIAN(buf + 0, tp->ext_Booleans);
71662449Speter	LITTLE_ENDIAN(buf + 2, tp->ext_Numbers);
71762449Speter	LITTLE_ENDIAN(buf + 4, tp->ext_Strings);
71862449Speter	LITTLE_ENDIAN(buf + 6, strmax);
71962449Speter	LITTLE_ENDIAN(buf + 8, nextfree);
720166124Srafan	TRACE_OUT(("WRITE extended-header @%d", *offset));
721166124Srafan	if (Write(buf, 10, 1) != 1)
72262449Speter	    return (ERR);
72350276Speter
724166124Srafan	TRACE_OUT(("WRITE %d booleans @%d", tp->ext_Booleans, *offset));
72562449Speter	if (tp->ext_Booleans
726166124Srafan	    && Write(tp->Booleans + BOOLCOUNT, sizeof(char),
727166124Srafan		     tp->ext_Booleans) != tp->ext_Booleans)
72862449Speter	      return (ERR);
72950276Speter
73062449Speter	if (even_boundary(tp->ext_Booleans))
73162449Speter	    return (ERR);
73250276Speter
733166124Srafan	TRACE_OUT(("WRITE %d numbers @%d", tp->ext_Numbers, *offset));
73462449Speter	if (tp->ext_Numbers) {
73562449Speter	    convert_shorts(buf, tp->Numbers + NUMCOUNT, tp->ext_Numbers);
736166124Srafan	    if (Write(buf, 2, tp->ext_Numbers) != tp->ext_Numbers)
73762449Speter		return (ERR);
73862449Speter	}
73950276Speter
74062449Speter	/*
74162449Speter	 * Convert the offsets for the ext_Strings and ext_Names tables,
74262449Speter	 * in that order.
74362449Speter	 */
74462449Speter	convert_shorts(buf, offsets, strmax);
745166124Srafan	TRACE_OUT(("WRITE offsets @%d", *offset));
746166124Srafan	if (Write(buf, 2, strmax) != strmax)
74762449Speter	    return (ERR);
74850276Speter
74962449Speter	/*
75062449Speter	 * Write the string table after the offset tables so we do not
75162449Speter	 * have to do anything about alignment.
75262449Speter	 */
75362449Speter	for (i = 0; i < tp->ext_Strings; i++) {
75462449Speter	    if (VALID_STRING(tp->Strings[i + STRCOUNT])) {
755174993Srafan		TRACE_OUT(("WRITE ext_Strings[%d]=%s", (int) i,
75676726Speter			   _nc_visbuf(tp->Strings[i + STRCOUNT])));
75762449Speter		if (!WRITE_STRING(tp->Strings[i + STRCOUNT]))
75862449Speter		    return (ERR);
75950276Speter	    }
76062449Speter	}
76150276Speter
76262449Speter	/*
76362449Speter	 * Write the extended names
76462449Speter	 */
76562449Speter	for (i = 0; i < extcnt; i++) {
766174993Srafan	    TRACE_OUT(("WRITE ext_Names[%d]=%s", (int) i, tp->ext_Names[i]));
76762449Speter	    if (!WRITE_STRING(tp->ext_Names[i]))
76862449Speter		return (ERR);
76962449Speter	}
77050276Speter
77162449Speter    }
77250276Speter#endif /* NCURSES_XNAMES */
77350276Speter
77462449Speter    total_written++;
77562449Speter    return (OK);
77650276Speter}
77750276Speter
77850276Speter/*
77950276Speter * Returns the total number of entries written by this process
78050276Speter */
78176726SpeterNCURSES_EXPORT(int)
78262449Speter_nc_tic_written(void)
78350276Speter{
78462449Speter    return total_written;
78550276Speter}
786