static_fn Namval_t *scope(Namval_t *np, struct lval *lvalue, int assign) {
    int flag = lvalue->flag;
    char *sub = 0, *cp = (char *)np;
    Namval_t *mp;
    Shell_t *shp = lvalue->shp;
    int c = 0, nosub = lvalue->nosub;
    Dt_t *sdict = (shp->st.real_fun ? shp->st.real_fun->sdict : 0);
    Dt_t *nsdict = (shp->namespace ? nv_dict(shp->namespace) : 0);
    Dt_t *root = shp->var_tree;

    nvflag_t nvflags = assign ? NV_ASSIGN : 0;
    lvalue->nosub = 0;
    if (nosub < 0 && lvalue->ovalue) return (Namval_t *)lvalue->ovalue;
    lvalue->ovalue = NULL;
    if (cp >= lvalue->expr && cp < lvalue->expr + lvalue->elen) {
        int offset;
        // Do binding to node now.
        int d = cp[flag];
        cp[flag] = 0;
        np = nv_open(cp, root, nvflags | NV_VARNAME | NV_NOADD | NV_NOFAIL);
        if ((!np || nv_isnull(np)) && sh_macfun(shp, cp, offset = stktell(shp->stk))) {
            Fun = sh_arith(shp, sub = stkptr(shp->stk, offset));
            STORE_VT(FunNode.nvalue, sfdoublep, &Fun);
            FunNode.nvshell = shp;
            nv_onattr(&FunNode, NV_NOFREE | NV_LDOUBLE | NV_RDONLY);
            cp[flag] = d;
            return &FunNode;
        }
        if (!np && assign) {
            np = nv_open(cp, root, nvflags | NV_VARNAME);
        }
        cp[flag] = d;
        if (!np) return 0;
        root = shp->last_root;
        if (cp[flag + 1] == '[') {
            flag++;
        } else {
            flag = 0;
        }
    }

    if ((lvalue->emode & ARITH_COMP) && dtvnext(root)) {
        mp = nv_search_namval(np, sdict ? sdict : root, NV_NOSCOPE);
        if (!mp && nsdict) mp = nv_search_namval(np, nsdict, 0);
        if (mp) np = mp;
    }

    while (nv_isref(np)) {
        sub = nv_refsub(np);
        np = nv_refnode(np);
        if (sub) nv_putsub(np, sub, 0, assign ? ARRAY_ADD : 0);
    }

    if (!nosub && flag) {
        int hasdot = 0;
        cp = (char *)&lvalue->expr[flag];
        if (sub) goto skip;
        sub = cp;
        while (1) {
            Namarr_t *ap;
            Namval_t *nq;
            cp = nv_endsubscript(np, cp, 0, shp);
            if (c || *cp == '.') {
                c = '.';
                while (*cp == '.') {
                    hasdot = 1;
                    cp++;
                    while (c = mb1char(&cp), isaname(c)) {
                        ;  // empty body
                    }
                }
                if (c == '[') continue;
            }
            flag = *cp;
            *cp = 0;
            if (c || hasdot) {
                sfprintf(shp->strbuf, "%s%s%c", nv_name(np), sub, 0);
                sub = sfstruse(shp->strbuf);
            }
            if (strchr(sub, '$')) sub = sh_mactrim(shp, sub, 0);
            *cp = flag;
            if (c || hasdot) {
                np = nv_open(sub, shp->var_tree, NV_VARNAME | nvflags);
                return np;
            }
            cp = nv_endsubscript(np, sub, (assign ? NV_ADD : 0) | NV_SUBQUOTE, np->nvshell);
            if (*cp != '[') break;
        skip:
            nq = nv_opensub(np);
            if (nq) {
                np = nq;
            } else {
                ap = nv_arrayptr(np);
                if (ap && !ap->table) {
                    ap->table = dtopen(&_Nvdisc, Dtoset);
                    dtuserdata(ap->table, shp, 1);
                }
                if (ap && ap->table && (nq = nv_search(nv_getsub(np), ap->table, NV_ADD))) {
                    nq->nvenv = np;
                }
                if (nq && nv_isnull(nq)) np = nv_arraychild(np, nq, 0);
            }
            sub = cp;
        }
    } else if (nosub > 0) {
        nv_putsub(np, NULL, nosub - 1, 0);
    }
    return np;
}


swtpm: Switch to open() from fopen() for the pidfile (CVE-2020-28407)

This patch addresses CVE-2020-28407.

Use the open() call rather than the fopen() call when creating a pidfile.
Also prevent us from following symbolic links when opening the pidfile for
writing.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

void pidfile_remove(void)
{
    if (!g_pidfile)
        return;

    unlink(g_pidfile);

    free(g_pidfile);
    g_pidfile = NULL;
}

In swtpm before 0.4.2 and 0.5.x before 0.5.1, a local attacker may be able to overwrite arbitrary files via a symlink attack against a temporary file such as TMP2-00.permall.


