diff options
Diffstat (limited to 'Src/utils.c')
-rw-r--r-- | Src/utils.c | 369 |
1 files changed, 218 insertions, 151 deletions
diff --git a/Src/utils.c b/Src/utils.c index 55f2d1ab0..ce4e875fd 100644 --- a/Src/utils.c +++ b/Src/utils.c @@ -74,9 +74,6 @@ set_widearray(char *mb_array, Widechar_array wca) } wca->len = 0; - if (!isset(MULTIBYTE)) - return; - if (mb_array) { VARARR(wchar_t, tmpwcs, strlen(mb_array)); wchar_t *wcptr = tmpwcs; @@ -87,8 +84,7 @@ set_widearray(char *mb_array, Widechar_array wca) int mblen; if ((unsigned char) *mb_array <= 0x7f) { - mb_array++; - *wcptr++ = (wchar_t)*mb_array; + *wcptr++ = (wchar_t)*mb_array++; continue; } @@ -126,14 +122,20 @@ set_widearray(char *mb_array, Widechar_array wca) (implemented by zerrmsg()): Code Argument types Prints - %s const char * C string (null terminated) - %l const char *, int C string of given length (null not required) + %s const char * C string (metafied, null terminated) + (output "nice") + %l const char *, int C string of given length (not metafied) + (output raw) %L long decimal value %d int decimal value %z zlong decimal value %% (none) literal '%' %c int character at that codepoint - %e int strerror() message (argument is typically 'errno') + %e int strerror() message (argument usually 'errno') + (output raw) + + For %s and %l, the caller is responsible for assuring end-of-string + is not in the middle of a metafy pair (%s) or a multibyte character. */ static void @@ -314,14 +316,9 @@ zerrmsg(FILE *file, const char *fmt, va_list ap) nicezputs(str, file); break; case 'l': { - char *s; str = va_arg(ap, const char *); num = va_arg(ap, int); - num = metalen(str, num); - s = zhalloc(num + 1); - memcpy(s, str, num); - s[num] = '\0'; - nicezputs(s, file); + fwrite(str, num, 1, file); break; } case 'L': @@ -380,11 +377,13 @@ zerrmsg(FILE *file, const char *fmt, va_list ap) fflush(file); } +#ifdef HAVE_SETUPTERM /* * Wrapper for setupterm() and del_curterm(). * These are called from terminfo.c and termcap.c. */ static int term_count; /* reference count of cur_term */ +#endif /**/ mod_export void @@ -717,7 +716,8 @@ wcs_nicechar(wchar_t c, size_t *widthp, char **swidep) */ /**/ -mod_export int is_wcs_nicechar(wchar_t c) +mod_export int +is_wcs_nicechar(wchar_t c) { if (!WC_ISPRINT(c) && (c < 0x80 || !isset(PRINTEIGHTBIT))) { if (c == 0x7f || c == L'\n' || c == L'\t' || c < 0x20) @@ -1071,7 +1071,7 @@ get_username(void) cached_uid = current_uid; zsfree(cached_username); if ((pswd = getpwuid(current_uid))) - cached_username = ztrdup(pswd->pw_name); + cached_username = ztrdup_metafy(pswd->pw_name); else cached_username = ztrdup(""); } @@ -1733,6 +1733,13 @@ freestr(void *a) mod_export void gettyinfo(struct ttyinfo *ti) { + fdgettyinfo(SHTTY, ti); +} + +/**/ +mod_export void +fdgettyinfo(int SHTTY, struct ttyinfo *ti) +{ if (SHTTY != -1) { #ifdef HAVE_TERMIOS_H # ifdef HAVE_TCGETATTR @@ -1758,6 +1765,13 @@ gettyinfo(struct ttyinfo *ti) mod_export void settyinfo(struct ttyinfo *ti) { + fdsettyinfo(SHTTY, ti); +} + +/**/ +mod_export void +fdsettyinfo(int SHTTY, struct ttyinfo *ti) +{ if (SHTTY != -1) { #ifdef HAVE_TERMIOS_H # ifdef HAVE_TCGETATTR @@ -1996,6 +2010,28 @@ redup(int x, int y) { int ret = y; +#ifdef HAVE_FPURGE + /* Make sure buffers are cleared when changing descriptor for a + * FILE object. No fflush() here because the only way anything + * can legitimately be left in the buffer is when an error has + * occurred, so attempting flush here would at best error again + * and at worst squirt out something unexpected. + */ + if (stdout && y == fileno(stdout)) + fpurge(stdout); + if (stderr && y == fileno(stderr)) + fpurge(stderr); + if (shout && y == fileno(shout)) + fpurge(shout); + if (xtrerr && y == fileno(xtrerr)) + fpurge(xtrerr); +#ifndef _IONBF + /* See init.c setupshin() -- stdin otherwise unbuffered */ + if (stdin && y == fileno(stdin)) + fpurge(stdin); +#endif +#endif + if(x < 0) zclose(y); else if (x != y) { @@ -3123,7 +3159,7 @@ spckword(char **s, int hist, int cmd, int ask) if (**s == String && !*t) { guess = *s + 1; - if (itype_end(guess, IIDENT, 1) == guess) + if (itype_end(guess, INAMESPC, 1) == guess) return; ic = String; d = 100; @@ -4119,8 +4155,9 @@ inittyptab(void) * having IIDENT here is a good idea at all, but this code * should disappear into history... */ - for (t0 = 0240; t0 != 0400; t0++) - typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD; + if isset(MULTIBYTE) + for (t0 = 0240; t0 != 0400; t0++) + typtab[t0] = IALPHA | IALNUM | IIDENT | IUSER | IWORD; #endif /* typtab['.'] |= IIDENT; */ /* Allow '.' in variable names - broken */ typtab['_'] = IIDENT | IUSER; @@ -4135,11 +4172,24 @@ inittyptab(void) typtab[t0] |= ITOK | IMETA; for (t0 = (int) (unsigned char) Snull; t0 <= (int) (unsigned char) Nularg; t0++) typtab[t0] |= ITOK | IMETA | INULL; - for (s = ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ? - DEFAULT_IFS_SH : DEFAULT_IFS; *s; s++) { + /* ifs */ +#define CURRENT_DEFAULT_IFS (EMULATION(EMULATE_KSH|EMULATE_SH) ? \ + DEFAULT_IFS_SH : DEFAULT_IFS) +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) { + set_widearray(ifs ? ifs : CURRENT_DEFAULT_IFS, &ifs_wide); + if (ifs && !ifs_wide.chars) { + zwarn("IFS has an invalid character; resetting IFS to default"); + zsfree(ifs); + ifs = ztrdup(CURRENT_DEFAULT_IFS); + set_widearray(ifs, &ifs_wide); + } + } +#endif + for (s = ifs ? ifs : CURRENT_DEFAULT_IFS; *s; s++) { int c = (unsigned char) (*s == Meta ? *++s ^ 32 : *s); #ifdef MULTIBYTE_SUPPORT - if (!isascii(c)) { + if (isset(MULTIBYTE) && !isascii(c)) { /* see comment for wordchars below */ continue; } @@ -4152,10 +4202,15 @@ inittyptab(void) } typtab[c] |= ISEP; } + /* wordchars */ +#ifdef MULTIBYTE_SUPPORT + if (isset(MULTIBYTE)) + set_widearray(wordchars, &wordchars_wide); +#endif for (s = wordchars ? wordchars : DEFAULT_WORDCHARS; *s; s++) { int c = (unsigned char) (*s == Meta ? *++s ^ 32 : *s); #ifdef MULTIBYTE_SUPPORT - if (!isascii(c)) { + if (isset(MULTIBYTE) && !isascii(c)) { /* * If we have support for multibyte characters, we don't * handle non-ASCII characters here; instead, we turn @@ -4168,11 +4223,6 @@ inittyptab(void) #endif typtab[c] |= IWORD; } -#ifdef MULTIBYTE_SUPPORT - set_widearray(wordchars, &wordchars_wide); - set_widearray(ifs ? ifs : EMULATION(EMULATE_KSH|EMULATE_SH) ? - DEFAULT_IFS_SH : DEFAULT_IFS, &ifs_wide); -#endif for (s = SPECCHARS; *s; s++) typtab[(unsigned char) *s] |= ISPECIAL; if (typtab_flags & ZTF_SP_COMMA) @@ -4310,13 +4360,27 @@ wcsitype(wchar_t c, int itype) * If "once" is set, just test the first character, i.e. (outptr != * inptr) tests whether the first character is valid in an identifier. * - * Currently this is only called with itype IIDENT, IUSER or ISEP. + * Currently called only with itype INAMESPC, IIDENT, IUSER or ISEP. */ /**/ mod_export char * itype_end(const char *ptr, int itype, int once) { + if (itype == INAMESPC) { + itype = IIDENT; + if (!isset(POSIXIDENTIFIERS) || EMULATION(EMULATE_KSH)) { + /* Special case for names containing ".", ksh93 namespaces */ + char *t = itype_end(ptr + (*ptr == '.'), itype, 0); + if (t > ptr + (*ptr == '.')) { + if (*t == '.') + ptr = t + 1; /* Fall through */ + else if (!once) + return t; + } + } + } + #ifdef MULTIBYTE_SUPPORT if (isset(MULTIBYTE) && (itype != IIDENT || !isset(POSIXIDENTIFIERS))) { @@ -5213,6 +5277,7 @@ nicedupstring(char const *s) } +/**/ #ifndef MULTIBYTE_SUPPORT /* Unmetafy and output a string, displaying special characters readably. */ @@ -5247,8 +5312,9 @@ niceztrlen(char const *s) } return l; } -#endif +/**/ +#endif /**/ #ifdef MULTIBYTE_SUPPORT @@ -6646,11 +6712,14 @@ dquotedzputs(char const *s, FILE *stream) # if defined(HAVE_NL_LANGINFO) && defined(CODESET) && !defined(__STDC_ISO_10646__) /* Convert a character from UCS4 encoding to UTF-8 */ -static size_t +static int ucs4toutf8(char *dest, unsigned int wval) { - size_t len; + int len; + /* UCS4 is now equvalent to UTF-32 and limited to 0 - 0x10_FFFF. + * This function accepts 0 - 0x7FFF_FFFF (old range of UCS4) to be + * compatible with wctomb(3) (in UTF-8 locale) on Linux. */ if (wval < 0x80) len = 1; else if (wval < 0x800) @@ -6661,8 +6730,12 @@ ucs4toutf8(char *dest, unsigned int wval) len = 4; else if (wval < 0x4000000) len = 5; - else + else if (wval < 0x80000000) len = 6; + else { + zerr("character not in range"); + return -1; + } switch (len) { /* falls through except to the last case */ case 6: dest[5] = (wval & 0x3f) | 0x80; wval >>= 6; @@ -6679,30 +6752,89 @@ ucs4toutf8(char *dest, unsigned int wval) } #endif +/* Convert UCS4 to a multibyte character in current locale. + * Result is saved in buf (must be at least MB_CUR_MAX bytes long). + * Returns the number of bytes saved in buf, or -1 if conversion fails. */ -/* - * The following only occurs once or twice in the code, but in different - * places depending how character set conversion is implemented. - */ -#define CHARSET_FAILED() \ - if (how & GETKEY_DOLLAR_QUOTE) { \ - while ((*tdest++ = *++s)) { \ - if (how & GETKEY_UPDATE_OFFSET) { \ - if (s - sstart > *misc) \ - (*misc)++; \ - } \ - if (*s == Snull) { \ - *len = (s - sstart) + 1; \ - *tdest = '\0'; \ - return buf; \ - } \ - } \ - *len = tdest - buf; \ - return buf; \ - } \ - *t = '\0'; \ - *len = t - buf; \ - return buf +/**/ +int +ucs4tomb(unsigned int wval, char *buf) +{ +#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__) + int count = wctomb(buf, (wchar_t)wval); + if (count == -1) + zerr("character not in range"); + return count; +#else /* !(HAVE_WCHAR_H && HAVE_WCTOMB && __STDC_ISO_10646__) */ +# if defined(HAVE_NL_LANGINFO) && defined(CODESET) + if (!strcmp(nl_langinfo(CODESET), "UTF-8")) { + return ucs4toutf8(buf, wval); + } else { +# ifdef HAVE_ICONV + iconv_t cd; + char inbuf[4], *bsave = buf; + ICONV_CONST char *inptr = inbuf; + size_t inbytes = 4, outbytes = 6; + const char *codesetstr = nl_langinfo(CODESET); + size_t count; + int i; + + /* + * If the code set isn't handled, we'd better assume it's US-ASCII + * rather than just failing hopelessly. Solaris has a weird habit + * of returning 646. This is handled by the native iconv(), but + * not by GNU iconv; what's more, some versions of the native iconv + * don't handle standard names like ASCII. + * + * This should only be a problem if there's a mismatch between the + * NLS and the iconv in use, which probably only means if libiconv + * is in use. We checked at configure time if our libraries pulled + * in _libiconv_version, which should be a good test. + * + * It shouldn't ever be NULL, but while we're being paranoid... + */ +# ifdef ICONV_FROM_LIBICONV + if (!codesetstr || !*codesetstr) + codesetstr = "US-ASCII"; +# endif + cd = iconv_open(codesetstr, "UCS-4BE"); +# ifdef ICONV_FROM_LIBICONV + if (cd == (iconv_t)-1 && !strcmp(codesetstr, "646")) { + codesetstr = "US-ASCII"; + cd = iconv_open(codesetstr, "UCS-4BE"); + } +# endif + if (cd == (iconv_t)-1) { + zerr("cannot do charset conversion (iconv failed)"); + return -1; + } + + /* store value in big endian form */ + for (i=3; i>=0; i--) { + inbuf[i] = wval & 0xff; + wval >>= 8; + } + count = iconv(cd, &inptr, &inbytes, &buf, &outbytes); + iconv_close(cd); + if (count) { + /* -1 indicates error. Positive value means number of "invalid" + * (or "non-reversible") conversions, which we consider as + * "out-of-range" characters. */ + zerr("character not in range"); + return -1; + } + return buf - bsave; +# else /* !HAVE_ICONV */ + zerr("cannot do charset conversion (iconv not available)"); + return -1; +# endif /* HAVE_ICONV */ + } +# else /* !(HAVE_NL_LANGINFO && CODESET) */ + zerr("cannot do charset conversion (NLS not supported)"); + return -1; +# endif /* HAVE_NL_LANGINFO && CODESET */ +#endif /* HAVE_WCHAR_H && HAVE_WCTOMB && __STDC_ISO_10646__ */ +} /* * Decode a key string, turning it into the literal characters. @@ -6759,21 +6891,6 @@ getkeystring(char *s, int *len, int how, int *misc) char *t, *tdest = NULL, *u = NULL, *sstart = s, *tbuf = NULL; char svchar = '\0'; int meta = 0, control = 0, ignoring = 0; - int i; -#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__) - wint_t wval; - int count; -#else - unsigned int wval; -# if defined(HAVE_NL_LANGINFO) && defined(CODESET) -# if defined(HAVE_ICONV) - iconv_t cd; - char inbuf[4]; - size_t inbytes, outbytes; -# endif - size_t count; -# endif -#endif DPUTS((how & GETKEY_UPDATE_OFFSET) && (how & ~(GETKEYS_DOLLARS_QUOTE|GETKEY_UPDATE_OFFSET)), @@ -6838,7 +6955,8 @@ getkeystring(char *s, int *len, int how, int *misc) } for (; *s; s++) { if (*s == '\\' && s[1]) { - int miscadded; + int miscadded, count, i; + unsigned int wval; if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) { (*misc)--; miscadded = 1; @@ -6953,86 +7071,32 @@ getkeystring(char *s, int *len, int how, int *misc) *misc = wval; return s+1; } -#if defined(HAVE_WCHAR_H) && defined(HAVE_WCTOMB) && defined(__STDC_ISO_10646__) - count = wctomb(t, (wchar_t)wval); + count = ucs4tomb(wval, t); if (count == -1) { - zerr("character not in range"); - CHARSET_FAILED(); + if (how & GETKEY_DOLLAR_QUOTE) { + while ((*tdest++ = *++s)) { + if (how & GETKEY_UPDATE_OFFSET) { + if (s - sstart > *misc) + (*misc)++; + } + if (*s == Snull) { + *len = (s - sstart) + 1; + *tdest = '\0'; + return buf; + } + } + *len = tdest - buf; + } + else { + *t = '\0'; + *len = t - buf; + } + return buf; } if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) (*misc) += count; t += count; -# else -# if defined(HAVE_NL_LANGINFO) && defined(CODESET) - if (!strcmp(nl_langinfo(CODESET), "UTF-8")) { - count = ucs4toutf8(t, wval); - t += count; - if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) - (*misc) += count; - } else { -# ifdef HAVE_ICONV - ICONV_CONST char *inptr = inbuf; - const char *codesetstr = nl_langinfo(CODESET); - inbytes = 4; - outbytes = 6; - /* store value in big endian form */ - for (i=3;i>=0;i--) { - inbuf[i] = wval & 0xff; - wval >>= 8; - } - /* - * If the code set isn't handled, we'd better - * assume it's US-ASCII rather than just failing - * hopelessly. Solaris has a weird habit of - * returning 646. This is handled by the - * native iconv(), but not by GNU iconv; what's - * more, some versions of the native iconv don't - * handle standard names like ASCII. - * - * This should only be a problem if there's a - * mismatch between the NLS and the iconv in use, - * which probably only means if libiconv is in use. - * We checked at configure time if our libraries - * pulled in _libiconv_version, which should be - * a good test. - * - * It shouldn't ever be NULL, but while we're - * being paranoid... - */ -#ifdef ICONV_FROM_LIBICONV - if (!codesetstr || !*codesetstr) - codesetstr = "US-ASCII"; -#endif - cd = iconv_open(codesetstr, "UCS-4BE"); -#ifdef ICONV_FROM_LIBICONV - if (cd == (iconv_t)-1 && !strcmp(codesetstr, "646")) { - codesetstr = "US-ASCII"; - cd = iconv_open(codesetstr, "UCS-4BE"); - } -#endif - if (cd == (iconv_t)-1) { - zerr("cannot do charset conversion (iconv failed)"); - CHARSET_FAILED(); - } - count = iconv(cd, &inptr, &inbytes, &t, &outbytes); - iconv_close(cd); - if (count == (size_t)-1) { - zerr("character not in range"); - CHARSET_FAILED(); - } - if ((how & GETKEY_UPDATE_OFFSET) && s - sstart < *misc) - (*misc) += count; -# else - zerr("cannot do charset conversion (iconv not available)"); - CHARSET_FAILED(); -# endif - } -# else - zerr("cannot do charset conversion (NLS not supported)"); - CHARSET_FAILED(); -# endif -# endif if (how & GETKEY_DOLLAR_QUOTE) { char *t2; for (t2 = tbuf; t2 < t; t2++) { @@ -7499,8 +7563,8 @@ restoredir(struct dirsav *d) else if (d->level < 0) err = -1; if (d->dev || d->ino) { - stat(".", &sbuf); - if (sbuf.st_ino != d->ino || sbuf.st_dev != d->dev) + if (stat(".", &sbuf) < 0 || + sbuf.st_ino != d->ino || sbuf.st_dev != d->dev) err = -2; } return err; @@ -7525,9 +7589,9 @@ privasserted(void) /* POSIX doesn't define a way to test whether a capability set * * is empty or not. Typical. I hope this is conforming... */ cap_flag_value_t val; - cap_value_t n; - for(n = 0; !cap_get_flag(caps, n, CAP_EFFECTIVE, &val); n++) - if(val) { + cap_value_t cap; + for(cap = 0; !cap_get_flag(caps, cap, CAP_EFFECTIVE, &val); cap++) + if(val && cap != CAP_WAKE_ALARM) { cap_free(caps); return 1; } @@ -7571,6 +7635,7 @@ mode_to_octal(mode_t mode) return m; } +/**/ #ifdef MAILDIR_SUPPORT /* * Stat a file. If it's a maildir, check all messages @@ -7694,4 +7759,6 @@ mailstat(char *path, struct stat *st) *st = st_ret_last = st_ret; return 0; } + +/**/ #endif |