summaryrefslogtreecommitdiff
path: root/Src/utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'Src/utils.c')
-rw-r--r--Src/utils.c387
1 files changed, 342 insertions, 45 deletions
diff --git a/Src/utils.c b/Src/utils.c
index 271c800fd..4c4dc55cd 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -82,7 +82,7 @@ set_widearray(char *mb_array, Widechar_array wca)
wchar_t *wcptr = tmpwcs;
wint_t wci;
- mb_metacharinit();
+ mb_charinit();
while (*mb_array) {
int mblen = mb_metacharlenconv(mb_array, &wci);
@@ -248,7 +248,7 @@ VA_DCL
VA_START(ap, message);
VA_GET_ARG(ap, message, const char *);
- if ((filename = getsparam("ZSH_DEBUG_LOG")) != NULL &&
+ if ((filename = getsparam_u("ZSH_DEBUG_LOG")) != NULL &&
(file = fopen(filename, "a")) != NULL) {
zerrmsg(file, message, ap);
fclose(file);
@@ -332,7 +332,7 @@ zerrmsg(FILE *file, const char *fmt, va_list ap)
case 'c':
num = va_arg(ap, int);
#ifdef MULTIBYTE_SUPPORT
- mb_metacharinit();
+ mb_charinit();
zputs(wcs_nicechar(num, NULL, NULL), file);
#else
zputs(nicechar(num), file);
@@ -461,12 +461,13 @@ static mbstate_t mb_shiftstate;
/*
* Initialise multibyte state: called before a sequence of
- * wcs_nicechar() or mb_metacharlenconv().
+ * wcs_nicechar(), mb_metacharlenconv(), or
+ * mb_charlenconv().
*/
/**/
mod_export void
-mb_metacharinit(void)
+mb_charinit(void)
{
memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
}
@@ -500,7 +501,7 @@ mb_metacharinit(void)
* (but not both). (Note the complication that the wide character
* part may contain metafied characters.)
*
- * The caller needs to call mb_metacharinit() before the first call, to
+ * The caller needs to call mb_charinit() before the first call, to
* set up the multibyte shift state for a range of characters.
*/
@@ -1948,7 +1949,8 @@ extern char *_mktemp(char *);
/* Get a unique filename for use as a temporary file. If "prefix" is
* NULL, the name is relative to $TMPPREFIX; If it is non-NULL, the
* unique suffix includes a prefixed '.' for improved readability. If
- * "use_heap" is true, we allocate the returned name on the heap. */
+ * "use_heap" is true, we allocate the returned name on the heap.
+ * The string passed as "prefix" is expected to be metafied. */
/**/
mod_export char *
@@ -1975,6 +1977,9 @@ gettempname(const char *prefix, int use_heap)
return ret;
}
+/* The gettempfile() "prefix" is expected to be metafied, see hist.c
+ * and gettempname(). */
+
/**/
mod_export int
gettempfile(const char *prefix, int use_heap, char **tempname)
@@ -2873,6 +2878,10 @@ ztrftimebuf(int *bufsizeptr, int decr)
* not enough memory --- and return -1. Not guaranteed to be portable,
* since the strftime() interface doesn't make any guarantees about
* the state of the buffer if it returns zero.
+ *
+ * fmt is metafied, but we need to unmetafy it on the fly to
+ * pass into strftime / combine with the output from strftime.
+ * The return value in buf is not metafied.
*/
/**/
@@ -2882,7 +2891,7 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
int hr12;
#ifdef HAVE_STRFTIME
int decr;
- char tmp[4];
+ char *fmtstart;
#else
static char *astr[] =
{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
@@ -2893,12 +2902,22 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
char *origbuf = buf;
- while (*fmt)
- if (*fmt == '%') {
+ while (*fmt) {
+ if (*fmt == Meta) {
+ int chr = fmt[1] ^ 32;
+ if (ztrftimebuf(&bufsize, 1))
+ return -1;
+ *buf++ = chr;
+ fmt += 2;
+ } else if (*fmt == '%') {
int strip;
int digs = 3;
+#ifdef HAVE_STRFTIME
+ fmtstart =
+#endif
fmt++;
+
if (*fmt == '-') {
strip = 1;
fmt++;
@@ -2923,6 +2942,21 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
*/
if (ztrftimebuf(&bufsize, 2))
return -1;
+#ifdef HAVE_STRFTIME
+ /* Our internal handling doesn't handle padding and other gnu extensions,
+ * so here we detect them and pass over to strftime(). We don't want
+ * to do this unconditionally though, as we have some extensions that
+ * strftime() doesn't have (%., %f, %L and %K) */
+morefmt:
+ if (!((fmt - fmtstart == 1) || (fmt - fmtstart == 2 && strip) || *fmt == '.')) {
+ while (*fmt && strchr("OE^#_-0123456789", *fmt))
+ fmt++;
+ if (*fmt) {
+ fmt++;
+ goto strftimehandling;
+ }
+ }
+#endif
switch (*fmt++) {
case '.':
if (ztrftimebuf(&bufsize, digs))
@@ -2938,10 +2972,10 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
sprintf(buf, "%0*ld", digs, usec);
buf += digs;
break;
- case 'd':
- if (tm->tm_mday > 9 || !strip)
- *buf++ = '0' + tm->tm_mday / 10;
- *buf++ = '0' + tm->tm_mday % 10;
+ case '\0':
+ /* Guard against premature end of string */
+ *buf++ = '%';
+ fmt--;
break;
case 'f':
strip = 1;
@@ -2982,6 +3016,11 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
*buf++ = '0' + (hr12 % 10);
break;
+ case 'd':
+ if (tm->tm_mday > 9 || !strip)
+ *buf++ = '0' + tm->tm_mday / 10;
+ *buf++ = '0' + tm->tm_mday % 10;
+ break;
case 'm':
if (tm->tm_mon > 8 || !strip)
*buf++ = '0' + (tm->tm_mon + 1) / 10;
@@ -3002,18 +3041,9 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
*buf++ = '0' + (tm->tm_year / 10) % 10;
*buf++ = '0' + tm->tm_year % 10;
break;
- case '\0':
- /* Guard against premature end of string */
- *buf++ = '%';
- fmt--;
- break;
#ifndef HAVE_STRFTIME
case 'Y':
{
- /*
- * Not worth handling this natively if
- * strftime has it.
- */
int year, digits, testyear;
year = tm->tm_year + 1900;
digits = 1;
@@ -3047,24 +3077,51 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
if (fmt[-1] != '%')
*buf++ = fmt[-1];
#else
+ case 'E':
+ case 'O':
+ case '^':
+ case '#':
+ case '_':
+ case '-':
+ case '0' ... '9':
+ goto morefmt;
+strftimehandling:
default:
/*
* Remember we've already allowed for two characters
* in the accounting in bufsize (but nowhere else).
*/
- *buf = '\1';
- sprintf(tmp, strip ? "%%-%c" : "%%%c", fmt[-1]);
- if (!strftime(buf, bufsize + 2, tmp, tm))
{
- if (*buf) {
- buf[0] = '\0';
- return -1;
+ int size = fmt - fmtstart;
+ char *tmp, *last;
+ tmp = zhalloc(size + 1);
+ strncpy(tmp, fmtstart, size);
+ last = fmt-1;
+ if (*last == Meta) {
+ /*
+ * This is for consistency in counting:
+ * a metafiable character isn't actually
+ * a valid strftime descriptor.
+ *
+ * Previous characters were explicitly checked,
+ * so can't be metafied.
+ */
+ *last = *++fmt ^ 32;
+ }
+ tmp[size] = '\0';
+ *buf = '\1';
+ if (!strftime(buf, bufsize + 2, tmp, tm))
+ {
+ if (*buf) {
+ buf[0] = '\0';
+ return -1;
+ }
+ return 0;
}
- return 0;
+ decr = strlen(buf);
+ buf += decr;
+ bufsize -= decr - 2;
}
- decr = strlen(buf);
- buf += decr;
- bufsize -= decr - 2;
#endif
break;
}
@@ -3073,6 +3130,7 @@ ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
return -1;
*buf++ = *fmt++;
}
+ }
*buf = '\0';
return buf - origbuf;
}
@@ -3555,7 +3613,7 @@ zbeep(void)
{
char *vb;
queue_signals();
- if ((vb = getsparam("ZBEEP"))) {
+ if ((vb = getsparam_u("ZBEEP"))) {
int len;
vb = getkeystring(vb, &len, GETKEYS_BINDKEY, NULL);
write_loop(SHTTY, vb, len);
@@ -3832,7 +3890,7 @@ itype_end(const char *ptr, int itype, int once)
#ifdef MULTIBYTE_SUPPORT
if (isset(MULTIBYTE) &&
(itype != IIDENT || !isset(POSIXIDENTIFIERS))) {
- mb_metacharinit();
+ mb_charinit();
while (*ptr) {
wint_t wc;
int len = mb_metacharlenconv(ptr, &wc);
@@ -4367,7 +4425,10 @@ unmeta(const char *file_name)
char *p;
const char *t;
int newsz, meta;
-
+
+ if (!file_name)
+ return NULL;
+
meta = 0;
for (t = file_name; *t; t++) {
if (*t == Meta)
@@ -4471,9 +4532,37 @@ ztrlen(char const *s)
for (l = 0; *s; l++) {
if (*s++ == Meta) {
#ifdef DEBUG
- if (! *s)
+ if (! *s) {
fprintf(stderr, "BUG: unexpected end of string in ztrlen()\n");
- else
+ break;
+ } else
+#endif
+ s++;
+ }
+ }
+ return l;
+}
+
+#ifndef MULTIBYTE_SUPPORT
+/*
+ * ztrlen() but with explicit end point for non-null-terminated
+ * segments. eptr may not be NULL.
+ */
+
+/**/
+mod_export int
+ztrlenend(char const *s, char const *eptr)
+{
+ int l;
+
+ for (l = 0; s < eptr; l++) {
+ if (*s++ == Meta) {
+#ifdef DEBUG
+ if (! *s) {
+ fprintf(stderr,
+ "BUG: unexpected end of string in ztrlenend()\n");
+ break;
+ } else
#endif
s++;
}
@@ -4481,6 +4570,8 @@ ztrlen(char const *s)
return l;
}
+#endif /* MULTIBYTE_SUPPORT */
+
/* Subtract two pointers in a metafied string. */
/**/
@@ -4879,11 +4970,16 @@ mb_metacharlenconv(const char *s, wint_t *wcp)
* If width is 1, return total character width rather than number.
* If width is greater than 1, return 1 if character has non-zero width,
* else 0.
+ *
+ * Ends if either *ptr is '\0', the normal case (eptr may be NULL for
+ * this), or ptr is eptr (i.e. *eptr is where the null would be if null
+ * terminated) for strings not delimited by nulls --- note these are
+ * still metafied.
*/
/**/
mod_export int
-mb_metastrlen(char *ptr, int width)
+mb_metastrlenend(char *ptr, int width, char *eptr)
{
char inchar, *laststart;
size_t ret;
@@ -4898,7 +4994,7 @@ mb_metastrlen(char *ptr, int width)
num = num_in_char = 0;
memset(&mb_shiftstate, 0, sizeof(mb_shiftstate));
- while (*ptr) {
+ while (*ptr && !(eptr && ptr >= eptr)) {
if (*ptr == Meta)
inchar = *++ptr ^ 32;
else
@@ -4937,6 +5033,65 @@ mb_metastrlen(char *ptr, int width)
return num + num_in_char;
}
+/*
+ * The equivalent of mb_metacharlenconv_r() for
+ * strings that aren't metafied and hence have
+ * explicit lengths.
+ */
+
+/**/
+mod_export int
+mb_charlenconv_r(const char *s, int slen, wint_t *wcp, mbstate_t *mbsp)
+{
+ size_t ret = MB_INVALID;
+ char inchar;
+ const char *ptr;
+ wchar_t wc;
+
+ for (ptr = s; slen; ) {
+ inchar = *ptr;
+ ptr++;
+ slen--;
+ ret = mbrtowc(&wc, &inchar, 1, mbsp);
+
+ if (ret == MB_INVALID)
+ break;
+ if (ret == MB_INCOMPLETE)
+ continue;
+ if (wcp)
+ *wcp = wc;
+ return ptr - s;
+ }
+
+ if (wcp)
+ *wcp = WEOF;
+ /* No valid multibyte sequence */
+ memset(mbsp, 0, sizeof(*mbsp));
+ if (ptr > s) {
+ return 1; /* Treat as single byte character */
+ } else
+ return 0; /* Probably shouldn't happen */
+}
+
+/*
+ * The equivalent of mb_metacharlenconv() for
+ * strings that aren't metafied and hence have
+ * explicit lengths;
+ */
+
+/**/
+mod_export int
+mb_charlenconv(const char *s, int slen, wint_t *wcp)
+{
+ if (!isset(MULTIBYTE)) {
+ if (wcp)
+ *wcp = (wint_t)*s;
+ return 1;
+ }
+
+ return mb_charlenconv_r(s, slen, wcp, &mb_shiftstate);
+}
+
/**/
#else
@@ -4961,8 +5116,127 @@ metacharlenconv(const char *x, int *c)
return 1;
}
+/* Simple replacement for mb_charlenconv */
+
/**/
+mod_export int
+charlenconv(const char *x, int len, int *c)
+{
+ if (!len) {
+ if (c)
+ *c = '\0';
+ return 0;
+ }
+
+ if (c)
+ *c = (char)*x;
+ return 1;
+}
+
+/**/
+#endif /* MULTIBYTE_SUPPORT */
+
+/*
+ * Expand tabs to given width, with given starting position on line.
+ * len is length of unmetafied string in bytes.
+ * Output to fout.
+ * Return the end position on the line, i.e. if this is 0 modulo width
+ * the next character is aligned with a tab stop.
+ *
+ * If all is set, all tabs are expanded, else only leading tabs.
+ */
+
+/**/
+mod_export int
+zexpandtabs(const char *s, int len, int width, int startpos, FILE *fout,
+ int all)
+{
+ int at_start = 1;
+
+#ifdef MULTIBYTE_SUPPORT
+ mbstate_t mbs;
+ size_t ret;
+ wchar_t wc;
+
+ memset(&mbs, 0, sizeof(mbs));
+#endif
+
+ while (len) {
+ if (*s == '\t') {
+ if (all || at_start) {
+ s++;
+ len--;
+ if (width <= 0 || !(startpos % width)) {
+ /* always output at least one space */
+ fputc(' ', fout);
+ startpos++;
+ }
+ if (width <= 0)
+ continue; /* paranoia */
+ while (startpos % width) {
+ fputc(' ', fout);
+ startpos++;
+ }
+ } else {
+ /*
+ * Leave tab alone.
+ * Guess width to apply... we might get this wrong.
+ * This is only needed if there's a following string
+ * that needs tabs expanding, which is unusual.
+ */
+ startpos += width - startpos % width;
+ s++;
+ len--;
+ fputc('\t', fout);
+ }
+ continue;
+ } else if (*s == '\n' || *s == '\r') {
+ fputc(*s, fout);
+ s++;
+ len--;
+ startpos = 0;
+ at_start = 1;
+ continue;
+ }
+
+ at_start = 0;
+#ifdef MULTIBYTE_SUPPORT
+ if (isset(MULTIBYTE)) {
+ const char *sstart = s;
+ ret = mbrtowc(&wc, s, len, &mbs);
+ if (ret == MB_INVALID) {
+ /* Assume single character per character */
+ memset(&mbs, 0, sizeof(mbs));
+ s++;
+ len--;
+ } else if (ret == MB_INCOMPLETE) {
+ /* incomplete at end --- assume likewise, best we've got */
+ s++;
+ len--;
+ } else {
+ s += ret;
+ len -= (int)ret;
+ }
+ if (ret == MB_INVALID || ret == MB_INCOMPLETE) {
+ startpos++;
+ } else {
+ int wcw = WCWIDTH(wc);
+ if (wcw > 0) /* paranoia */
+ startpos += wcw;
+ }
+ fwrite(sstart, s - sstart, 1, fout);
+
+ continue;
+ }
#endif /* MULTIBYTE_SUPPORT */
+ fputc(*s, fout);
+ s++;
+ len--;
+ startpos++;
+ }
+
+ return startpos;
+}
/* check for special characters in the string */
@@ -5301,7 +5575,25 @@ quotestring(const char *s, char **e, int instring)
/* Needs to be passed straight through. */
if (dobackslash)
*v++ = '\\';
- *v++ = *u++;
+ if (*u == Inparmath) {
+ /*
+ * Already syntactically quoted: don't
+ * add more.
+ */
+ int inmath = 1;
+ *v++ = *u++;
+ for (;;) {
+ char uc = *u;
+ *v++ = *u++;
+ if (uc == '\0')
+ break;
+ else if (uc == Outparmath && !--inmath)
+ break;
+ else if (uc == Inparmath)
+ ++inmath;
+ }
+ } else
+ *v++ = *u++;
continue;
}
@@ -6148,10 +6440,15 @@ init_dirsav(Dirsav d)
d->dirfd = d->level = -1;
}
-/* Change directory, without following symlinks. Returns 0 on success, -1 *
- * on failure. Sets errno to ENOTDIR if any symlinks are encountered. If *
- * fchdir() fails, or the current directory is unreadable, we might end up *
- * in an unwanted directory in case of failure. */
+/*
+ * Change directory, without following symlinks. Returns 0 on success, -1
+ * on failure. Sets errno to ENOTDIR if any symlinks are encountered. If
+ * fchdir() fails, or the current directory is unreadable, we might end up
+ * in an unwanted directory in case of failure.
+ *
+ * path is an unmetafied but null-terminated string, as needed by system
+ * calls.
+ */
/**/
mod_export int