int pidfile_write(pid_t pid)
{
    int fd;
    char buffer[32];
    ssize_t nwritten;

    if (g_pidfile) {
        fd = open(g_pidfile, O_WRONLY|O_CREAT|O_TRUNC|O_NOFOLLOW,
                  S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    } else if (pidfilefd >= 0) {
        fd = pidfilefd;
        g_pidfile = fd_to_filename(pidfilefd);
        if (!g_pidfile)
            goto error;
    } else {
        return 0;
    }

    if (fd < 0) {
        logprintf(STDERR_FILENO, "Could not open pidfile %s : %s\n",
                  g_pidfile, strerror(errno));
        goto error;
    }

    if (snprintf(buffer, sizeof(buffer), "%d", pid) >= (int)sizeof(buffer)) {
        logprintf(STDERR_FILENO, "Could not write pid to buffer\n");
        goto error_close;
    }

    nwritten = write_full(fd, buffer, strlen(buffer));
    if (nwritten < 0 || nwritten != (ssize_t)strlen(buffer)) {
        logprintf(STDERR_FILENO, "Could not write to pidfile : %s\n",
                  strerror(errno));
        goto error_close;
    }

    close(fd);

    return 0;

error_close:
    if (fd != pidfilefd)
        close(fd);

error:
    return -1;
}


int pidfile_set(const char *pidfile)
{
   g_pidfile = strdup(pidfile);
   if (!g_pidfile) {
       logprintf(STDERR_FILENO, "Out of memory.\n");
       return -1;
   }

   return 0;
}


int pidfile_set_fd(int newpidfilefd)
{
    pidfilefd = newpidfilefd;

    return 0;
}


X11 forwarding - avoid unsafe use of magic cookie as arg to xauth command.

Magic cookie can leak through /proc this way. There is a race here between
this usually short-lived xauth command running and an attacker scraping
the value from /proc. This can be exacerbated through use of
X11Parameters=home_xauthority on a cluster with a shared home directory
under heavy load.

CVE-2020-27746.

extern int x11_set_xauth(char *xauthority, char *cookie,
			 char *host, uint16_t display)
{
	int i=0, status;
	char *result;
	char **xauth_argv;
	char template[] = "/tmp/xauth-source-XXXXXX";
	char *contents = NULL;
	int fd;

	/* protect against weak file permissions in old glibc */
	umask(0077);
	if ((fd = mkstemp(template)) < 0)
		fatal("%s: could not create temp file", __func__);

	xstrfmtcat(contents, "add %s/unix:%u MIT-MAGIC-COOKIE-1 %s\n",
		   host, display, cookie);
	safe_write(fd, contents, strlen(contents));
	xfree(contents);
	close(fd);

	xauth_argv = xmalloc(sizeof(char *) * 10);
	xauth_argv[i++] = "xauth";
	xauth_argv[i++] = "-v";
	xauth_argv[i++] = "-f";
	xauth_argv[i++] = xauthority;
	xauth_argv[i++] = "source";
	xauth_argv[i++] = template;
	xauth_argv[i++] = NULL;
	xassert(i < 10);

	result = run_command("xauth", XAUTH_PATH, xauth_argv, 10000, 0,
			     &status);

	(void) unlink(template);
	xfree(xauth_argv);

	debug2("%s: result from xauth: %s", __func__, result);
	xfree(result);

	return status;

rwfail:
	fatal("%s: could not write temporary xauth file", __func__);
	return SLURM_ERROR;
}

Slurm before 19.05.8 and 20.x before 20.02.6 exposes Sensitive Information to an Unauthorized Actor because xauth for X11 magic cookies is affected by a race condition in a read operation on the /proc filesystem.


const char *x11_flags2str(uint16_t flags)
{
	if (flags & X11_FORWARD_ALL)
		return "all";
	if (flags & X11_FORWARD_BATCH)
		return "batch";
	if (flags & X11_FORWARD_FIRST)
		return "first";
	if (flags & X11_FORWARD_LAST)
		return "last";

	return "unset";
}


extern int x11_delete_xauth(char *xauthority, char *host, uint16_t display)
{
	int i=0, status;
	char *result;
	char **xauth_argv;

	xauth_argv = xmalloc(sizeof(char *) * 10);
	xauth_argv[i++] = xstrdup("xauth");
	xauth_argv[i++] = xstrdup("-v");
	xauth_argv[i++] = xstrdup("-f");
	xauth_argv[i++] = xstrdup(xauthority);
	xauth_argv[i++] = xstrdup("remove");
	xauth_argv[i++] = xstrdup_printf("%s/unix:%u", host, display);
	xauth_argv[i++] = NULL;
	xassert(i < 10);

	result = run_command("xauth", XAUTH_PATH, xauth_argv, 10000, 0,
			     &status);

	free_command_argv(xauth_argv);

	debug2("%s: result from xauth: %s", __func__, result);
	xfree(result);

	return status;
}


uint16_t x11_str2flags(const char *str)
{
	uint16_t flags = 0;

	if (!xstrcasecmp(str, "all"))
		flags |= X11_FORWARD_ALL;
	if (!xstrcasecmp(str, "batch"))
		flags |= X11_FORWARD_BATCH;
	if (!xstrcasecmp(str, "first"))
		flags |= X11_FORWARD_FIRST;
	if (!xstrcasecmp(str, "last"))
		flags |= X11_FORWARD_LAST;

	return flags;
}


extern char *x11_get_xauth(void)
{
	int status, matchlen;
	char **xauth_argv;
	regex_t reg;
	regmatch_t regmatch[2];
	char *result, *cookie;
	/*
	 * Two real-world examples:
	 * "zoidberg/unix:10  MIT-MAGIC-COOKIE-1  abcdef0123456789"
	 * "zoidberg:10  MIT-MAGIC-COOKIE-1  abcdef0123456789"
	 *
	 * The "/unix" bit is optional, and captured in "[[:alnum:].-/]+:".
	 * '.' and '-' are also allowed in the hostname portion, so match them
	 * in addition to '/'.
	 *
	 * Warning: the '-' must be either first or last in the [] brackets,
	 * otherwise it will be interpreted as a range instead of the literal
	 * character.
	 */
	static char *cookie_pattern = "^[[:alnum:]./-]+:[[:digit:]]+"
				      "[[:space:]]+MIT-MAGIC-COOKIE-1"
				      "[[:space:]]+([[:xdigit:]]+)$";

	xauth_argv = xmalloc(sizeof(char *) * 10);
	xauth_argv[0] = xstrdup("xauth");
	xauth_argv[1] = xstrdup("list");
	xauth_argv[2] = xstrdup(getenv("DISPLAY"));

	result = run_command("xauth", XAUTH_PATH, xauth_argv, 10000, 0,
			     &status);

	debug2("%s: result from xauth: %s", __func__, result);

	free_command_argv(xauth_argv);

	if (status) {
		error("Problem running xauth command. "
		      "Cannot use X11 forwarding.");
		exit(-1);

	}

	regcomp(&reg, cookie_pattern, REG_EXTENDED|REG_NEWLINE);
	if (regexec(&reg, result, 2, regmatch, 0) == REG_NOMATCH) {
		error("%s: Could not retrieve magic cookie. "
		      "Cannot use X11 forwarding.", __func__);
		exit(-1);
	}

	matchlen = regmatch[1].rm_eo - regmatch[1].rm_so + 1;
	cookie = xmalloc(matchlen);
	strlcpy(cookie, result + regmatch[1].rm_so, matchlen);
	xfree(result);

	return cookie;
}


extern void x11_get_display(uint16_t *port, char **target)
{
	char *display, *port_split, *port_period;
	*target = NULL;

	display = xstrdup(getenv("DISPLAY"));

	if (!display) {
		error("No DISPLAY variable set, cannot setup x11 forwarding.");
		exit(-1);
	}

	if (display[0] == ':') {
		struct stat st;
		char *screen_period;
		*port = 0;
		screen_period = strchr(display, '.');
		if (screen_period)
			*screen_period = '\0';
		xstrfmtcat(*target, "/tmp/.X11-unix/X%s", display + 1);
		xfree(display);
		if (stat(*target, &st) != 0) {
			error("Cannot stat() local X11 socket `%s`", *target);
			exit(-1);
		}
		return;
	}

	/*
	 * Parse out port number
	 * Example: localhost/unix:89.0 or localhost/unix:89
	 */
	port_split = strchr(display, ':');
	if (!port_split) {
		error("Error parsing DISPLAY environment variable. "
		      "Cannot use X11 forwarding.");
		exit(-1);
	}
	*port_split = '\0';

	/*
	 * Handle the "screen" portion of the display port.
	 * Xorg does not require a screen to be specified, defaults to 0.
	 */
	port_split++;
	port_period = strchr(port_split, '.');
	if (port_period)
		*port_period = '\0';

	*port = atoi(port_split) + X11_TCP_PORT_OFFSET;
	*target = display;
}


mm: check that mm is still valid in madvise()

IORING_OP_MADVISE can end up basically doing mprotect() on the VM of
another process, which means that it can race with our crazy core dump
handling which accesses the VM state without holding the mmap_sem
(because it incorrectly thinks that it is the final user).

This is clearly a core dumping problem, but we've never fixed it the
right way, and instead have the notion of "check that the mm is still
ok" using mmget_still_valid() after getting the mmap_sem for writing in
any situation where we're not the original VM thread.

See commit 04f5866e41fb ("coredump: fix race condition between
mmget_not_zero()/get_task_mm() and core dumping") for more background on
this whole mmget_still_valid() thing.  You might want to have a barf bag
handy when you do.

We're discussing just fixing this properly in the only remaining core
dumping routines.  But even if we do that, let's make do_madvise() do
the right thing, and then when we fix core dumping, we can remove all
these mmget_still_valid() checks.

Reported-and-tested-by: Jann Horn <jannh@google.com>
Fixes: c1ca757bd6f4 ("io_uring: add IORING_OP_MADVISE")
Acked-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

static int madvise_cold_or_pageout_pte_range(pmd_t *pmd,
				unsigned long addr, unsigned long end,
				struct mm_walk *walk)
{
	struct madvise_walk_private *private = walk->private;
	struct mmu_gather *tlb = private->tlb;
	bool pageout = private->pageout;
	struct mm_struct *mm = tlb->mm;
	struct vm_area_struct *vma = walk->vma;
	pte_t *orig_pte, *pte, ptent;
	spinlock_t *ptl;
	struct page *page = NULL;
	LIST_HEAD(page_list);

	if (fatal_signal_pending(current))
		return -EINTR;

#ifdef CONFIG_TRANSPARENT_HUGEPAGE
	if (pmd_trans_huge(*pmd)) {
		pmd_t orig_pmd;
		unsigned long next = pmd_addr_end(addr, end);

		tlb_change_page_size(tlb, HPAGE_PMD_SIZE);
		ptl = pmd_trans_huge_lock(pmd, vma);
		if (!ptl)
			return 0;

		orig_pmd = *pmd;
		if (is_huge_zero_pmd(orig_pmd))
			goto huge_unlock;

		if (unlikely(!pmd_present(orig_pmd))) {
			VM_BUG_ON(thp_migration_supported() &&
					!is_pmd_migration_entry(orig_pmd));
			goto huge_unlock;
		}

		page = pmd_page(orig_pmd);

		/* Do not interfere with other mappings of this page */
		if (page_mapcount(page) != 1)
			goto huge_unlock;

		if (next - addr != HPAGE_PMD_SIZE) {
			int err;

			get_page(page);
			spin_unlock(ptl);
			lock_page(page);
			err = split_huge_page(page);
			unlock_page(page);
			put_page(page);
			if (!err)
				goto regular_page;
			return 0;
		}

		if (pmd_young(orig_pmd)) {
			pmdp_invalidate(vma, addr, pmd);
			orig_pmd = pmd_mkold(orig_pmd);

			set_pmd_at(mm, addr, pmd, orig_pmd);
			tlb_remove_pmd_tlb_entry(tlb, pmd, addr);
		}

		ClearPageReferenced(page);
		test_and_clear_page_young(page);
		if (pageout) {
			if (!isolate_lru_page(page)) {
				if (PageUnevictable(page))
					putback_lru_page(page);
				else
					list_add(&page->lru, &page_list);
			}
		} else
			deactivate_page(page);
huge_unlock:
		spin_unlock(ptl);
		if (pageout)
			reclaim_pages(&page_list);
		return 0;
	}

	if (pmd_trans_unstable(pmd))
		return 0;
regular_page:
#endif
	tlb_change_page_size(tlb, PAGE_SIZE);
	orig_pte = pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
	flush_tlb_batched_pending(mm);
	arch_enter_lazy_mmu_mode();
	for (; addr < end; pte++, addr += PAGE_SIZE) {
		ptent = *pte;

		if (pte_none(ptent))
			continue;

		if (!pte_present(ptent))
			continue;

		page = vm_normal_page(vma, addr, ptent);
		if (!page)
			continue;

		/*
		 * Creating a THP page is expensive so split it only if we
		 * are sure it's worth. Split it if we are only owner.
		 */
		if (PageTransCompound(page)) {
			if (page_mapcount(page) != 1)
				break;
			get_page(page);
			if (!trylock_page(page)) {
				put_page(page);
				break;
			}
			pte_unmap_unlock(orig_pte, ptl);
			if (split_huge_page(page)) {
				unlock_page(page);
				put_page(page);
				pte_offset_map_lock(mm, pmd, addr, &ptl);
				break;
			}
			unlock_page(page);
			put_page(page);
			pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
			pte--;
			addr -= PAGE_SIZE;
			continue;
		}

		/* Do not interfere with other mappings of this page */
		if (page_mapcount(page) != 1)
			continue;

		VM_BUG_ON_PAGE(PageTransCompound(page), page);

		if (pte_young(ptent)) {
			ptent = ptep_get_and_clear_full(mm, addr, pte,
							tlb->fullmm);
			ptent = pte_mkold(ptent);
			set_pte_at(mm, addr, pte, ptent);
			tlb_remove_tlb_entry(tlb, pte, addr);
		}

		/*
		 * We are deactivating a page for accelerating reclaiming.
		 * VM couldn't reclaim the page unless we clear PG_young.
		 * As a side effect, it makes confuse idle-page tracking
		 * because they will miss recent referenced history.
		 */
		ClearPageReferenced(page);
		test_and_clear_page_young(page);
		if (pageout) {
			if (!isolate_lru_page(page)) {
				if (PageUnevictable(page))
					putback_lru_page(page);
				else
					list_add(&page->lru, &page_list);
			}
		} else
			deactivate_page(page);
	}

	arch_leave_lazy_mmu_mode();
	pte_unmap_unlock(orig_pte, ptl);
	if (pageout)
		reclaim_pages(&page_list);
	cond_resched();

	return 0;
}


static void madvise_cold_page_range(struct mmu_gather *tlb,
			     struct vm_area_struct *vma,
			     unsigned long addr, unsigned long end)
{
	struct madvise_walk_private walk_private = {
		.pageout = false,
		.tlb = tlb,
	};

	tlb_start_vma(tlb, vma);
	walk_page_range(vma->vm_mm, addr, end, &cold_walk_ops, &walk_private);
	tlb_end_vma(tlb, vma);
}


static int madvise_free_pte_range(pmd_t *pmd, unsigned long addr,
				unsigned long end, struct mm_walk *walk)

{
	struct mmu_gather *tlb = walk->private;
	struct mm_struct *mm = tlb->mm;
	struct vm_area_struct *vma = walk->vma;
	spinlock_t *ptl;
	pte_t *orig_pte, *pte, ptent;
	struct page *page;
	int nr_swap = 0;
	unsigned long next;

	next = pmd_addr_end(addr, end);
	if (pmd_trans_huge(*pmd))
		if (madvise_free_huge_pmd(tlb, vma, pmd, addr, next))
			goto next;

	if (pmd_trans_unstable(pmd))
		return 0;

	tlb_change_page_size(tlb, PAGE_SIZE);
	orig_pte = pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
	flush_tlb_batched_pending(mm);
	arch_enter_lazy_mmu_mode();
	for (; addr != end; pte++, addr += PAGE_SIZE) {
		ptent = *pte;

		if (pte_none(ptent))
			continue;
		/*
		 * If the pte has swp_entry, just clear page table to
		 * prevent swap-in which is more expensive rather than
		 * (page allocation + zeroing).
		 */
		if (!pte_present(ptent)) {
			swp_entry_t entry;

			entry = pte_to_swp_entry(ptent);
			if (non_swap_entry(entry))
				continue;
			nr_swap--;
			free_swap_and_cache(entry);
			pte_clear_not_present_full(mm, addr, pte, tlb->fullmm);
			continue;
		}

		page = vm_normal_page(vma, addr, ptent);
		if (!page)
			continue;

		/*
		 * If pmd isn't transhuge but the page is THP and
		 * is owned by only this process, split it and
		 * deactivate all pages.
		 */
		if (PageTransCompound(page)) {
			if (page_mapcount(page) != 1)
				goto out;
			get_page(page);
			if (!trylock_page(page)) {
				put_page(page);
				goto out;
			}
			pte_unmap_unlock(orig_pte, ptl);
			if (split_huge_page(page)) {
				unlock_page(page);
				put_page(page);
				pte_offset_map_lock(mm, pmd, addr, &ptl);
				goto out;
			}
			unlock_page(page);
			put_page(page);
			pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
			pte--;
			addr -= PAGE_SIZE;
			continue;
		}

		VM_BUG_ON_PAGE(PageTransCompound(page), page);

		if (PageSwapCache(page) || PageDirty(page)) {
			if (!trylock_page(page))
				continue;
			/*
			 * If page is shared with others, we couldn't clear
			 * PG_dirty of the page.
			 */
			if (page_mapcount(page) != 1) {
				unlock_page(page);
				continue;
			}

			if (PageSwapCache(page) && !try_to_free_swap(page)) {
				unlock_page(page);
				continue;
			}

			ClearPageDirty(page);
			unlock_page(page);
		}

		if (pte_young(ptent) || pte_dirty(ptent)) {
			/*
			 * Some of architecture(ex, PPC) don't update TLB
			 * with set_pte_at and tlb_remove_tlb_entry so for
			 * the portability, remap the pte with old|clean
			 * after pte clearing.
			 */
			ptent = ptep_get_and_clear_full(mm, addr, pte,
							tlb->fullmm);

			ptent = pte_mkold(ptent);
			ptent = pte_mkclean(ptent);
			set_pte_at(mm, addr, pte, ptent);
			tlb_remove_tlb_entry(tlb, pte, addr);
		}
		mark_page_lazyfree(page);
	}
out:
	if (nr_swap) {
		if (current->mm == mm)
			sync_mm_rss(mm);

		add_mm_counter(mm, MM_SWAPENTS, nr_swap);
	}
	arch_leave_lazy_mmu_mode();
	pte_unmap_unlock(orig_pte, ptl);
	cond_resched();
next:
	return 0;
}


static inline bool can_do_pageout(struct vm_area_struct *vma)
{
	if (vma_is_anonymous(vma))
		return true;
	if (!vma->vm_file)
		return false;
	/*
	 * paging out pagecache only for non-anonymous mappings that correspond
	 * to the files the calling process could (if tried) open for writing;
	 * otherwise we'd be including shared non-exclusive mappings, which
	 * opens a side channel.
	 */
	return inode_owner_or_capable(file_inode(vma->vm_file)) ||
		inode_permission(file_inode(vma->vm_file), MAY_WRITE) == 0;
}


madvise_behavior_valid(int behavior)
{
	switch (behavior) {
	case MADV_DOFORK:
	case MADV_DONTFORK:
	case MADV_NORMAL:
	case MADV_SEQUENTIAL:
	case MADV_RANDOM:
	case MADV_REMOVE:
	case MADV_WILLNEED:
	case MADV_DONTNEED:
	case MADV_FREE:
	case MADV_COLD:
	case MADV_PAGEOUT:
#ifdef CONFIG_KSM
	case MADV_MERGEABLE:
	case MADV_UNMERGEABLE:
#endif
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
	case MADV_HUGEPAGE:
	case MADV_NOHUGEPAGE:
#endif
	case MADV_DONTDUMP:
	case MADV_DODUMP:
	case MADV_WIPEONFORK:
	case MADV_KEEPONFORK:
#ifdef CONFIG_MEMORY_FAILURE
	case MADV_SOFT_OFFLINE:
	case MADV_HWPOISON:
#endif
		return true;

	default:
		return false;
	}
}


static void madvise_pageout_page_range(struct mmu_gather *tlb,
			     struct vm_area_struct *vma,
			     unsigned long addr, unsigned long end)
{
	struct madvise_walk_private walk_private = {
		.pageout = true,
		.tlb = tlb,
	};

	tlb_start_vma(tlb, vma);
	walk_page_range(vma->vm_mm, addr, end, &cold_walk_ops, &walk_private);
	tlb_end_vma(tlb, vma);
}


static long madvise_dontneed_free(struct vm_area_struct *vma,
				  struct vm_area_struct **prev,
				  unsigned long start, unsigned long end,
				  int behavior)
{
	*prev = vma;
	if (!can_madv_lru_vma(vma))
		return -EINVAL;

	if (!userfaultfd_remove(vma, start, end)) {
		*prev = NULL; /* mmap_sem has been dropped, prev is stale */

		down_read(&current->mm->mmap_sem);
		vma = find_vma(current->mm, start);
		if (!vma)
			return -ENOMEM;
		if (start < vma->vm_start) {
			/*
			 * This "vma" under revalidation is the one
			 * with the lowest vma->vm_start where start
			 * is also < vma->vm_end. If start <
			 * vma->vm_start it means an hole materialized
			 * in the user address space within the
			 * virtual range passed to MADV_DONTNEED
			 * or MADV_FREE.
			 */
			return -ENOMEM;
		}
		if (!can_madv_lru_vma(vma))
			return -EINVAL;
		if (end > vma->vm_end) {
			/*
			 * Don't fail if end > vma->vm_end. If the old
			 * vma was splitted while the mmap_sem was
			 * released the effect of the concurrent
			 * operation may not cause madvise() to
			 * have an undefined result. There may be an
			 * adjacent next vma that we'll walk
			 * next. userfaultfd_remove() will generate an
			 * UFFD_EVENT_REMOVE repetition on the
			 * end-vma->vm_end range, but the manager can
			 * handle a repetition fine.
			 */
			end = vma->vm_end;
		}
		VM_WARN_ON(start >= end);
	}

	if (behavior == MADV_DONTNEED)
		return madvise_dontneed_single_vma(vma, start, end);
	else if (behavior == MADV_FREE)
		return madvise_free_single_vma(vma, start, end);
	else
		return -EINVAL;
}


static int madvise_free_single_vma(struct vm_area_struct *vma,
			unsigned long start_addr, unsigned long end_addr)
{
	struct mm_struct *mm = vma->vm_mm;
	struct mmu_notifier_range range;
	struct mmu_gather tlb;

	/* MADV_FREE works for only anon vma at the moment */
	if (!vma_is_anonymous(vma))
		return -EINVAL;

	range.start = max(vma->vm_start, start_addr);
	if (range.start >= vma->vm_end)
		return -EINVAL;
	range.end = min(vma->vm_end, end_addr);
	if (range.end <= vma->vm_start)
		return -EINVAL;
	mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, vma, mm,
				range.start, range.end);

	lru_add_drain();
	tlb_gather_mmu(&tlb, mm, range.start, range.end);
	update_hiwater_rss(mm);

	mmu_notifier_invalidate_range_start(&range);
	tlb_start_vma(&tlb, vma);
	walk_page_range(vma->vm_mm, range.start, range.end,
			&madvise_free_walk_ops, &tlb);
	tlb_end_vma(&tlb, vma);
	mmu_notifier_invalidate_range_end(&range);
	tlb_finish_mmu(&tlb, range.start, range.end);

	return 0;
}


static long madvise_behavior(struct vm_area_struct *vma,
		     struct vm_area_struct **prev,
		     unsigned long start, unsigned long end, int behavior)
{
	struct mm_struct *mm = vma->vm_mm;
	int error = 0;
	pgoff_t pgoff;
	unsigned long new_flags = vma->vm_flags;

	switch (behavior) {
	case MADV_NORMAL:
		new_flags = new_flags & ~VM_RAND_READ & ~VM_SEQ_READ;
		break;
	case MADV_SEQUENTIAL:
		new_flags = (new_flags & ~VM_RAND_READ) | VM_SEQ_READ;
		break;
	case MADV_RANDOM:
		new_flags = (new_flags & ~VM_SEQ_READ) | VM_RAND_READ;
		break;
	case MADV_DONTFORK:
		new_flags |= VM_DONTCOPY;
		break;
	case MADV_DOFORK:
		if (vma->vm_flags & VM_IO) {
			error = -EINVAL;
			goto out;
		}
		new_flags &= ~VM_DONTCOPY;
		break;
	case MADV_WIPEONFORK:
		/* MADV_WIPEONFORK is only supported on anonymous memory. */
		if (vma->vm_file || vma->vm_flags & VM_SHARED) {
			error = -EINVAL;
			goto out;
		}
		new_flags |= VM_WIPEONFORK;
		break;
	case MADV_KEEPONFORK:
		new_flags &= ~VM_WIPEONFORK;
		break;
	case MADV_DONTDUMP:
		new_flags |= VM_DONTDUMP;
		break;
	case MADV_DODUMP:
		if (!is_vm_hugetlb_page(vma) && new_flags & VM_SPECIAL) {
			error = -EINVAL;
			goto out;
		}
		new_flags &= ~VM_DONTDUMP;
		break;
	case MADV_MERGEABLE:
	case MADV_UNMERGEABLE:
		error = ksm_madvise(vma, start, end, behavior, &new_flags);
		if (error)
			goto out_convert_errno;
		break;
	case MADV_HUGEPAGE:
	case MADV_NOHUGEPAGE:
		error = hugepage_madvise(vma, &new_flags, behavior);
		if (error)
			goto out_convert_errno;
		break;
	}

	if (new_flags == vma->vm_flags) {
		*prev = vma;
		goto out;
	}

	pgoff = vma->vm_pgoff + ((start - vma->vm_start) >> PAGE_SHIFT);
	*prev = vma_merge(mm, *prev, start, end, new_flags, vma->anon_vma,
			  vma->vm_file, pgoff, vma_policy(vma),
			  vma->vm_userfaultfd_ctx);
	if (*prev) {
		vma = *prev;
		goto success;
	}

	*prev = vma;

	if (start != vma->vm_start) {
		if (unlikely(mm->map_count >= sysctl_max_map_count)) {
			error = -ENOMEM;
			goto out;
		}
		error = __split_vma(mm, vma, start, 1);
		if (error)
			goto out_convert_errno;
	}

	if (end != vma->vm_end) {
		if (unlikely(mm->map_count >= sysctl_max_map_count)) {
			error = -ENOMEM;
			goto out;
		}
		error = __split_vma(mm, vma, end, 0);
		if (error)
			goto out_convert_errno;
	}

success:
	/*
	 * vm_flags is protected by the mmap_sem held in write mode.
	 */
	vma->vm_flags = new_flags;

out_convert_errno:
	/*
	 * madvise() returns EAGAIN if kernel resources, such as
	 * slab, are temporarily unavailable.
	 */
	if (error == -ENOMEM)
		error = -EAGAIN;
out:
	return error;
}


static int madvise_inject_error(int behavior,
		unsigned long start, unsigned long end)
{
	struct page *page;
	struct zone *zone;
	unsigned long size;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;

	for (; start < end; start += size) {
		unsigned long pfn;
		int ret;

		ret = get_user_pages_fast(start, 1, 0, &page);
		if (ret != 1)
			return ret;
		pfn = page_to_pfn(page);

		/*
		 * When soft offlining hugepages, after migrating the page
		 * we dissolve it, therefore in the second loop "page" will
		 * no longer be a compound page.
		 */
		size = page_size(compound_head(page));

		if (PageHWPoison(page)) {
			put_page(page);
			continue;
		}

		if (behavior == MADV_SOFT_OFFLINE) {
			pr_info("Soft offlining pfn %#lx at process virtual address %#lx\n",
					pfn, start);

			ret = soft_offline_page(pfn, MF_COUNT_INCREASED);
			if (ret)
				return ret;
			continue;
		}

		pr_info("Injecting memory failure for pfn %#lx at process virtual address %#lx\n",
				pfn, start);

		/*
		 * Drop the page reference taken by get_user_pages_fast(). In
		 * the absence of MF_COUNT_INCREASED the memory_failure()
		 * routine is responsible for pinning the page to prevent it
		 * from being released back to the page allocator.
		 */
		put_page(page);
		ret = memory_failure(pfn, 0);
		if (ret)
			return ret;
	}

	/* Ensure that all poisoned pages are removed from per-cpu lists */
	for_each_populated_zone(zone)
		drain_all_pages(zone);

	return 0;
}


madvise_vma(struct vm_area_struct *vma, struct vm_area_struct **prev,
		unsigned long start, unsigned long end, int behavior)
{
	switch (behavior) {
	case MADV_REMOVE:
		return madvise_remove(vma, prev, start, end);
	case MADV_WILLNEED:
		return madvise_willneed(vma, prev, start, end);
	case MADV_COLD:
		return madvise_cold(vma, prev, start, end);
	case MADV_PAGEOUT:
		return madvise_pageout(vma, prev, start, end);
	case MADV_FREE:
	case MADV_DONTNEED:
		return madvise_dontneed_free(vma, prev, start, end, behavior);
	default:
		return madvise_behavior(vma, prev, start, end, behavior);
	}
}


static long madvise_willneed(struct vm_area_struct *vma,
			     struct vm_area_struct **prev,
			     unsigned long start, unsigned long end)
{
	struct file *file = vma->vm_file;
	loff_t offset;

	*prev = vma;
#ifdef CONFIG_SWAP
	if (!file) {
		walk_page_range(vma->vm_mm, start, end, &swapin_walk_ops, vma);
		lru_add_drain(); /* Push any new pages onto the LRU now */
		return 0;
	}

	if (shmem_mapping(file->f_mapping)) {
		force_shm_swapin_readahead(vma, start, end,
					file->f_mapping);
		return 0;
	}
#else
	if (!file)
		return -EBADF;
#endif

	if (IS_DAX(file_inode(file))) {
		/* no bad return value, but ignore advice */
		return 0;
	}

	/*
	 * Filesystem's fadvise may need to take various locks.  We need to
	 * explicitly grab a reference because the vma (and hence the
	 * vma's reference to the file) can go away as soon as we drop
	 * mmap_sem.
	 */
	*prev = NULL;	/* tell sys_madvise we drop mmap_sem */
	get_file(file);
	up_read(&current->mm->mmap_sem);
	offset = (loff_t)(start - vma->vm_start)
			+ ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
	vfs_fadvise(file, offset, end - start, POSIX_FADV_WILLNEED);
	fput(file);
	down_read(&current->mm->mmap_sem);
	return 0;
}


int do_madvise(unsigned long start, size_t len_in, int behavior)
{
	unsigned long end, tmp;
	struct vm_area_struct *vma, *prev;
	int unmapped_error = 0;
	int error = -EINVAL;
	int write;
	size_t len;
	struct blk_plug plug;

	start = untagged_addr(start);

	if (!madvise_behavior_valid(behavior))
		return error;

	if (!PAGE_ALIGNED(start))
		return error;
	len = PAGE_ALIGN(len_in);

	/* Check to see whether len was rounded up from small -ve to zero */
	if (len_in && !len)
		return error;

	end = start + len;
	if (end < start)
		return error;

	error = 0;
	if (end == start)
		return error;

#ifdef CONFIG_MEMORY_FAILURE
	if (behavior == MADV_HWPOISON || behavior == MADV_SOFT_OFFLINE)
		return madvise_inject_error(behavior, start, start + len_in);
#endif

	write = madvise_need_mmap_write(behavior);
	if (write) {
		if (down_write_killable(&current->mm->mmap_sem))
			return -EINTR;

		/*
		 * We may have stolen the mm from another process
		 * that is undergoing core dumping.
		 *
		 * Right now that's io_ring, in the future it may
		 * be remote process management and not "current"
		 * at all.
		 *
		 * We need to fix core dumping to not do this,
		 * but for now we have the mmget_still_valid()
		 * model.
		 */
		if (!mmget_still_valid(current->mm)) {
			up_write(&current->mm->mmap_sem);
			return -EINTR;
		}
	} else {
		down_read(&current->mm->mmap_sem);
	}

	/*
	 * If the interval [start,end) covers some unmapped address
	 * ranges, just ignore them, but return -ENOMEM at the end.
	 * - different from the way of handling in mlock etc.
	 */
	vma = find_vma_prev(current->mm, start, &prev);
	if (vma && start > vma->vm_start)
		prev = vma;

	blk_start_plug(&plug);
	for (;;) {
		/* Still start < end. */
		error = -ENOMEM;
		if (!vma)
			goto out;

		/* Here start < (end|vma->vm_end). */
		if (start < vma->vm_start) {
			unmapped_error = -ENOMEM;
			start = vma->vm_start;
			if (start >= end)
				goto out;
		}

		/* Here vma->vm_start <= start < (end|vma->vm_end) */
		tmp = vma->vm_end;
		if (end < tmp)
			tmp = end;

		/* Here vma->vm_start <= start < tmp <= (end|vma->vm_end). */
		error = madvise_vma(vma, &prev, start, tmp, behavior);
		if (error)
			goto out;
		start = tmp;
		if (prev && start < prev->vm_end)
			start = prev->vm_end;
		error = unmapped_error;
		if (start >= end)
			goto out;
		if (prev)
			vma = prev->vm_next;
		else	/* madvise_remove dropped mmap_sem */
			vma = find_vma(current->mm, start);
	}
out:
	blk_finish_plug(&plug);
	if (write)
		up_write(&current->mm->mmap_sem);
	else
		up_read(&current->mm->mmap_sem);

	return error;
}


static long madvise_pageout(struct vm_area_struct *vma,
			struct vm_area_struct **prev,
			unsigned long start_addr, unsigned long end_addr)
{
	struct mm_struct *mm = vma->vm_mm;
	struct mmu_gather tlb;

	*prev = vma;
	if (!can_madv_lru_vma(vma))
		return -EINVAL;

	if (!can_do_pageout(vma))
		return 0;

	lru_add_drain();
	tlb_gather_mmu(&tlb, mm, start_addr, end_addr);
	madvise_pageout_page_range(&tlb, vma, start_addr, end_addr);
	tlb_finish_mmu(&tlb, start_addr, end_addr);

	return 0;
}


static long madvise_cold(struct vm_area_struct *vma,
			struct vm_area_struct **prev,
			unsigned long start_addr, unsigned long end_addr)
{
	struct mm_struct *mm = vma->vm_mm;
	struct mmu_gather tlb;

	*prev = vma;
	if (!can_madv_lru_vma(vma))
		return -EINVAL;

	lru_add_drain();
	tlb_gather_mmu(&tlb, mm, start_addr, end_addr);
	madvise_cold_page_range(&tlb, vma, start_addr, end_addr);
	tlb_finish_mmu(&tlb, start_addr, end_addr);

	return 0;
}


SYSCALL_DEFINE3(madvise, unsigned long, start, size_t, len_in, int, behavior)
{
	return do_madvise(start, len_in, behavior);
}


static int madvise_need_mmap_write(int behavior)
{
	switch (behavior) {
	case MADV_REMOVE:
	case MADV_WILLNEED:
	case MADV_DONTNEED:
	case MADV_COLD:
	case MADV_PAGEOUT:
	case MADV_FREE:
		return 0;
	default:
		/* be safe, default to 1. list exceptions explicitly */
		return 1;
	}
}


static void force_shm_swapin_readahead(struct vm_area_struct *vma,
		unsigned long start, unsigned long end,
		struct address_space *mapping)
{
	pgoff_t index;
	struct page *page;
	swp_entry_t swap;

	for (; start < end; start += PAGE_SIZE) {
		index = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;

		page = find_get_entry(mapping, index);
		if (!xa_is_value(page)) {
			if (page)
				put_page(page);
			continue;
		}
		swap = radix_to_swp_entry(page);
		page = read_swap_cache_async(swap, GFP_HIGHUSER_MOVABLE,
							NULL, 0, false);
		if (page)
			put_page(page);
	}

	lru_add_drain();	/* Push any new pages onto the LRU now */
}


Fix a possible heap-buffer-overflow in archive_string_append_from_wcs()

When we grow the archive_string buffer, we have to make sure it fits
at least one maximum-sized multibyte character in the current locale
and the null character.

Fixes #1298

make_codepage_from_charset(const char *charset)
{
	(void)charset; /* UNUSED */
	return (-1);/* Unknown */
}

Heap-based buffer overflow in archive_string_append_from_wcs() (archive_string.c) in libarchive-3.4.1dev allows remote attackers to cause a denial of service (out-of-bounds write in heap memory resulting into a crash) via a crafted archive file. NOTE: this only affects users who downloaded the development code from GitHub. Users of the product's official releases are unaffected.


is_big_endian(void)
{
	uint16_t d = 1;

	return (archive_be16dec(&d) == 1);
}


win_strncat_to_utf16be(struct archive_string *as16, const void *_p,
    size_t length, struct archive_string_conv *sc)
{
	return (win_strncat_to_utf16(as16, _p, length, sc, 1));
}


unicode_to_utf16le(char *p, size_t remaining, uint32_t uc)
{
	char *utf16 = p;

	if (uc > 0xffff) {
		/* We have a code point that won't fit into a
		 * wchar_t; convert it to a surrogate pair. */
		if (remaining < 4)
			return (0);
		uc -= 0x10000;
		archive_le16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800);
		archive_le16enc(utf16+2, (uc & 0x3ff) + 0xDC00);
		return (4);
	} else {
		if (remaining < 2)
			return (0);
		archive_le16enc(utf16, uc);
		return (2);
	}
}


