/*
 * Convert a string to visible form.
 */
static char *
visible(const char *string)
{
    char *result = 0;
    size_t need = 1;
    int pass;
    int n;

    if (string != 0 && *string != '\0') {
	for (pass = 0; pass < 2; ++pass) {
	    for (n = 0; string[n] != '\0'; ++n) {
		char temp[80];
		_nc_STRNCPY(temp, visichar(string[n]), sizeof(temp) - 2);
		if (pass) {
		    _nc_STRCAT(result, temp, need);
		} else {
		    need += strlen(temp);
		}
	    }
	    if (!pass)
		result = typeCalloc(char, need);
	}
    } else {
_nc_write_entry(TERMTYPE2 *const tp)
{
#if USE_HASHED_DB

    char buffer[MAX_ENTRY_SIZE + 1];
    unsigned limit = sizeof(buffer);
    unsigned offset = 0;

#else /* !USE_HASHED_DB */

    struct stat statbuf;
    char filename[PATH_MAX];
    char linkname[PATH_MAX];
#if USE_SYMLINKS
    char symlinkname[PATH_MAX];
#if !HAVE_LINK
#undef HAVE_LINK
#define HAVE_LINK 1
#endif
#endif /* USE_SYMLINKS */

    unsigned limit2 = sizeof(filename) - (2 + LEAF_LEN);
    char saved = '\0';

    static int call_count;
    static time_t start_time;	/* time at start of writes */

#endif /* USE_HASHED_DB */

    char name_list[MAX_TERMINFO_LENGTH];
    char *first_name, *other_names;
    char *ptr;
    char *term_names = tp->term_names;
    size_t name_size = strlen(term_names);

    if (name_size == 0) {
	_nc_syserr_abort("no terminal name found.");
    } else if (name_size >= sizeof(name_list) - 1) {
	_nc_syserr_abort("terminal name too long: %s", term_names);
    }

    _nc_STRCPY(name_list, term_names, sizeof(name_list));
    DEBUG(7, ("Name list = '%s'", name_list));

    first_name = name_list;

    ptr = &name_list[name_size - 1];
    other_names = ptr + 1;

    while (ptr > name_list && *ptr != '|')
	ptr--;

    if (ptr != name_list) {
	*ptr = '\0';

	for (ptr = name_list; *ptr != '\0' && *ptr != '|'; ptr++)
	    continue;

	if (*ptr == '\0')
	    other_names = ptr;
	else {
	    *ptr = '\0';
	    other_names = ptr + 1;
	}
    }

    DEBUG(7, ("First name = '%s'", first_name));
    DEBUG(7, ("Other names = '%s'", other_names));

    _nc_set_type(first_name);

#if USE_HASHED_DB
    if (_nc_write_object(tp, buffer + 1, &offset, limit - 1) != ERR) {
	DB *capdb = _nc_db_open(_nc_tic_dir(0), TRUE);
	DBT key, data;

	if (capdb != 0) {
	    buffer[0] = 0;

	    memset(&key, 0, sizeof(key));
	    key.data = term_names;
	    key.size = name_size;

	    memset(&data, 0, sizeof(data));
	    data.data = buffer;
	    data.size = offset + 1;

	    _nc_db_put(capdb, &key, &data);

	    buffer[0] = 2;

	    key.data = name_list;
	    key.size = strlen(name_list);

	    _nc_STRCPY(buffer + 1,
		       term_names,
		       sizeof(buffer) - 1);
	    data.size = name_size + 1;

	    total_size += data.size;
	    total_parts++;
	    _nc_db_put(capdb, &key, &data);

	    while (*other_names != '\0') {
		ptr = other_names++;
		assert(ptr < buffer + sizeof(buffer) - 1);
		while (*other_names != '|' && *other_names != '\0')
		    other_names++;

		if (*other_names != '\0')
		    *(other_names++) = '\0';

		key.data = ptr;
		key.size = strlen(ptr);

		total_size += data.size;
		total_parts++;
		_nc_db_put(capdb, &key, &data);
	    }
	}
    }
#else /* !USE_HASHED_DB */
    if (call_count++ == 0) {
	start_time = 0;
    }

    if (strlen(first_name) >= sizeof(filename) - (2 + LEAF_LEN)) {
	_nc_warning("terminal name too long.");
	saved = first_name[limit2];
	first_name[limit2] = '\0';
    }

    _nc_SPRINTF(filename, _nc_SLIMIT(sizeof(filename))
		LEAF_FMT "/%s", first_name[0], first_name);

    if (saved)
	first_name[limit2] = saved;

    /*
     * Has this primary name been written since the first call to
     * write_entry()?  If so, the newer write will step on the older,
     * so warn the user.
     */
    if (start_time > 0 &&
	stat(filename, &statbuf) >= 0
	&& statbuf.st_mtime >= start_time) {
#if HAVE_LINK && !USE_SYMLINKS
	/*
	 * If the file has more than one link, the reason for the previous
	 * write could be that the current primary name used to be an alias for
	 * the previous entry.  In that case, unlink the file so that we will
	 * not modify the previous entry as we write this one.
	 */
	if (statbuf.st_nlink > 1) {
	    _nc_warning("name redefined.");
	    unlink(filename);
	} else {
	    _nc_warning("name multiply defined.");
	}
#else
	_nc_warning("name multiply defined.");
#endif
    }

    check_writeable(first_name[0]);
    write_file(filename, tp);

    if (start_time == 0) {
	if (stat(filename, &statbuf) < 0
	    || (start_time = statbuf.st_mtime) == 0) {
	    _nc_syserr_abort("error obtaining time from %s/%s",
			     _nc_tic_dir(0), filename);
	}
    }
    while (*other_names != '\0') {
	ptr = other_names++;
	while (*other_names != '|' && *other_names != '\0')
	    other_names++;

	if (*other_names != '\0')
	    *(other_names++) = '\0';

	if (strlen(ptr) > sizeof(linkname) - (2 + LEAF_LEN)) {
	    _nc_warning("terminal alias %s too long.", ptr);
	    continue;
	}
	if (strchr(ptr, '/') != 0) {
	    _nc_warning("cannot link alias %s.", ptr);
	    continue;
	}

	check_writeable(ptr[0]);
	_nc_SPRINTF(linkname, _nc_SLIMIT(sizeof(linkname))
		    LEAF_FMT "/%s", ptr[0], ptr);

	if (strcmp(filename, linkname) == 0) {
	    _nc_warning("self-synonym ignored");
	} else if (stat(linkname, &statbuf) >= 0 &&
		   statbuf.st_mtime < start_time) {
	    _nc_warning("alias %s multiply defined.", ptr);
	} else if (_nc_access(linkname, W_OK) == 0)
#if HAVE_LINK
	{
	    int code;
#if USE_SYMLINKS
#define MY_SIZE sizeof(symlinkname) - 1
	    if (first_name[0] == linkname[0]) {
		_nc_STRNCPY(symlinkname, first_name, MY_SIZE);
	    } else {
		_nc_STRCPY(symlinkname, "../", sizeof(symlinkname));
		_nc_STRNCPY(symlinkname + 3, filename, MY_SIZE - 3);
	    }
	    symlinkname[MY_SIZE] = '\0';
#endif /* USE_SYMLINKS */
#if HAVE_REMOVE
	    code = remove(linkname);
#else
	    code = unlink(linkname);
#endif
	    if (code != 0 && errno == ENOENT)
		code = 0;
#if USE_SYMLINKS
	    if (symlink(symlinkname, linkname) < 0)
#else
	    if (link(filename, linkname) < 0)
#endif /* USE_SYMLINKS */
	    {
		/*
		 * If there wasn't anything there, and we cannot
		 * link to the target because it is the same as the
		 * target, then the source must be on a filesystem
		 * that uses caseless filenames, such as Win32, etc.
		 */
		if (code == 0 && errno == EEXIST)
		    _nc_warning("can't link %s to %s", filename, linkname);
		else if (code == 0 && (errno == EPERM || errno == ENOENT))
		    write_file(linkname, tp);
		else {
#if MIXEDCASE_FILENAMES
		    _nc_syserr_abort("can't link %s to %s", filename, linkname);
#else
		    _nc_warning("can't link %s to %s (errno=%d)", filename,
				linkname, errno);
#endif
		}
	    } else {
		DEBUG(1, ("Linked %s", linkname));
	    }
	}
#else /* just make copies */
	    write_file(linkname, tp);
#endif /* HAVE_LINK */
    }
#endif /* USE_HASHED_DB */
}