archive_string_default_conversion_for_write(struct archive *a)
{
	const char *cur_charset = get_current_charset(a);
	char oemcp[16];

	/* NOTE: a check of cur_charset is unneeded but we need
	 * that get_current_charset() has been surely called at
	 * this time whatever C compiler optimized. */
	if (cur_charset != NULL &&
	    (a->current_codepage == CP_C_LOCALE ||
	     a->current_codepage == a->current_oemcp))
		return (NULL);/* no conversion. */

	_snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp);
	/* Make sure a null termination must be set. */
	oemcp[sizeof(oemcp)-1] = '\0';
	return (get_sconv_object(a, cur_charset, oemcp,
	    SCONV_TO_CHARSET));
}


archive_strncat_l(struct archive_string *as, const void *_p, size_t n,
    struct archive_string_conv *sc)
{
	const void *s;
	size_t length = 0;
	int i, r = 0, r2;

	if (_p != NULL && n > 0) {
		if (sc != NULL && (sc->flag & SCONV_FROM_UTF16))
			length = utf16nbytes(_p, n);
		else
			length = mbsnbytes(_p, n);
	}

	/* We must allocate memory even if there is no data for conversion
	 * or copy. This simulates archive_string_append behavior. */
	if (length == 0) {
		int tn = 1;
		if (sc != NULL && (sc->flag & SCONV_TO_UTF16))
			tn = 2;
		if (archive_string_ensure(as, as->length + tn) == NULL)
			return (-1);
		as->s[as->length] = 0;
		if (tn == 2)
			as->s[as->length+1] = 0;
		return (0);
	}

	/*
	 * If sc is NULL, we just make a copy.
	 */
	if (sc == NULL) {
		if (archive_string_append(as, _p, length) == NULL)
			return (-1);/* No memory */
		return (0);
	}

	s = _p;
	i = 0;
	if (sc->nconverter > 1) {
		sc->utftmp.length = 0;
		r2 = sc->converter[0](&(sc->utftmp), s, length, sc);
		if (r2 != 0 && errno == ENOMEM)
			return (r2);
		if (r > r2)
			r = r2;
		s = sc->utftmp.s;
		length = sc->utftmp.length;
		++i;
	}
	r2 = sc->converter[i](as, s, length, sc);
	if (r > r2)
		r = r2;
	return (r);
}


archive_string_conversion_free(struct archive *a)
{
	struct archive_string_conv *sc;
	struct archive_string_conv *sc_next;

	for (sc = a->sconv; sc != NULL; sc = sc_next) {
		sc_next = sc->next;
		free_sconv_object(sc);
	}
	a->sconv = NULL;
	free(a->current_code);
	a->current_code = NULL;
}


strncat_in_codepage(struct archive_string *as,
    const void *_p, size_t length, struct archive_string_conv *sc)
{
	const char *s = (const char *)_p;
	struct archive_wstring aws;
	size_t l;
	int r, saved_flag;

	archive_string_init(&aws);
	saved_flag = sc->flag;
	sc->flag &= ~(SCONV_NORMALIZATION_D | SCONV_NORMALIZATION_C);
	r = archive_wstring_append_from_mbs_in_codepage(&aws, s, length, sc);
	sc->flag = saved_flag;
	if (r != 0) {
		archive_wstring_free(&aws);
		if (errno != ENOMEM)
			archive_string_append(as, s, length);
		return (-1);
	}

	l = as->length;
	r = archive_string_append_from_wcs_in_codepage(
	    as, aws.s, aws.length, sc);
	if (r != 0 && errno != ENOMEM && l == as->length)
		archive_string_append(as, s, length);
	archive_wstring_free(&aws);
	return (r);
}


best_effort_strncat_to_utf16(struct archive_string *as16, const void *_p,
    size_t length, struct archive_string_conv *sc, int bigendian)
{
	const char *s = (const char *)_p;
	char *utf16;
	size_t remaining;
	int ret;

	(void)sc; /* UNUSED */
	/*
	 * Other case, we should do the best effort.
	 * If all character are ASCII(<0x7f), we can convert it.
	 * if not , we set a alternative character and return -1.
	 */
	ret = 0;
	remaining = length;

	if (archive_string_ensure(as16,
	    as16->length + (length + 1) * 2) == NULL)
		return (-1);

	utf16 = as16->s + as16->length;
	while (remaining--) {
		unsigned c = *s++;
		if (c > 127) {
			/* We cannot handle it. */
			c = UNICODE_R_CHAR;
			ret = -1;
		}
		if (bigendian)
			archive_be16enc(utf16, c);
		else
			archive_le16enc(utf16, c);
		utf16 += 2;
	}
	as16->length = utf16 - as16->s;
	as16->s[as16->length] = 0;
	as16->s[as16->length+1] = 0;
	return (ret);
}


archive_mstring_update_utf8(struct archive *a, struct archive_mstring *aes,
    const char *utf8)
{
	struct archive_string_conv *sc;
	int r;

	if (utf8 == NULL) {
		aes->aes_set = 0;
		return (0); /* Succeeded in clearing everything. */
	}

	/* Save the UTF8 string. */
	archive_strcpy(&(aes->aes_utf8), utf8);

	/* Empty the mbs and wcs strings. */
	archive_string_empty(&(aes->aes_mbs));
	archive_wstring_empty(&(aes->aes_wcs));

	aes->aes_set = AES_SET_UTF8;	/* Only UTF8 is set now. */

	/* Try converting UTF-8 to MBS, return false on failure. */
	sc = archive_string_conversion_from_charset(a, "UTF-8", 1);
	if (sc == NULL)
		return (-1);/* Couldn't allocate memory for sc. */
	r = archive_strcpy_l(&(aes->aes_mbs), utf8, sc);
	if (a == NULL)
		free_sconv_object(sc);
	if (r != 0)
		return (-1);
	aes->aes_set = AES_SET_UTF8 | AES_SET_MBS; /* Both UTF8 and MBS set. */

	/* Try converting MBS to WCS, return false on failure. */
	if (archive_wstring_append_from_mbs(&(aes->aes_wcs), aes->aes_mbs.s,
	    aes->aes_mbs.length))
		return (-1);
	aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS;

	/* All conversions succeeded. */
	return (0);
}


archive_string_append_unicode(struct archive_string *as, const void *_p,
    size_t len, struct archive_string_conv *sc)
{
	const char *s;
	char *p, *endp;
	uint32_t uc;
	size_t w;
	int n, ret = 0, ts, tm;
	int (*parse)(uint32_t *, const char *, size_t);
	size_t (*unparse)(char *, size_t, uint32_t);

	if (sc->flag & SCONV_TO_UTF16BE) {
		unparse = unicode_to_utf16be;
		ts = 2;
	} else if (sc->flag & SCONV_TO_UTF16LE) {
		unparse = unicode_to_utf16le;
		ts = 2;
	} else if (sc->flag & SCONV_TO_UTF8) {
		unparse = unicode_to_utf8;
		ts = 1;
	} else {
		/*
		 * This case is going to be converted to another
		 * character-set through iconv.
		 */
		if (sc->flag & SCONV_FROM_UTF16BE) {
			unparse = unicode_to_utf16be;
			ts = 2;
		} else if (sc->flag & SCONV_FROM_UTF16LE) {
			unparse = unicode_to_utf16le;
			ts = 2;
		} else {
			unparse = unicode_to_utf8;
			ts = 1;
		}
	}

	if (sc->flag & SCONV_FROM_UTF16BE) {
		parse = utf16be_to_unicode;
		tm = 1;
	} else if (sc->flag & SCONV_FROM_UTF16LE) {
		parse = utf16le_to_unicode;
		tm = 1;
	} else {
		parse = cesu8_to_unicode;
		tm = ts;
	}

	if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)
		return (-1);

	s = (const char *)_p;
	p = as->s + as->length;
	endp = as->s + as->buffer_length - ts;
	while ((n = parse(&uc, s, len)) != 0) {
		if (n < 0) {
			/* Use a replaced unicode character. */
			n *= -1;
			ret = -1;
		}
		s += n;
		len -= n;
		while ((w = unparse(p, endp - p, uc)) == 0) {
			/* There is not enough output buffer so
			 * we have to expand it. */
			as->length = p - as->s;
			if (archive_string_ensure(as,
			    as->buffer_length + len * tm + ts) == NULL)
				return (-1);
			p = as->s + as->length;
			endp = as->s + as->buffer_length - ts;
		}
		p += w;
	}
	as->length = p - as->s;
	as->s[as->length] = '\0';
	if (ts == 2)
		as->s[as->length+1] = '\0';
	return (ret);
}


archive_string_append_from_wcs(struct archive_string *as,
    const wchar_t *w, size_t len)
{
	/* We cannot use the standard wcstombs() here because it
	 * cannot tell us how big the output buffer should be.  So
	 * I've built a loop around wcrtomb() or wctomb() that
	 * converts a character at a time and resizes the string as
	 * needed.  We prefer wcrtomb() when it's available because
	 * it's thread-safe. */
	int n, ret_val = 0;
	char *p;
	char *end;
#if HAVE_WCRTOMB
	mbstate_t shift_state;

	memset(&shift_state, 0, sizeof(shift_state));
#else
	/* Clear the shift state before starting. */
	wctomb(NULL, L'\0');
#endif
	/*
	 * Allocate buffer for MBS.
	 * We need this allocation here since it is possible that
	 * as->s is still NULL.
	 */
	if (archive_string_ensure(as, as->length + len + 1) == NULL)
		return (-1);

	p = as->s + as->length;
	end = as->s + as->buffer_length - MB_CUR_MAX -1;
	while (*w != L'\0' && len > 0) {
		if (p >= end) {
			as->length = p - as->s;
			as->s[as->length] = '\0';
			/* Re-allocate buffer for MBS. */
			if (archive_string_ensure(as,
			    as->length + max(len * 2,
			    (size_t)MB_CUR_MAX) + 1) == NULL)
				return (-1);
			p = as->s + as->length;
			end = as->s + as->buffer_length - MB_CUR_MAX -1;
		}
#if HAVE_WCRTOMB
		n = wcrtomb(p, *w++, &shift_state);
#else
		n = wctomb(p, *w++);
#endif
		if (n == -1) {
			if (errno == EILSEQ) {
				/* Skip an illegal wide char. */
				*p++ = '?';
				ret_val = -1;
			} else {
				ret_val = -1;
				break;
			}
		} else
			p += n;
		len--;
	}
	as->length = p - as->s;
	as->s[as->length] = '\0';
	return (ret_val);
}


archive_string_free(struct archive_string *as)
{
	as->length = 0;
	as->buffer_length = 0;
	free(as->s);
	as->s = NULL;
}


mbsnbytes(const void *_p, size_t n)
{
	size_t s;
	const char *p, *pp;

	if (_p == NULL)
		return (0);
	p = (const char *)_p;

	/* Like strlen(p), except won't examine positions beyond p[n]. */
	s = 0;
	pp = p;
	while (s < n && *pp) {
		pp++;
		s++;
	}
	return (s);
}


archive_string_normalize_D(struct archive_string *as, const void *_p,
    size_t len, struct archive_string_conv *sc)
{
	const char *s = (const char *)_p;
	char *p, *endp;
	uint32_t uc, uc2;
	size_t w;
	int always_replace, n, n2, ret = 0, spair, ts, tm;
	int (*parse)(uint32_t *, const char *, size_t);
	size_t (*unparse)(char *, size_t, uint32_t);

	always_replace = 1;
	ts = 1;/* text size. */
	if (sc->flag & SCONV_TO_UTF16BE) {
		unparse = unicode_to_utf16be;
		ts = 2;
		if (sc->flag & SCONV_FROM_UTF16BE)
			always_replace = 0;
	} else if (sc->flag & SCONV_TO_UTF16LE) {
		unparse = unicode_to_utf16le;
		ts = 2;
		if (sc->flag & SCONV_FROM_UTF16LE)
			always_replace = 0;
	} else if (sc->flag & SCONV_TO_UTF8) {
		unparse = unicode_to_utf8;
		if (sc->flag & SCONV_FROM_UTF8)
			always_replace = 0;
	} else {
		/*
		 * This case is going to be converted to another
		 * character-set through iconv.
		 */
		always_replace = 0;
		if (sc->flag & SCONV_FROM_UTF16BE) {
			unparse = unicode_to_utf16be;
			ts = 2;
		} else if (sc->flag & SCONV_FROM_UTF16LE) {
			unparse = unicode_to_utf16le;
			ts = 2;
		} else {
			unparse = unicode_to_utf8;
		}
	}

	if (sc->flag & SCONV_FROM_UTF16BE) {
		parse = utf16be_to_unicode;
		tm = 1;
		spair = 4;/* surrogate pair size in UTF-16. */
	} else if (sc->flag & SCONV_FROM_UTF16LE) {
		parse = utf16le_to_unicode;
		tm = 1;
		spair = 4;/* surrogate pair size in UTF-16. */
	} else {
		parse = cesu8_to_unicode;
		tm = ts;
		spair = 6;/* surrogate pair size in UTF-8. */
	}

	if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)
		return (-1);

	p = as->s + as->length;
	endp = as->s + as->buffer_length - ts;
	while ((n = parse(&uc, s, len)) != 0) {
		const char *ucptr;
		uint32_t cp1, cp2;
		int SIndex;
		struct {
			uint32_t uc;
			int ccc;
		} fdc[FDC_MAX];
		int fdi, fdj;
		int ccc;

check_first_code:
		if (n < 0) {
			/* Use a replaced unicode character. */
			UNPARSE(p, endp, uc);
			s += n*-1;
			len -= n*-1;
			ret = -1;
			continue;
		} else if (n == spair || always_replace)
			/* uc is converted from a surrogate pair.
			 * this should be treated as a changed code. */
			ucptr = NULL;
		else
			ucptr = s;
		s += n;
		len -= n;

		/* Hangul Decomposition. */
		if ((SIndex = uc - HC_SBASE) >= 0 && SIndex < HC_SCOUNT) {
			int L = HC_LBASE + SIndex / HC_NCOUNT;
			int V = HC_VBASE + (SIndex % HC_NCOUNT) / HC_TCOUNT;
			int T = HC_TBASE + SIndex % HC_TCOUNT;

			REPLACE_UC_WITH(L);
			WRITE_UC();
			REPLACE_UC_WITH(V);
			WRITE_UC();
			if (T != HC_TBASE) {
				REPLACE_UC_WITH(T);
				WRITE_UC();
			}
			continue;
		}
		if (IS_DECOMPOSABLE_BLOCK(uc) && CCC(uc) != 0) {
			WRITE_UC();
			continue;
		}

		fdi = 0;
		while (get_nfd(&cp1, &cp2, uc) && fdi < FDC_MAX) {
			int k;

			for (k = fdi; k > 0; k--)
				fdc[k] = fdc[k-1];
			fdc[0].ccc = CCC(cp2);
			fdc[0].uc = cp2;
			fdi++;
			REPLACE_UC_WITH(cp1);
		}

		/* Read following code points. */
		while ((n2 = parse(&uc2, s, len)) > 0 &&
		    (ccc = CCC(uc2)) != 0 && fdi < FDC_MAX) {
			int j, k;

			s += n2;
			len -= n2;
			for (j = 0; j < fdi; j++) {
				if (fdc[j].ccc > ccc)
					break;
			}
			if (j < fdi) {
				for (k = fdi; k > j; k--)
					fdc[k] = fdc[k-1];
				fdc[j].ccc = ccc;
				fdc[j].uc = uc2;
			} else {
				fdc[fdi].ccc = ccc;
				fdc[fdi].uc = uc2;
			}
			fdi++;
		}

		WRITE_UC();
		for (fdj = 0; fdj < fdi; fdj++) {
			REPLACE_UC_WITH(fdc[fdj].uc);
			WRITE_UC();
		}

		if (n2 == 0)
			break;
		REPLACE_UC_WITH(uc2);
		n = n2;
		goto check_first_code;
	}
	as->length = p - as->s;
	as->s[as->length] = '\0';
	if (ts == 2)
		as->s[as->length+1] = '\0';
	return (ret);
}


archive_string_default_conversion_for_write(struct archive *a)
{
	(void)a; /* UNUSED */
	return (NULL);
}


archive_wstring_concat(struct archive_wstring *dest,
    struct archive_wstring *src)
{
	if (archive_wstring_append(dest, src->s, src->length) == NULL)
		__archive_errx(1, "Out of memory");
}


archive_wstring_ensure(struct archive_wstring *as, size_t s)
{
	return (struct archive_wstring *)
		archive_string_ensure((struct archive_string *)as,
					s * sizeof(wchar_t));
}


get_current_charset(struct archive *a)
{
	const char *cur_charset;

	if (a == NULL)
		cur_charset = default_iconv_charset("");
	else {
		cur_charset = default_iconv_charset(a->current_code);
		if (a->current_code == NULL) {
			a->current_code = strdup(cur_charset);
			a->current_codepage = get_current_codepage();
			a->current_oemcp = get_current_oemcp();
		}
	}
	return (cur_charset);
}


archive_wstring_free(struct archive_wstring *as)
{
	as->length = 0;
	as->buffer_length = 0;
	free(as->s);
	as->s = NULL;
}


archive_mstring_copy_utf8(struct archive_mstring *aes, const char *utf8)
{
  if (utf8 == NULL) {
    aes->aes_set = 0;
    return (0);
  }
  aes->aes_set = AES_SET_UTF8;
  archive_string_empty(&(aes->aes_mbs));
  archive_string_empty(&(aes->aes_wcs));
  archive_strncpy(&(aes->aes_utf8), utf8, strlen(utf8));
  return (int)strlen(utf8);
}


archive_string_normalize_C(struct archive_string *as, const void *_p,
    size_t len, struct archive_string_conv *sc)
{
	const char *s = (const char *)_p;
	char *p, *endp;
	uint32_t uc, uc2;
	size_t w;
	int always_replace, n, n2, ret = 0, spair, ts, tm;
	int (*parse)(uint32_t *, const char *, size_t);
	size_t (*unparse)(char *, size_t, uint32_t);

	always_replace = 1;
	ts = 1;/* text size. */
	if (sc->flag & SCONV_TO_UTF16BE) {
		unparse = unicode_to_utf16be;
		ts = 2;
		if (sc->flag & SCONV_FROM_UTF16BE)
			always_replace = 0;
	} else if (sc->flag & SCONV_TO_UTF16LE) {
		unparse = unicode_to_utf16le;
		ts = 2;
		if (sc->flag & SCONV_FROM_UTF16LE)
			always_replace = 0;
	} else if (sc->flag & SCONV_TO_UTF8) {
		unparse = unicode_to_utf8;
		if (sc->flag & SCONV_FROM_UTF8)
			always_replace = 0;
	} else {
		/*
		 * This case is going to be converted to another
		 * character-set through iconv.
		 */
		always_replace = 0;
		if (sc->flag & SCONV_FROM_UTF16BE) {
			unparse = unicode_to_utf16be;
			ts = 2;
		} else if (sc->flag & SCONV_FROM_UTF16LE) {
			unparse = unicode_to_utf16le;
			ts = 2;
		} else {
			unparse = unicode_to_utf8;
		}
	}

	if (sc->flag & SCONV_FROM_UTF16BE) {
		parse = utf16be_to_unicode;
		tm = 1;
		spair = 4;/* surrogate pair size in UTF-16. */
	} else if (sc->flag & SCONV_FROM_UTF16LE) {
		parse = utf16le_to_unicode;
		tm = 1;
		spair = 4;/* surrogate pair size in UTF-16. */
	} else {
		parse = cesu8_to_unicode;
		tm = ts;
		spair = 6;/* surrogate pair size in UTF-8. */
	}

	if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)
		return (-1);

	p = as->s + as->length;
	endp = as->s + as->buffer_length - ts;
	while ((n = parse(&uc, s, len)) != 0) {
		const char *ucptr, *uc2ptr;

		if (n < 0) {
			/* Use a replaced unicode character. */
			UNPARSE(p, endp, uc);
			s += n*-1;
			len -= n*-1;
			ret = -1;
			continue;
		} else if (n == spair || always_replace)
			/* uc is converted from a surrogate pair.
			 * this should be treated as a changed code. */
			ucptr = NULL;
		else
			ucptr = s;
		s += n;
		len -= n;

		/* Read second code point. */
		while ((n2 = parse(&uc2, s, len)) > 0) {
			uint32_t ucx[FDC_MAX];
			int ccx[FDC_MAX];
			int cl, cx, i, nx, ucx_size;
			int LIndex,SIndex;
			uint32_t nfc;

			if (n2 == spair || always_replace)
				/* uc2 is converted from a surrogate pair.
			 	 * this should be treated as a changed code. */
				uc2ptr = NULL;
			else
				uc2ptr = s;
			s += n2;
			len -= n2;

			/*
			 * If current second code point is out of decomposable
			 * code points, finding compositions is unneeded.
			 */
			if (!IS_DECOMPOSABLE_BLOCK(uc2)) {
				WRITE_UC();
				REPLACE_UC_WITH_UC2();
				continue;
			}

			/*
			 * Try to combine current code points.
			 */
			/*
			 * We have to combine Hangul characters according to
			 * http://uniicode.org/reports/tr15/#Hangul
			 */
			if (0 <= (LIndex = uc - HC_LBASE) &&
			    LIndex < HC_LCOUNT) {
				/*
				 * Hangul Composition.
				 * 1. Two current code points are L and V.
				 */
				int VIndex = uc2 - HC_VBASE;
				if (0 <= VIndex && VIndex < HC_VCOUNT) {
					/* Make syllable of form LV. */
					UPDATE_UC(HC_SBASE +
					    (LIndex * HC_VCOUNT + VIndex) *
					     HC_TCOUNT);
				} else {
					WRITE_UC();
					REPLACE_UC_WITH_UC2();
				}
				continue;
			} else if (0 <= (SIndex = uc - HC_SBASE) &&
			    SIndex < HC_SCOUNT && (SIndex % HC_TCOUNT) == 0) {
				/*
				 * Hangul Composition.
				 * 2. Two current code points are LV and T.
				 */
				int TIndex = uc2 - HC_TBASE;
				if (0 < TIndex && TIndex < HC_TCOUNT) {
					/* Make syllable of form LVT. */
					UPDATE_UC(uc + TIndex);
				} else {
					WRITE_UC();
					REPLACE_UC_WITH_UC2();
				}
				continue;
			} else if ((nfc = get_nfc(uc, uc2)) != 0) {
				/* A composition to current code points
				 * is found. */
				UPDATE_UC(nfc);
				continue;
			} else if ((cl = CCC(uc2)) == 0) {
				/* Clearly 'uc2' the second code point is not
				 * a decomposable code. */
				WRITE_UC();
				REPLACE_UC_WITH_UC2();
				continue;
			}

			/*
			 * Collect following decomposable code points.
			 */
			cx = 0;
			ucx[0] = uc2;
			ccx[0] = cl;
			COLLECT_CPS(1);

			/*
			 * Find a composed code in the collected code points.
			 */
			i = 1;
			while (i < ucx_size) {
				int j;

				if ((nfc = get_nfc(uc, ucx[i])) == 0) {
					i++;
					continue;
				}

				/*
				 * nfc is composed of uc and ucx[i].
				 */
				UPDATE_UC(nfc);

				/*
				 * Remove ucx[i] by shifting
				 * following code points.
				 */
				for (j = i; j+1 < ucx_size; j++) {
					ucx[j] = ucx[j+1];
					ccx[j] = ccx[j+1];
				}
				ucx_size --;

				/*
				 * Collect following code points blocked
				 * by ucx[i] the removed code point.
				 */
				if (ucx_size > 0 && i == ucx_size &&
				    nx > 0 && cx == cl) {
					cl =  ccx[ucx_size-1];
					COLLECT_CPS(ucx_size);
				}
				/*
				 * Restart finding a composed code with
				 * the updated uc from the top of the
				 * collected code points.
				 */
				i = 0;
			}

			/*
			 * Apparently the current code points are not
			 * decomposed characters or already composed.
			 */
			WRITE_UC();
			for (i = 0; i < ucx_size; i++)
				UNPARSE(p, endp, ucx[i]);

			/*
			 * Flush out remaining canonical combining characters.
			 */
			if (nx > 0 && cx == cl && len > 0) {
				while ((nx = parse(&ucx[0], s, len))
				    > 0) {
					cx = CCC(ucx[0]);
					if (cl > cx)
						break;
					s += nx;
					len -= nx;
					cl = cx;
					UNPARSE(p, endp, ucx[0]);
				}
			}
			break;
		}
		if (n2 < 0) {
			WRITE_UC();
			/* Use a replaced unicode character. */
			UNPARSE(p, endp, uc2);
			s += n2*-1;
			len -= n2*-1;
			ret = -1;
			continue;
		} else if (n2 == 0) {
			WRITE_UC();
			break;
		}
	}
	as->length = p - as->s;
	as->s[as->length] = '\0';
	if (ts == 2)
		as->s[as->length+1] = '\0';
	return (ret);
}


archive_string_ensure(struct archive_string *as, size_t s)
{
	char *p;
	size_t new_length;

	/* If buffer is already big enough, don't reallocate. */
	if (as->s && (s <= as->buffer_length))
		return (as);

	/*
	 * Growing the buffer at least exponentially ensures that
	 * append operations are always linear in the number of
	 * characters appended.  Using a smaller growth rate for
	 * larger buffers reduces memory waste somewhat at the cost of
	 * a larger constant factor.
	 */
	if (as->buffer_length < 32)
		/* Start with a minimum 32-character buffer. */
		new_length = 32;
	else if (as->buffer_length < 8192)
		/* Buffers under 8k are doubled for speed. */
		new_length = as->buffer_length + as->buffer_length;
	else {
		/* Buffers 8k and over grow by at least 25% each time. */
		new_length = as->buffer_length + as->buffer_length / 4;
		/* Be safe: If size wraps, fail. */
		if (new_length < as->buffer_length) {
			/* On failure, wipe the string and return NULL. */
			archive_string_free(as);
			errno = ENOMEM;/* Make sure errno has ENOMEM. */
			return (NULL);
		}
	}
	/*
	 * The computation above is a lower limit to how much we'll
	 * grow the buffer.  In any case, we have to grow it enough to
	 * hold the request.
	 */
	if (new_length < s)
		new_length = s;
	/* Now we can reallocate the buffer. */
	p = (char *)realloc(as->s, new_length);
	if (p == NULL) {
		/* On failure, wipe the string and return NULL. */
		archive_string_free(as);
		errno = ENOMEM;/* Make sure errno has ENOMEM. */
		return (NULL);
	}

	as->s = p;
	as->buffer_length = new_length;
	return (as);
}


archive_string_append_from_wcs_in_codepage(struct archive_string *as,
    const wchar_t *ws, size_t len, struct archive_string_conv *sc)
{
	BOOL defchar_used, *dp;
	int count, ret = 0;
	UINT to_cp;
	int wslen = (int)len;

	if (sc != NULL)
		to_cp = sc->to_cp;
	else
		to_cp = get_current_codepage();

	if (to_cp == CP_C_LOCALE) {
		/*
		 * "C" locale special processing.
		 */
		const wchar_t *wp = ws;
		char *p;

		if (NULL == archive_string_ensure(as,
		    as->length + wslen +1))
			return (-1);
		p = as->s + as->length;
		count = 0;
		defchar_used = 0;
		while (count < wslen && *wp) {
			if (*wp > 255) {
				*p++ = '?';
				wp++;
				defchar_used = 1;
			} else
				*p++ = (char)*wp++;
			count++;
		}
	} else if (sc != NULL && (sc->flag & SCONV_TO_UTF16)) {
		uint16_t *u16;

		if (NULL ==
		    archive_string_ensure(as, as->length + len * 2 + 2))
			return (-1);
		u16 = (uint16_t *)(as->s + as->length);
		count = 0;
		defchar_used = 0;
		if (sc->flag & SCONV_TO_UTF16BE) {
			while (count < (int)len && *ws) {
				archive_be16enc(u16+count, *ws);
				ws++;
				count++;
			}
		} else {
			while (count < (int)len && *ws) {
				archive_le16enc(u16+count, *ws);
				ws++;
				count++;
			}
		}
		count <<= 1; /* to be byte size */
	} else {
		/* Make sure the MBS buffer has plenty to set. */
		if (NULL ==
		    archive_string_ensure(as, as->length + len * 2 + 1))
			return (-1);
		do {
			defchar_used = 0;
			if (to_cp == CP_UTF8 || sc == NULL)
				dp = NULL;
			else
				dp = &defchar_used;
			count = WideCharToMultiByte(to_cp, 0, ws, wslen,
			    as->s + as->length, (int)as->buffer_length-1, NULL, dp);
			if (count == 0 &&
			    GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
				/* Expand the MBS buffer and retry. */
				if (NULL == archive_string_ensure(as,
					as->buffer_length + len))
					return (-1);
				continue;
			}
			if (count == 0)
				ret = -1;
			break;
		} while (1);
	}
	as->length += count;
	as->s[as->length] = '\0';
	return (defchar_used?-1:ret);
}


archive_strncpy_l(struct archive_string *as, const void *_p, size_t n,
    struct archive_string_conv *sc)
{
	as->length = 0;
	return (archive_strncat_l(as, _p, n, sc));
}


best_effort_strncat_in_locale(struct archive_string *as, const void *_p,
    size_t length, struct archive_string_conv *sc)
{
	size_t remaining;
	const uint8_t *itp;
	int return_value = 0; /* success */

	/*
	 * If both from-locale and to-locale is the same, this makes a copy.
	 * And then this checks all copied MBS can be WCS if so returns 0.
	 */
	if (sc->same) {
		if (archive_string_append(as, _p, length) == NULL)
			return (-1);/* No memory */
		return (invalid_mbs(_p, length, sc));
	}

	/*
	 * If a character is ASCII, this just copies it. If not, this
	 * assigns '?' character instead but in UTF-8 locale this assigns
	 * byte sequence 0xEF 0xBD 0xBD, which are code point U+FFFD,
	 * a Replacement Character in Unicode.
	 */

	remaining = length;
	itp = (const uint8_t *)_p;
	while (*itp && remaining > 0) {
		if (*itp > 127) {
			// Non-ASCII: Substitute with suitable replacement
			if (sc->flag & SCONV_TO_UTF8) {
				if (archive_string_append(as, utf8_replacement_char, sizeof(utf8_replacement_char)) == NULL) {
					__archive_errx(1, "Out of memory");
				}
			} else {
				archive_strappend_char(as, '?');
			}
			return_value = -1;
		} else {
			archive_strappend_char(as, *itp);
		}
		++itp;
	}
	return (return_value);
}


win_strncat_from_utf16(struct archive_string *as, const void *_p, size_t bytes,
    struct archive_string_conv *sc, int be)
{
	struct archive_string tmp;
	const char *u16;
	int ll;
	BOOL defchar;
	char *mbs;
	size_t mbs_size, b;
	int ret = 0;

	bytes &= ~1;
	if (archive_string_ensure(as, as->length + bytes +1) == NULL)
		return (-1);

	mbs = as->s + as->length;
	mbs_size = as->buffer_length - as->length -1;

	if (sc->to_cp == CP_C_LOCALE) {
		/*
		 * "C" locale special process.
		 */
		u16 = _p;
		ll = 0;
		for (b = 0; b < bytes; b += 2) {
			uint16_t val;
			if (be)
				val = archive_be16dec(u16+b);
			else
				val = archive_le16dec(u16+b);
			if (val > 255) {
				*mbs++ = '?';
				ret = -1;
			} else
				*mbs++ = (char)(val&0xff);
			ll++;
		}
		as->length += ll;
		as->s[as->length] = '\0';
		return (ret);
	}

	archive_string_init(&tmp);
	if (be) {
		if (is_big_endian()) {
			u16 = _p;
		} else {
			if (archive_string_ensure(&tmp, bytes+2) == NULL)
				return (-1);
			memcpy(tmp.s, _p, bytes);
			for (b = 0; b < bytes; b += 2) {
				uint16_t val = archive_be16dec(tmp.s+b);
				archive_le16enc(tmp.s+b, val);
			}
			u16 = tmp.s;
		}
	} else {
		if (!is_big_endian()) {
			u16 = _p;
		} else {
			if (archive_string_ensure(&tmp, bytes+2) == NULL)
				return (-1);
			memcpy(tmp.s, _p, bytes);
			for (b = 0; b < bytes; b += 2) {
				uint16_t val = archive_le16dec(tmp.s+b);
				archive_be16enc(tmp.s+b, val);
			}
			u16 = tmp.s;
		}
	}

	do {
		defchar = 0;
		ll = WideCharToMultiByte(sc->to_cp, 0,
		    (LPCWSTR)u16, (int)bytes>>1, mbs, (int)mbs_size,
			NULL, &defchar);
		/* Exit loop if we succeeded */
		if (ll != 0 ||
		    GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
			break;
		}
		/* Else expand buffer and loop to try again. */
		ll = WideCharToMultiByte(sc->to_cp, 0,
		    (LPCWSTR)u16, (int)bytes, NULL, 0, NULL, NULL);
		if (archive_string_ensure(as, ll +1) == NULL)
			return (-1);
		mbs = as->s + as->length;
		mbs_size = as->buffer_length - as->length -1;
	} while (1);
	archive_string_free(&tmp);
	as->length += ll;
	as->s[as->length] = '\0';
	if (ll == 0 || defchar)
		ret = -1;
	return (ret);
}


get_current_oemcp(void)
{
	int i;
	char *locale, *p;
	size_t len;

	locale = setlocale(LC_CTYPE, NULL);
	if (locale == NULL)
		return (GetOEMCP());
	if (locale[0] == 'C' && locale[1] == '\0')
		return (CP_C_LOCALE);

	p = strrchr(locale, '.');
	if (p == NULL)
		return (GetOEMCP());
	len = p - locale;
	for (i = 0; acp_ocp_map[i].acp; i++) {
		if (strncmp(acp_ocp_map[i].locale, locale, len) == 0)
			return (acp_ocp_map[i].ocp);
	}
	return (GetOEMCP());
}


utf16_to_unicode(uint32_t *pwc, const char *s, size_t n, int be)
{
	const char *utf16 = s;
	unsigned uc;

	if (n == 0)
		return (0);
	if (n == 1) {
		/* set the Replacement Character instead. */
		*pwc = UNICODE_R_CHAR;
		return (-1);
	}

	if (be)
		uc = archive_be16dec(utf16);
	else
		uc = archive_le16dec(utf16);
	utf16 += 2;

	/* If this is a surrogate pair, assemble the full code point.*/
	if (IS_HIGH_SURROGATE_LA(uc)) {
		unsigned uc2;

		if (n >= 4) {
			if (be)
				uc2 = archive_be16dec(utf16);
			else
				uc2 = archive_le16dec(utf16);
		} else
			uc2 = 0;
		if (IS_LOW_SURROGATE_LA(uc2)) {
			uc = combine_surrogate_pair(uc, uc2);
			utf16 += 2;
		} else {
	 		/* Undescribed code point should be U+FFFD
		 	* (replacement character). */
			*pwc = UNICODE_R_CHAR;
			return (-2);
		}
	}

	/*
	 * Surrogate pair values(0xd800 through 0xdfff) are only
	 * used by UTF-16, so, after above calculation, the code
	 * must not be surrogate values, and Unicode has no codes
	 * larger than 0x10ffff. Thus, those are not legal Unicode
	 * values.
	 */
	if (IS_SURROGATE_PAIR_LA(uc) || uc > UNICODE_MAX) {
	 	/* Undescribed code point should be U+FFFD
	 	* (replacement character). */
		*pwc = UNICODE_R_CHAR;
		return (((int)(utf16 - s)) * -1);
	}
	*pwc = uc;
	return ((int)(utf16 - s));
}


unicode_to_utf8(char *p, size_t remaining, uint32_t uc)
{
	char *_p = p;

	/* Invalid Unicode char maps to Replacement character */
	if (uc > UNICODE_MAX)
		uc = UNICODE_R_CHAR;
	/* Translate code point to UTF8 */
	if (uc <= 0x7f) {
		if (remaining == 0)
			return (0);
		*p++ = (char)uc;
	} else if (uc <= 0x7ff) {
		if (remaining < 2)
			return (0);
		*p++ = 0xc0 | ((uc >> 6) & 0x1f);
		*p++ = 0x80 | (uc & 0x3f);
	} else if (uc <= 0xffff) {
		if (remaining < 3)
			return (0);
		*p++ = 0xe0 | ((uc >> 12) & 0x0f);
		*p++ = 0x80 | ((uc >> 6) & 0x3f);
		*p++ = 0x80 | (uc & 0x3f);
	} else {
		if (remaining < 4)
			return (0);
		*p++ = 0xf0 | ((uc >> 18) & 0x07);
		*p++ = 0x80 | ((uc >> 12) & 0x3f);
		*p++ = 0x80 | ((uc >> 6) & 0x3f);
		*p++ = 0x80 | (uc & 0x3f);
	}
	return (p - _p);
}


invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc)
{
	const char *p = (const char *)_p;
	size_t r;

#if HAVE_MBRTOWC
	mbstate_t shift_state;

	memset(&shift_state, 0, sizeof(shift_state));
#else
	/* Clear the shift state before starting. */
	mbtowc(NULL, NULL, 0);
#endif
	while (n) {
		wchar_t wc;

#if HAVE_MBRTOWC
		r = mbrtowc(&wc, p, n, &shift_state);
#else
		r = mbtowc(&wc, p, n);
#endif
		if (r == (size_t)-1 || r == (size_t)-2)
			return (-1);/* Invalid. */
		if (r == 0)
			break;
		p += r;
		n -= r;
	}
	(void)sc; /* UNUSED */
	return (0); /* All Okey. */
}


archive_wstrncat(struct archive_wstring *as, const wchar_t *p, size_t n)
{
	size_t s;
	const wchar_t *pp;

	/* Like strlen(p), except won't examine positions beyond p[n]. */
	s = 0;
	pp = p;
	while (s < n && *pp) {
		pp++;
		s++;
	}
	if ((as = archive_wstring_append(as, p, s)) == NULL)
		__archive_errx(1, "Out of memory");
	return (as);
}


unicode_to_utf16be(char *p, size_t remaining, uint32_t uc)
{
	char *utf16 = p;

	if (uc > 0xffff) {
		/* We have a code point that won't fit into a
		 * wchar_t; convert it to a surrogate pair. */
		if (remaining < 4)
			return (0);
		uc -= 0x10000;
		archive_be16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800);
		archive_be16enc(utf16+2, (uc & 0x3ff) + 0xDC00);
		return (4);
	} else {
		if (remaining < 2)
			return (0);
		archive_be16enc(utf16, uc);
		return (2);
	}
}


iconv_strncat_in_locale(struct archive_string *as, const void *_p,
    size_t length, struct archive_string_conv *sc)
{
	ICONV_CONST char *itp;
	size_t remaining;
	iconv_t cd;
	char *outp;
	size_t avail, bs;
	int return_value = 0; /* success */
	int to_size, from_size;

	if (sc->flag & SCONV_TO_UTF16)
		to_size = 2;
	else
		to_size = 1;
	if (sc->flag & SCONV_FROM_UTF16)
		from_size = 2;
	else
		from_size = 1;

	if (archive_string_ensure(as, as->length + length*2+to_size) == NULL)
		return (-1);

	cd = sc->cd;
	itp = (char *)(uintptr_t)_p;
	remaining = length;
	outp = as->s + as->length;
	avail = as->buffer_length - as->length - to_size;
	while (remaining >= (size_t)from_size) {
		size_t result = iconv(cd, &itp, &remaining, &outp, &avail);

		if (result != (size_t)-1)
			break; /* Conversion completed. */

		if (errno == EILSEQ || errno == EINVAL) {
			/*
		 	 * If an output charset is UTF-8 or UTF-16BE/LE,
			 * unknown character should be U+FFFD
			 * (replacement character).
			 */
			if (sc->flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) {
				size_t rbytes;
				if (sc->flag & SCONV_TO_UTF8)
					rbytes = sizeof(utf8_replacement_char);
				else
					rbytes = 2;

				if (avail < rbytes) {
					as->length = outp - as->s;
					bs = as->buffer_length +
					    (remaining * to_size) + rbytes;
					if (NULL ==
					    archive_string_ensure(as, bs))
						return (-1);
					outp = as->s + as->length;
					avail = as->buffer_length
					    - as->length - to_size;
				}
				if (sc->flag & SCONV_TO_UTF8)
					memcpy(outp, utf8_replacement_char, sizeof(utf8_replacement_char));
				else if (sc->flag & SCONV_TO_UTF16BE)
					archive_be16enc(outp, UNICODE_R_CHAR);
				else
					archive_le16enc(outp, UNICODE_R_CHAR);
				outp += rbytes;
				avail -= rbytes;
			} else {
				/* Skip the illegal input bytes. */
				*outp++ = '?';
				avail--;
			}
			itp += from_size;
			remaining -= from_size;
			return_value = -1; /* failure */
		} else {
			/* E2BIG no output buffer,
			 * Increase an output buffer.  */
			as->length = outp - as->s;
			bs = as->buffer_length + remaining * 2;
			if (NULL == archive_string_ensure(as, bs))
				return (-1);
			outp = as->s + as->length;
			avail = as->buffer_length - as->length - to_size;
		}
	}
	as->length = outp - as->s;
	as->s[as->length] = 0;
	if (to_size == 2)
		as->s[as->length+1] = 0;
	return (return_value);
}


archive_string_conversion_to_charset(struct archive *a, const char *charset,
    int best_effort)
{
	int flag = SCONV_TO_CHARSET;

	if (best_effort)
		flag |= SCONV_BEST_EFFORT;
	return (get_sconv_object(a, get_current_charset(a), charset, flag));
}


archive_string_default_conversion_for_read(struct archive *a)
{
	(void)a; /* UNUSED */
	return (NULL);
}


my_atoi(const char *p)
{
	unsigned cp;

	cp = 0;
	while (*p) {
		if (*p >= '0' && *p <= '9')
			cp = cp * 10 + (*p - '0');
		else
			return (-1);
		p++;
	}
	return (cp);
}


archive_wstrcat(struct archive_wstring *as, const wchar_t *p)
{
	/* Ditto. */
	return archive_wstrncat(as, p, 0x1000000);
}


create_sconv_object(const char *fc, const char *tc,
    unsigned current_codepage, int flag)
{
	struct archive_string_conv *sc;

	sc = calloc(1, sizeof(*sc));
	if (sc == NULL)
		return (NULL);
	sc->next = NULL;
	sc->from_charset = strdup(fc);
	if (sc->from_charset == NULL) {
		free(sc);
		return (NULL);
	}
	sc->to_charset = strdup(tc);
	if (sc->to_charset == NULL) {
		free(sc->from_charset);
		free(sc);
		return (NULL);
	}
	archive_string_init(&sc->utftmp);

	if (flag & SCONV_TO_CHARSET) {
		/*
		 * Convert characters from the current locale charset to
		 * a specified charset.
		 */
		sc->from_cp = current_codepage;
		sc->to_cp = make_codepage_from_charset(tc);
#if defined(_WIN32) && !defined(__CYGWIN__)
		if (IsValidCodePage(sc->to_cp))
			flag |= SCONV_WIN_CP;
#endif
	} else if (flag & SCONV_FROM_CHARSET) {
		/*
		 * Convert characters from a specified charset to
		 * the current locale charset.
		 */
		sc->to_cp = current_codepage;
		sc->from_cp = make_codepage_from_charset(fc);
#if defined(_WIN32) && !defined(__CYGWIN__)
		if (IsValidCodePage(sc->from_cp))
			flag |= SCONV_WIN_CP;
#endif
	}

	/*
	 * Check if "from charset" and "to charset" are the same.
	 */
	if (strcmp(fc, tc) == 0 ||
	    (sc->from_cp != (unsigned)-1 && sc->from_cp == sc->to_cp))
		sc->same = 1;
	else
		sc->same = 0;

	/*
	 * Mark if "from charset" or "to charset" are UTF-8 or UTF-16BE/LE.
	 */
	if (strcmp(tc, "UTF-8") == 0)
		flag |= SCONV_TO_UTF8;
	else if (strcmp(tc, "UTF-16BE") == 0)
		flag |= SCONV_TO_UTF16BE;
	else if (strcmp(tc, "UTF-16LE") == 0)
		flag |= SCONV_TO_UTF16LE;
	if (strcmp(fc, "UTF-8") == 0)
		flag |= SCONV_FROM_UTF8;
	else if (strcmp(fc, "UTF-16BE") == 0)
		flag |= SCONV_FROM_UTF16BE;
	else if (strcmp(fc, "UTF-16LE") == 0)
		flag |= SCONV_FROM_UTF16LE;
#if defined(_WIN32) && !defined(__CYGWIN__)
	if (sc->to_cp == CP_UTF8)
		flag |= SCONV_TO_UTF8;
	else if (sc->to_cp == CP_UTF16BE)
		flag |= SCONV_TO_UTF16BE | SCONV_WIN_CP;
	else if (sc->to_cp == CP_UTF16LE)
		flag |= SCONV_TO_UTF16LE | SCONV_WIN_CP;
	if (sc->from_cp == CP_UTF8)
		flag |= SCONV_FROM_UTF8;
	else if (sc->from_cp == CP_UTF16BE)
		flag |= SCONV_FROM_UTF16BE | SCONV_WIN_CP;
	else if (sc->from_cp == CP_UTF16LE)
		flag |= SCONV_FROM_UTF16LE | SCONV_WIN_CP;
#endif

	/*
	 * Set a flag for Unicode NFD. Usually iconv cannot correctly
	 * handle it. So we have to translate NFD characters to NFC ones
	 * ourselves before iconv handles. Another reason is to prevent
	 * that the same sight of two filenames, one is NFC and other
	 * is NFD, would be in its directory.
	 * On Mac OS X, although its filesystem layer automatically
	 * convert filenames to NFD, it would be useful for filename
	 * comparing to find out the same filenames that we normalize
	 * that to be NFD ourselves.
	 */
	if ((flag & SCONV_FROM_CHARSET) &&
	    (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8))) {
#if defined(__APPLE__)
		if (flag & SCONV_TO_UTF8)
			flag |= SCONV_NORMALIZATION_D;
		else
#endif
			flag |= SCONV_NORMALIZATION_C;
	}
#if defined(__APPLE__)
	/*
	 * In case writing an archive file, make sure that a filename
	 * going to be passed to iconv is a Unicode NFC string since
	 * a filename in HFS Plus filesystem is a Unicode NFD one and
	 * iconv cannot handle it with "UTF-8" charset. It is simpler
	 * than a use of "UTF-8-MAC" charset.
	 */
	if ((flag & SCONV_TO_CHARSET) &&
	    (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) &&
	    !(flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8)))
		flag |= SCONV_NORMALIZATION_C;
	/*
	 * In case reading an archive file. make sure that a filename
	 * will be passed to users is a Unicode NFD string in order to
	 * correctly compare the filename with other one which comes
	 * from HFS Plus filesystem.
	 */
	if ((flag & SCONV_FROM_CHARSET) &&
	   !(flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) &&
	    (flag & SCONV_TO_UTF8))
		flag |= SCONV_NORMALIZATION_D;
#endif

#if defined(HAVE_ICONV)
	sc->cd_w = (iconv_t)-1;
	/*
	 * Create an iconv object.
	 */
	if (((flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) &&
	    (flag & (SCONV_FROM_UTF8 | SCONV_FROM_UTF16))) ||
	    (flag & SCONV_WIN_CP)) {
		/* This case we won't use iconv. */
		sc->cd = (iconv_t)-1;
	} else {
		sc->cd = iconv_open(tc, fc);
		if (sc->cd == (iconv_t)-1 && (sc->flag & SCONV_BEST_EFFORT)) {
			/*
			 * Unfortunately, all of iconv implements do support
			 * "CP932" character-set, so we should use "SJIS"
			 * instead if iconv_open failed.
			 */
			if (strcmp(tc, "CP932") == 0)
				sc->cd = iconv_open("SJIS", fc);
			else if (strcmp(fc, "CP932") == 0)
				sc->cd = iconv_open(tc, "SJIS");
		}
#if defined(_WIN32) && !defined(__CYGWIN__)
		/*
		 * archive_mstring on Windows directly convert multi-bytes
		 * into archive_wstring in order not to depend on locale
		 * so that you can do a I18N programming. This will be
		 * used only in archive_mstring_copy_mbs_len_l so far.
		 */
		if (flag & SCONV_FROM_CHARSET) {
			sc->cd_w = iconv_open("UTF-8", fc);
			if (sc->cd_w == (iconv_t)-1 &&
			    (sc->flag & SCONV_BEST_EFFORT)) {
				if (strcmp(fc, "CP932") == 0)
					sc->cd_w = iconv_open("UTF-8", "SJIS");
			}
		}
#endif /* _WIN32 && !__CYGWIN__ */
	}
#endif	/* HAVE_ICONV */

	sc->flag = flag;

	/*
	 * Set up converters.
	 */
	setup_converter(sc);

	return (sc);
}


archive_wstring_append(struct archive_wstring *as, const wchar_t *p, size_t s)
{
	if (archive_wstring_ensure(as, as->length + s + 1) == NULL)
		return (NULL);
	if (s)
		wmemmove(as->s + as->length, p, s);
	as->length += s;
	as->s[as->length] = 0;
	return (as);
}


archive_string_conversion_set_opt(struct archive_string_conv *sc, int opt)
{
	switch (opt) {
	/*
	 * A filename in UTF-8 was made with libarchive 2.x in a wrong
	 * assumption that wchar_t was Unicode.
	 * This option enables simulating the assumption in order to read
	 * that filename correctly.
	 */
	case SCONV_SET_OPT_UTF8_LIBARCHIVE2X:
#if (defined(_WIN32) && !defined(__CYGWIN__)) \
	 || defined(__STDC_ISO_10646__) || defined(__APPLE__)
		/*
		 * Nothing to do for it since wchar_t on these platforms
		 * is really Unicode.
		 */
		(void)sc; /* UNUSED */
#else
		if ((sc->flag & SCONV_UTF8_LIBARCHIVE_2) == 0) {
			sc->flag |= SCONV_UTF8_LIBARCHIVE_2;
			/* Set up string converters. */
			setup_converter(sc);
		}
#endif
		break;
	case SCONV_SET_OPT_NORMALIZATION_C:
		if ((sc->flag & SCONV_NORMALIZATION_C) == 0) {
			sc->flag |= SCONV_NORMALIZATION_C;
			sc->flag &= ~SCONV_NORMALIZATION_D;
			/* Set up string converters. */
			setup_converter(sc);
		}
		break;
	case SCONV_SET_OPT_NORMALIZATION_D:
#if defined(HAVE_ICONV)
		/*
		 * If iconv will take the string, do not change the
		 * setting of the normalization.
		 */
		if (!(sc->flag & SCONV_WIN_CP) &&
		     (sc->flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) &&
		    !(sc->flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8)))
			break;
#endif
		if ((sc->flag & SCONV_NORMALIZATION_D) == 0) {
			sc->flag |= SCONV_NORMALIZATION_D;
			sc->flag &= ~SCONV_NORMALIZATION_C;
			/* Set up string converters. */
			setup_converter(sc);
		}
		break;
	default:
		break;
	}
}


win_strncat_from_utf16be(struct archive_string *as, const void *_p,
    size_t bytes, struct archive_string_conv *sc)
{
	return (win_strncat_from_utf16(as, _p, bytes, sc, 1));
}


archive_mstring_copy_mbs_len_l(struct archive_mstring *aes,
    const char *mbs, size_t len, struct archive_string_conv *sc)
{
	int r;

	if (mbs == NULL) {
		aes->aes_set = 0;
		return (0);
	}
	archive_string_empty(&(aes->aes_mbs));
	archive_wstring_empty(&(aes->aes_wcs));
	archive_string_empty(&(aes->aes_utf8));
#if defined(_WIN32) && !defined(__CYGWIN__)
	/*
	 * Internationalization programming on Windows must use Wide
	 * characters because Windows platform cannot make locale UTF-8.
	 */
	if (sc == NULL) {
		if (archive_string_append(&(aes->aes_mbs),
			mbs, mbsnbytes(mbs, len)) == NULL) {
			aes->aes_set = 0;
			r = -1;
		} else {
			aes->aes_set = AES_SET_MBS;
			r = 0;
		}
#if defined(HAVE_ICONV)
	} else if (sc != NULL && sc->cd_w != (iconv_t)-1) {
		/*
		 * This case happens only when MultiByteToWideChar() cannot
		 * handle sc->from_cp, and we have to iconv in order to
		 * translate character-set to wchar_t,UTF-16.
		 */
		iconv_t cd = sc->cd;
		unsigned from_cp;
		int flag;

		/*
		 * Translate multi-bytes from some character-set to UTF-8.
		 */
		sc->cd = sc->cd_w;
		r = archive_strncpy_l(&(aes->aes_utf8), mbs, len, sc);
		sc->cd = cd;
		if (r != 0) {
			aes->aes_set = 0;
			return (r);
		}
		aes->aes_set = AES_SET_UTF8;

		/*
		 * Append the UTF-8 string into wstring.
		 */
		flag = sc->flag;
		sc->flag &= ~(SCONV_NORMALIZATION_C
				| SCONV_TO_UTF16| SCONV_FROM_UTF16);
		from_cp = sc->from_cp;
		sc->from_cp = CP_UTF8;
		r = archive_wstring_append_from_mbs_in_codepage(&(aes->aes_wcs),
			aes->aes_utf8.s, aes->aes_utf8.length, sc);
		sc->flag = flag;
		sc->from_cp = from_cp;
		if (r == 0)
			aes->aes_set |= AES_SET_WCS;
#endif
	} else {
		r = archive_wstring_append_from_mbs_in_codepage(
		    &(aes->aes_wcs), mbs, len, sc);
		if (r == 0)
			aes->aes_set = AES_SET_WCS;
		else
			aes->aes_set = 0;
	}
#else
	r = archive_strncpy_l(&(aes->aes_mbs), mbs, len, sc);
	if (r == 0)
		aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */
	else
		aes->aes_set = 0;
#endif
	return (r);
}


archive_strcat(struct archive_string *as, const void *p)
{
	/* strcat is just strncat without an effective limit.
	 * Assert that we'll never get called with a source
	 * string over 16MB.
	 * TODO: Review all uses of strcat in the source
	 * and try to replace them with strncat().
	 */
	return archive_strncat(as, p, 0x1000000);
}


archive_mstring_copy_wcs_len(struct archive_mstring *aes, const wchar_t *wcs,
    size_t len)
{
	if (wcs == NULL) {
		aes->aes_set = 0;
		return (0);
	}
	aes->aes_set = AES_SET_WCS; /* Only WCS form set. */
	archive_string_empty(&(aes->aes_mbs));
	archive_string_empty(&(aes->aes_utf8));
	archive_wstrncpy(&(aes->aes_wcs), wcs, len);
	return (0);
}


best_effort_strncat_from_utf16(struct archive_string *as, const void *_p,
    size_t bytes, struct archive_string_conv *sc, int be)
{
	const char *utf16 = (const char *)_p;
	char *mbs;
	uint32_t uc;
	int n, ret;

	(void)sc; /* UNUSED */
	/*
	 * Other case, we should do the best effort.
	 * If all character are ASCII(<0x7f), we can convert it.
	 * if not , we set a alternative character and return -1.
	 */
	ret = 0;
	if (archive_string_ensure(as, as->length + bytes +1) == NULL)
		return (-1);
	mbs = as->s + as->length;

	while ((n = utf16_to_unicode(&uc, utf16, bytes, be)) != 0) {
		if (n < 0) {
			n *= -1;
			ret =  -1;
		}
		bytes -= n;
		utf16 += n;

		if (uc > 127) {
			/* We cannot handle it. */
			*mbs++ = '?';
			ret =  -1;
		} else
			*mbs++ = (char)uc;
	}
	as->length = mbs - as->s;
	as->s[as->length] = '\0';
	return (ret);
}


setup_converter(struct archive_string_conv *sc)
{

	/* Reset. */
	sc->nconverter = 0;

	/*
	 * Perform special sequence for the incorrect UTF-8 filenames
	 * made by libarchive2.x.
	 */
	if (sc->flag & SCONV_UTF8_LIBARCHIVE_2) {
		add_converter(sc, strncat_from_utf8_libarchive2);
		return;
	}

	/*
	 * Convert a string to UTF-16BE/LE.
	 */
	if (sc->flag & SCONV_TO_UTF16) {
		/*
		 * If the current locale is UTF-8, we can translate
		 * a UTF-8 string into a UTF-16BE string.
		 */
		if (sc->flag & SCONV_FROM_UTF8) {
			add_converter(sc, archive_string_append_unicode);
			return;
		}

#if defined(_WIN32) && !defined(__CYGWIN__)
		if (sc->flag & SCONV_WIN_CP) {
			if (sc->flag & SCONV_TO_UTF16BE)
				add_converter(sc, win_strncat_to_utf16be);
			else
				add_converter(sc, win_strncat_to_utf16le);
			return;
		}
#endif

#if defined(HAVE_ICONV)
		if (sc->cd != (iconv_t)-1) {
			add_converter(sc, iconv_strncat_in_locale);
			return;
		}
#endif

		if (sc->flag & SCONV_BEST_EFFORT) {
			if (sc->flag & SCONV_TO_UTF16BE)
				add_converter(sc,
					best_effort_strncat_to_utf16be);
			else
				add_converter(sc,
					best_effort_strncat_to_utf16le);
		} else
			/* Make sure we have no converter. */
			sc->nconverter = 0;
		return;
	}

	/*
	 * Convert a string from UTF-16BE/LE.
	 */
	if (sc->flag & SCONV_FROM_UTF16) {
		/*
		 * At least we should normalize a UTF-16BE string.
		 */
		if (sc->flag & SCONV_NORMALIZATION_D)
			add_converter(sc,archive_string_normalize_D);
		else if (sc->flag & SCONV_NORMALIZATION_C)
			add_converter(sc, archive_string_normalize_C);

		if (sc->flag & SCONV_TO_UTF8) {
			/*
			 * If the current locale is UTF-8, we can translate
			 * a UTF-16BE/LE string into a UTF-8 string directly.
			 */
			if (!(sc->flag &
			    (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C)))
				add_converter(sc,
				    archive_string_append_unicode);
			return;
		}

#if defined(_WIN32) && !defined(__CYGWIN__)
		if (sc->flag & SCONV_WIN_CP) {
			if (sc->flag & SCONV_FROM_UTF16BE)
				add_converter(sc, win_strncat_from_utf16be);
			else
				add_converter(sc, win_strncat_from_utf16le);
			return;
		}
#endif

#if defined(HAVE_ICONV)
		if (sc->cd != (iconv_t)-1) {
			add_converter(sc, iconv_strncat_in_locale);
			return;
		}
#endif

		if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE))
		    == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE))
			add_converter(sc, best_effort_strncat_from_utf16be);
		else if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE))
		    == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE))
			add_converter(sc, best_effort_strncat_from_utf16le);
		else
			/* Make sure we have no converter. */
			sc->nconverter = 0;
		return;
	}

	if (sc->flag & SCONV_FROM_UTF8) {
		/*
		 * At least we should normalize a UTF-8 string.
		 */
		if (sc->flag & SCONV_NORMALIZATION_D)
			add_converter(sc,archive_string_normalize_D);
		else if (sc->flag & SCONV_NORMALIZATION_C)
			add_converter(sc, archive_string_normalize_C);

		/*
		 * Copy UTF-8 string with a check of CESU-8.
		 * Apparently, iconv does not check surrogate pairs in UTF-8
		 * when both from-charset and to-charset are UTF-8, and then
		 * we use our UTF-8 copy code.
		 */
		if (sc->flag & SCONV_TO_UTF8) {
			/*
			 * If the current locale is UTF-8, we can translate
			 * a UTF-16BE string into a UTF-8 string directly.
			 */
			if (!(sc->flag &
			    (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C)))
				add_converter(sc, strncat_from_utf8_to_utf8);
			return;
		}
	}

#if defined(_WIN32) && !defined(__CYGWIN__)
	/*
	 * On Windows we can use Windows API for a string conversion.
	 */
	if (sc->flag & SCONV_WIN_CP) {
		add_converter(sc, strncat_in_codepage);
		return;
	}
#endif

#if HAVE_ICONV
	if (sc->cd != (iconv_t)-1) {
		add_converter(sc, iconv_strncat_in_locale);
		/*
		 * iconv generally does not support UTF-8-MAC and so
		 * we have to the output of iconv from NFC to NFD if
		 * need.
		 */
		if ((sc->flag & SCONV_FROM_CHARSET) &&
		    (sc->flag & SCONV_TO_UTF8)) {
			if (sc->flag & SCONV_NORMALIZATION_D)
				add_converter(sc, archive_string_normalize_D);
		}
		return;
	}
#endif

	/*
	 * Try conversion in the best effort or no conversion.
	 */
	if ((sc->flag & SCONV_BEST_EFFORT) || sc->same)
		add_converter(sc, best_effort_strncat_in_locale);
	else
		/* Make sure we have no converter. */
		sc->nconverter = 0;
}


canonical_charset_name(const char *charset)
{
	char cs[16];
	char *p;
	const char *s;

	if (charset == NULL || charset[0] == '\0'
	    || strlen(charset) > 15)
		return (charset);

	/* Copy name to uppercase. */
	p = cs;
	s = charset;
	while (*s) {
		char c = *s++;
		if (c >= 'a' && c <= 'z')
			c -= 'a' - 'A';
		*p++ = c;
	}
	*p++ = '\0';

	if (strcmp(cs, "UTF-8") == 0 ||
	    strcmp(cs, "UTF8") == 0)
		return ("UTF-8");
	if (strcmp(cs, "UTF-16BE") == 0 ||
	    strcmp(cs, "UTF16BE") == 0)
		return ("UTF-16BE");
	if (strcmp(cs, "UTF-16LE") == 0 ||
	    strcmp(cs, "UTF16LE") == 0)
		return ("UTF-16LE");
	if (strcmp(cs, "CP932") == 0)
		return ("CP932");
	return (charset);
}


get_sconv_object(struct archive *a, const char *fc, const char *tc, int flag)
{
	struct archive_string_conv *sc;
	unsigned current_codepage;

	/* Check if we have made the sconv object. */
	sc = find_sconv_object(a, fc, tc);
	if (sc != NULL)
		return (sc);

	if (a == NULL)
		current_codepage = get_current_codepage();
	else
		current_codepage = a->current_codepage;

	sc = create_sconv_object(canonical_charset_name(fc),
	    canonical_charset_name(tc), current_codepage, flag);
	if (sc == NULL) {
		if (a != NULL)
			archive_set_error(a, ENOMEM,
			    "Could not allocate memory for "
			    "a string conversion object");
		return (NULL);
	}

	/*
	 * If there is no converter for current string conversion object,
	 * we cannot handle this conversion.
	 */
	if (sc->nconverter == 0) {
		if (a != NULL) {
#if HAVE_ICONV
			archive_set_error(a, ARCHIVE_ERRNO_MISC,
			    "iconv_open failed : Cannot handle ``%s''",
			    (flag & SCONV_TO_CHARSET)?tc:fc);
#else
			archive_set_error(a, ARCHIVE_ERRNO_MISC,
			    "A character-set conversion not fully supported "
			    "on this platform");
#endif
		}
		/* Failed; free a sconv object. */
		free_sconv_object(sc);
		return (NULL);
	}

	/*
	 * Success!
	 */
	if (a != NULL)
		add_sconv_object(a, sc);
	return (sc);
}


archive_string_append(struct archive_string *as, const char *p, size_t s)
{
	if (archive_string_ensure(as, as->length + s + 1) == NULL)
		return (NULL);
	if (s)
		memmove(as->s + as->length, p, s);
	as->length += s;
	as->s[as->length] = 0;
	return (as);
}


utf16le_to_unicode(uint32_t *pwc, const char *s, size_t n)
{
	return (utf16_to_unicode(pwc, s, n, 0));
}


archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src)
{
	dest->aes_set = src->aes_set;
	archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs));
	archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8));
	archive_wstring_copy(&(dest->aes_wcs), &(src->aes_wcs));
}


archive_string_concat(struct archive_string *dest, struct archive_string *src)
{
	if (archive_string_append(dest, src->s, src->length) == NULL)
		__archive_errx(1, "Out of memory");
}


archive_mstring_get_wcs(struct archive *a, struct archive_mstring *aes,
    const wchar_t **wp)
{
	int r, ret = 0;

	(void)a;/* UNUSED */
	/* Return WCS form if we already have it. */
	if (aes->aes_set & AES_SET_WCS) {
		*wp = aes->aes_wcs.s;
		return (ret);
	}

	*wp = NULL;
	/* Try converting MBS to WCS using native locale. */
	if (aes->aes_set & AES_SET_MBS) {
		archive_wstring_empty(&(aes->aes_wcs));
		r = archive_wstring_append_from_mbs(&(aes->aes_wcs),
		    aes->aes_mbs.s, aes->aes_mbs.length);
		if (r == 0) {
			aes->aes_set |= AES_SET_WCS;
			*wp = aes->aes_wcs.s;
		} else
			ret = -1;/* failure. */
	}
	return (ret);
}


combine_surrogate_pair(uint32_t uc, uint32_t uc2)
{
	uc -= 0xD800;
	uc *= 0x400;
	uc += uc2 - 0xDC00;
	uc += 0x10000;
	return (uc);
}


win_strncat_to_utf16(struct archive_string *as16, const void *_p,
    size_t length, struct archive_string_conv *sc, int bigendian)
{
	const char *s = (const char *)_p;
	char *u16;
	size_t count, avail;

	if (archive_string_ensure(as16,
	    as16->length + (length + 1) * 2) == NULL)
		return (-1);

	u16 = as16->s + as16->length;
	avail = as16->buffer_length - 2;
	if (sc->from_cp == CP_C_LOCALE) {
		/*
		 * "C" locale special process.
		 */
		count = 0;
		while (count < length && *s) {
			if (bigendian)
				archive_be16enc(u16, *s);
			else
				archive_le16enc(u16, *s);
			u16 += 2;
			s++;
			count++;
		}
		as16->length += count << 1;
		as16->s[as16->length] = 0;
		as16->s[as16->length+1] = 0;
		return (0);
	}
	do {
		count = MultiByteToWideChar(sc->from_cp,
		    MB_PRECOMPOSED, s, (int)length, (LPWSTR)u16, (int)avail>>1);
		/* Exit loop if we succeeded */
		if (count != 0 ||
		    GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
			break;
		}
		/* Expand buffer and try again */
		count = MultiByteToWideChar(sc->from_cp,
		    MB_PRECOMPOSED, s, (int)length, NULL, 0);
		if (archive_string_ensure(as16, (count +1) * 2)
		    == NULL)
			return (-1);
		u16 = as16->s + as16->length;
		avail = as16->buffer_length - 2;
	} while (1);
	as16->length += count * 2;
	as16->s[as16->length] = 0;
	as16->s[as16->length+1] = 0;
	if (count == 0)
		return (-1);

	if (is_big_endian()) {
		if (!bigendian) {
			while (count > 0) {
				uint16_t v = archive_be16dec(u16);
				archive_le16enc(u16, v);
				u16 += 2;
				count--;
			}
		}
	} else {
		if (bigendian) {
			while (count > 0) {
				uint16_t v = archive_le16dec(u16);
				archive_be16enc(u16, v);
				u16 += 2;
				count--;
			}
		}
	}
	return (0);
}


add_sconv_object(struct archive *a, struct archive_string_conv *sc)
{
	struct archive_string_conv **psc;

	/* Add a new sconv to sconv list. */
	psc = &(a->sconv);
	while (*psc != NULL)
		psc = &((*psc)->next);
	*psc = sc;
}


utf8_to_unicode(uint32_t *pwc, const char *s, size_t n)
{
	int cnt;

	cnt = _utf8_to_unicode(pwc, s, n);
	/* Any of Surrogate pair is not legal Unicode values. */
	if (cnt == 3 && IS_SURROGATE_PAIR_LA(*pwc))
		return (-3);
	return (cnt);
}


utf16be_to_unicode(uint32_t *pwc, const char *s, size_t n)
{
	return (utf16_to_unicode(pwc, s, n, 1));
}


archive_string_conversion_from_charset(struct archive *a, const char *charset,
    int best_effort)
{
	int flag = SCONV_FROM_CHARSET;

	if (best_effort)
		flag |= SCONV_BEST_EFFORT;
	return (get_sconv_object(a, charset, get_current_charset(a), flag));
}


get_current_oemcp(void)
{
	return (-1);/* Unknown */
}


archive_mstring_get_mbs(struct archive *a, struct archive_mstring *aes,
    const char **p)
{
	int r, ret = 0;

	(void)a; /* UNUSED */
	/* If we already have an MBS form, return that immediately. */
	if (aes->aes_set & AES_SET_MBS) {
		*p = aes->aes_mbs.s;
		return (ret);
	}

	*p = NULL;
	/* If there's a WCS form, try converting with the native locale. */
	if (aes->aes_set & AES_SET_WCS) {
		archive_string_empty(&(aes->aes_mbs));
		r = archive_string_append_from_wcs(&(aes->aes_mbs),
		    aes->aes_wcs.s, aes->aes_wcs.length);
		*p = aes->aes_mbs.s;
		if (r == 0) {
			aes->aes_set |= AES_SET_MBS;
			return (ret);
		} else
			ret = -1;
	}

	/*
	 * Only a UTF-8 form cannot avail because its conversion already
	 * failed at archive_mstring_update_utf8().
	 */
	return (ret);
}


archive_string_default_conversion_for_read(struct archive *a)
{
	const char *cur_charset = get_current_charset(a);
	char oemcp[16];

	/* NOTE: a check of cur_charset is unneeded but we need
	 * that get_current_charset() has been surely called at
	 * this time whatever C compiler optimized. */
	if (cur_charset != NULL &&
	    (a->current_codepage == CP_C_LOCALE ||
	     a->current_codepage == a->current_oemcp))
		return (NULL);/* no conversion. */

	_snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp);
	/* Make sure a null termination must be set. */
	oemcp[sizeof(oemcp)-1] = '\0';
	return (get_sconv_object(a, oemcp, cur_charset,
	    SCONV_FROM_CHARSET));
}


free_sconv_object(struct archive_string_conv *sc)
{
	free(sc->from_charset);
	free(sc->to_charset);
	archive_string_free(&sc->utftmp);
#if HAVE_ICONV
	if (sc->cd != (iconv_t)-1)
		iconv_close(sc->cd);
	if (sc->cd_w != (iconv_t)-1)
		iconv_close(sc->cd_w);
#endif
	free(sc);
}


invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc)
{
	const char *p = (const char *)_p;
	unsigned codepage;
	DWORD mbflag = MB_ERR_INVALID_CHARS;

	if (sc->flag & SCONV_FROM_CHARSET)
		codepage = sc->to_cp;
	else
		codepage = sc->from_cp;

	if (codepage == CP_C_LOCALE)
		return (0);
	if (codepage != CP_UTF8)
		mbflag |= MB_PRECOMPOSED;

	if (MultiByteToWideChar(codepage, mbflag, p, (int)n, NULL, 0) == 0)
		return (-1); /* Invalid */
	return (0); /* Okay */
}


get_current_codepage(void)
{
	return (-1);/* Unknown */
}


archive_mstring_copy_mbs(struct archive_mstring *aes, const char *mbs)
{
	if (mbs == NULL) {
		aes->aes_set = 0;
		return (0);
	}
	return (archive_mstring_copy_mbs_len(aes, mbs, strlen(mbs)));
}


archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest,
    const char *s, size_t length, struct archive_string_conv *sc)
{
	int count, ret = 0;
	UINT from_cp;

	if (sc != NULL)
		from_cp = sc->from_cp;
	else
		from_cp = get_current_codepage();

	if (from_cp == CP_C_LOCALE) {
		/*
		 * "C" locale special processing.
		 */
		wchar_t *ws;
		const unsigned char *mp;

		if (NULL == archive_wstring_ensure(dest,
		    dest->length + length + 1))
			return (-1);

		ws = dest->s + dest->length;
		mp = (const unsigned char *)s;
		count = 0;
		while (count < (int)length && *mp) {
			*ws++ = (wchar_t)*mp++;
			count++;
		}
	} else if (sc != NULL &&
	    (sc->flag & (SCONV_NORMALIZATION_C | SCONV_NORMALIZATION_D))) {
		/*
		 * Normalize UTF-8 and UTF-16BE and convert it directly
		 * to UTF-16 as wchar_t.
		 */
		struct archive_string u16;
		int saved_flag = sc->flag;/* save current flag. */

		if (is_big_endian())
			sc->flag |= SCONV_TO_UTF16BE;
		else
			sc->flag |= SCONV_TO_UTF16LE;

		if (sc->flag & SCONV_FROM_UTF16) {
			/*
			 *  UTF-16BE/LE NFD ===> UTF-16 NFC
			 *  UTF-16BE/LE NFC ===> UTF-16 NFD
			 */
			count = (int)utf16nbytes(s, length);
		} else {
			/*
			 *  UTF-8 NFD ===> UTF-16 NFC
			 *  UTF-8 NFC ===> UTF-16 NFD
			 */
			count = (int)mbsnbytes(s, length);
		}
		u16.s = (char *)dest->s;
		u16.length = dest->length << 1;;
		u16.buffer_length = dest->buffer_length;
		if (sc->flag & SCONV_NORMALIZATION_C)
			ret = archive_string_normalize_C(&u16, s, count, sc);
		else
			ret = archive_string_normalize_D(&u16, s, count, sc);
		dest->s = (wchar_t *)u16.s;
		dest->length = u16.length >> 1;
		dest->buffer_length = u16.buffer_length;
		sc->flag = saved_flag;/* restore the saved flag. */
		return (ret);
	} else if (sc != NULL && (sc->flag & SCONV_FROM_UTF16)) {
		count = (int)utf16nbytes(s, length);
		count >>= 1; /* to be WCS length */
		/* Allocate memory for WCS. */
		if (NULL == archive_wstring_ensure(dest,
		    dest->length + count + 1))
			return (-1);
		wmemcpy(dest->s + dest->length, (const wchar_t *)s, count);
		if ((sc->flag & SCONV_FROM_UTF16BE) && !is_big_endian()) {
			uint16_t *u16 = (uint16_t *)(dest->s + dest->length);
			int b;
			for (b = 0; b < count; b++) {
				uint16_t val = archive_le16dec(u16+b);
				archive_be16enc(u16+b, val);
			}
		} else if ((sc->flag & SCONV_FROM_UTF16LE) && is_big_endian()) {
			uint16_t *u16 = (uint16_t *)(dest->s + dest->length);
			int b;
			for (b = 0; b < count; b++) {
				uint16_t val = archive_be16dec(u16+b);
				archive_le16enc(u16+b, val);
			}
		}
	} else {
		DWORD mbflag;
		size_t buffsize;

		if (sc == NULL)
			mbflag = 0;
		else if (sc->flag & SCONV_FROM_CHARSET) {
			/* Do not trust the length which comes from
			 * an archive file. */
			length = mbsnbytes(s, length);
			mbflag = 0;
		} else
			mbflag = MB_PRECOMPOSED;

		buffsize = dest->length + length + 1;
		do {
			/* Allocate memory for WCS. */
			if (NULL == archive_wstring_ensure(dest, buffsize))
				return (-1);
			/* Convert MBS to WCS. */
			count = MultiByteToWideChar(from_cp,
			    mbflag, s, (int)length, dest->s + dest->length,
			    (int)(dest->buffer_length >> 1) -1);
			if (count == 0 &&
			    GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
				/* Expand the WCS buffer. */
				buffsize = dest->buffer_length << 1;
				continue;
			}
			if (count == 0 && length != 0)
				ret = -1;
			break;
		} while (1);
	}
	dest->length += count;
	dest->s[dest->length] = L'\0';
	return (ret);
}


make_codepage_from_charset(const char *charset)
{
	char cs[16];
	char *p;
	unsigned cp;
	int a, b;

	if (charset == NULL || strlen(charset) > 15)
		return -1;

	/* Copy name to uppercase. */
	p = cs;
	while (*charset) {
		char c = *charset++;
		if (c >= 'a' && c <= 'z')
			c -= 'a' - 'A';
		*p++ = c;
	}
	*p++ = '\0';
	cp = -1;

	/* Look it up in the table first, so that we can easily
	 * override CP367, which we map to 1252 instead of 367. */
	a = 0;
	b = sizeof(charsets)/sizeof(charsets[0]);
	while (b > a) {
		int c = (b + a) / 2;
		int r = strcmp(charsets[c].name, cs);
		if (r < 0)
			a = c + 1;
		else if (r > 0)
			b = c;
		else
			return charsets[c].cp;
	}

	/* If it's not in the table, try to parse it. */
	switch (*cs) {
	case 'C':
		if (cs[1] == 'P' && cs[2] >= '0' && cs[2] <= '9') {
			cp = my_atoi(cs + 2);
		} else if (strcmp(cs, "CP_ACP") == 0)
			cp = get_current_codepage();
		else if (strcmp(cs, "CP_OEMCP") == 0)
			cp = get_current_oemcp();
		break;
	case 'I':
		if (cs[1] == 'B' && cs[2] == 'M' &&
		    cs[3] >= '0' && cs[3] <= '9') {
			cp = my_atoi(cs + 3);
		}
		break;
	case 'W':
		if (strncmp(cs, "WINDOWS-", 8) == 0) {
			cp = my_atoi(cs + 8);
			if (cp != 874 && (cp < 1250 || cp > 1258))
				cp = -1;/* This may invalid code. */
		}
		break;
	}
	return (cp);
}


get_current_codepage(void)
{
	char *locale, *p;
	unsigned cp;

	locale = setlocale(LC_CTYPE, NULL);
	if (locale == NULL)
		return (GetACP());
	if (locale[0] == 'C' && locale[1] == '\0')
		return (CP_C_LOCALE);
	p = strrchr(locale, '.');
	if (p == NULL)
		return (GetACP());
	if (strcmp(p+1, "utf8") == 0)
		return CP_UTF8;
	cp = my_atoi(p+1);
	if ((int)cp <= 0)
		return (GetACP());
	return (cp);
}


archive_mstring_copy_wcs(struct archive_mstring *aes, const wchar_t *wcs)
{
	return archive_mstring_copy_wcs_len(aes, wcs,
				wcs == NULL ? 0 : wcslen(wcs));
}