Suggest removing ABOUT-NLS from the repository; it's automatically
copied and I keep having to edit it out of my diffs (since my ABOUT-NLS
is different than yours.)
The C++ test is in this, too (cleaned up slightly), since you havn't
applied that yet.
Added mbswidth.c and mbswidth.h; same as wcswidth, for multibyte
strings. (From fileutils.) This drops back when wcwidth(), etc, aren't
available.
Added mbrtowc.m4, mbstate_t.m4, mbswidth.m4; support tests for mbswidth.c.
Column output now treats width properly (using mbswidth().)
Date output added to cls -l; uses locale-dependant date (like ls does.)
Added xasprintf and xvasprintf. (Trivial asprintf/vasprintf
implementation so we don't need to deal with trio just for those.)
Added some ColumnOutput support for date output (addf()).
Tested this in ja_JP.UTF-8.
We can't really support different encodings over FTP, of course (unless
there's some extension for servers to specify a locale; I'm not aware of
one.) We could conceivably let the user specify what encoding the server
is in, and iconv() it to the local one; this would be useful for the
occasional mp3 server with SJIS filenames (I'm in UTF-8). The tricky part
is conversion: we can't convert filenames to display, then convert user
commands back, and expect it to line up. I don't have a good solution
right now. (I don't *need* it right now, so I'm not going to give
myself a headache trying to think one up.)
The current cls column display does format correctly for wide characters
(ie. Japanese) if the encoding is the same. squeeze_file_name won't; it
should use mbswidth() to determine width, and make sure it doesn't split
in the middle of a MB character. (The former's easy to do, while
supporting older systems; the latter, I think, requires conversion to
wchar_t, which is a little harder.)
--
Glenn Maynard
#serial 2
dnl From Paul Eggert
AC_DEFUN(jm_FUNC_MBRTOWC,
[
AC_CACHE_CHECK([whether mbrtowc and mbstate_t are properly declared],
jm_cv_func_mbrtowc,
[AC_TRY_LINK(
[@%:@include <wchar.h>],
[mbstate_t state; return ! (sizeof state && mbrtowc);],
jm_cv_func_mbrtowc=yes,
jm_cv_func_mbrtowc=no)])
if test $jm_cv_func_mbrtowc = yes; then
AC_DEFINE(HAVE_MBRTOWC, 1,
[Define to 1 if mbrtowc and mbstate_t are properly declared.])
fi
])
# serial 8
# From Paul Eggert.
# BeOS 5 has <wchar.h> but does not define mbstate_t,
# so you can't declare an object of that type.
# Check for this incompatibility with Standard C.
# Include stdlib.h first, because otherwise this test would fail on Linux
# (at least glibc-2.1.3) because the "_XOPEN_SOURCE 500" definition elicits
# a syntax error in wchar.h due to the use of undefined __int32_t.
AC_DEFUN(AC_MBSTATE_T,
[
AC_CHECK_HEADERS(stdlib.h)
AC_CACHE_CHECK([for mbstate_t], ac_cv_type_mbstate_t,
[AC_TRY_COMPILE([
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#include <wchar.h>],
[mbstate_t x; return sizeof x;],
ac_cv_type_mbstate_t=yes,
ac_cv_type_mbstate_t=no)])
if test $ac_cv_type_mbstate_t = no; then
AC_DEFINE(mbstate_t, int,
[Define to a type if <wchar.h> does not define.])
fi])
#serial 4
dnl autoconf tests required for use of mbswidth.c
dnl From Bruno Haible.
AC_DEFUN(jm_PREREQ_MBSWIDTH,
[
AC_REQUIRE([AC_HEADER_STDC])
AC_REQUIRE([AM_C_PROTOTYPES])
AC_CHECK_HEADERS(limits.h stdlib.h string.h wchar.h wctype.h)
AC_CHECK_FUNCS(isascii iswprint wcwidth)
jm_FUNC_MBRTOWC
headers='
# if HAVE_WCHAR_H
# include <wchar.h>
# endif
'
AC_CHECK_DECLS([wcwidth], , , $headers)
AC_MBSTATE_T
])
/* Determine the number of screen columns needed for a string.
Copyright (C) 2000 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/* Written by Bruno Haible <[EMAIL PROTECTED]>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
/* Get MB_LEN_MAX. */
#if HAVE_LIMITS_H
# include <limits.h>
#endif
/* Get MB_CUR_MAX. */
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#if HAVE_STRING_H
# include <string.h>
#endif
/* Get isprint(). */
#include <ctype.h>
/* Get mbstate_t, mbrtowc(), mbsinit(), wcwidth(). */
#if HAVE_WCHAR_H
# include <wchar.h>
#endif
/* Get iswprint(). */
#if HAVE_WCTYPE_H
# include <wctype.h>
#endif
#if !defined iswprint && !HAVE_ISWPRINT
# define iswprint(wc) 1
#endif
#ifndef HAVE_DECL_WCWIDTH
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_WCWIDTH
int wcwidth ();
#endif
#ifndef wcwidth
# if !HAVE_WCWIDTH
/* wcwidth doesn't exist, so assume all printable characters have
width 1. */
# define wcwidth(wc) ((wc) == 0 ? 0 : iswprint (wc) ? 1 : -1)
# endif
#endif
/* Get ISPRINT. */
#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
# define IN_CTYPE_DOMAIN(c) 1
#else
# define IN_CTYPE_DOMAIN(c) isascii(c)
#endif
/* Undefine to protect against the definition in wctype.h of solaris2.6. */
#undef ISPRINT
#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c))
#include "mbswidth.h"
/* Returns the number of columns needed to represent the multibyte
character string pointed to by STRING. If a non-printable character
occurs, -1 is returned, unless MBSW_ACCEPT_UNPRINTABLE is specified.
With flags = 0, this is the multibyte analogon of the wcswidth function. */
int
mbswidth (const char *string, int flags)
{
return mbsnwidth (string, strlen (string), flags);
}
/* Returns the number of columns needed to represent the multibyte
character string pointed to by STRING of length NBYTES. If a
non-printable character occurs, -1 is returned, unless
MBSW_ACCEPT_UNPRINTABLE is specified. */
int
mbsnwidth (const char *string, size_t nbytes, int flags)
{
const char *p = string;
const char *plimit = p + nbytes;
int width;
width = 0;
#if HAVE_MBRTOWC && (MB_LEN_MAX > 1)
if (MB_CUR_MAX > 1)
{
while (p < plimit)
switch (*p)
{
case ' ': case '!': case '"': case '#': case '%':
case '&': case '\'': case '(': case ')': case '*':
case '+': case ',': case '-': case '.': case '/':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case ':': case ';': case '<': case '=': case '>':
case '?':
case 'A': case 'B': case 'C': case 'D': case 'E':
case 'F': case 'G': case 'H': case 'I': case 'J':
case 'K': case 'L': case 'M': case 'N': case 'O':
case 'P': case 'Q': case 'R': case 'S': case 'T':
case 'U': case 'V': case 'W': case 'X': case 'Y':
case 'Z':
case '[': case '\\': case ']': case '^': case '_':
case 'a': case 'b': case 'c': case 'd': case 'e':
case 'f': case 'g': case 'h': case 'i': case 'j':
case 'k': case 'l': case 'm': case 'n': case 'o':
case 'p': case 'q': case 'r': case 's': case 't':
case 'u': case 'v': case 'w': case 'x': case 'y':
case 'z': case '{': case '|': case '}': case '~':
/* These characters are printable ASCII characters. */
p++;
width++;
break;
default:
/* If we have a multibyte sequence, scan it up to its end. */
{
mbstate_t mbstate;
memset (&mbstate, 0, sizeof mbstate);
do
{
wchar_t wc;
size_t bytes;
int w;
bytes = mbrtowc (&wc, p, plimit - p, &mbstate);
if (bytes == (size_t) -1)
/* An invalid multibyte sequence was encountered. */
{
if (flags & MBSW_ACCEPT_INVALID)
{
p++;
width++;
break;
}
else
return -1;
}
if (bytes == (size_t) -2)
/* An incomplete multibyte character at the end. */
{
if (flags & MBSW_ACCEPT_INVALID)
{
p = plimit;
width++;
break;
}
else
return -1;
}
if (bytes == 0)
/* A null wide character was encountered. */
bytes = 1;
w = wcwidth (wc);
if (w >= 0)
/* A printable multibyte character. */
width += w;
else
/* An unprintable multibyte character. */
if (flags & MBSW_ACCEPT_UNPRINTABLE)
width += 1;
else
return -1;
p += bytes;
}
while (! mbsinit (&mbstate));
}
break;
}
return width;
}
#endif
while (p < plimit)
{
unsigned char c = (unsigned char) *p++;
if ((flags & MBSW_ACCEPT_UNPRINTABLE) || ISPRINT (c))
width++;
else
return -1;
}
return width;
}
/* Determine the number of screen columns needed for a string.
Copyright (C) 2000-2001 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#ifdef __cplusplus
#define EXT extern "C"
#else
#define EXT
#endif
#ifndef PARAMS
# if defined PROTOTYPES || defined __STDC__
# define PARAMS(Args) Args
# else
# define PARAMS(Args) ()
# endif
#endif
/* Optional flags to influence mbswidth/mbsnwidth behavior. */
/* If this bit is set, assume invalid characters have width 0.
Otherwise, return -1 upon finding an invalid or incomplete character. */
#define MBSW_ACCEPT_INVALID 1
/* If this bit is set, assume unprintable characters have width 1.
Otherwise, return -1 upon finding a non-printable character. */
#define MBSW_ACCEPT_UNPRINTABLE 2
/* Returns the number of screen columns needed for STRING. */
#define mbswidth gnu_mbswidth /* avoid clash with UnixWare 7.1.1 function */
EXT int mbswidth PARAMS ((const char *string, int flags));
/* Returns the number of screen columns needed for the NBYTES bytes
starting at BUF. */
EXT int mbsnwidth PARAMS ((const char *buf, size_t nbytes, int flags));
Index: configure.in
===================================================================
RCS file: /home/lav/cvsroot/lftp/configure.in,v
retrieving revision 1.105
diff -u -r1.105 configure.in
--- configure.in 2001/09/13 15:37:33 1.105
+++ configure.in 2001/10/15 00:54:59
@@ -20,8 +20,17 @@
AC_PROG_CC
AC_PROG_CXX
+dnl Make sure C++ build environment is sane.
+LFTP_CXX_TEST
+
dnl AC_CANONICAL_HOST
AC_ISC_POSIX
+
+# for lib/mbswidth.{c,h}
+jm_PREREQ_MBSWIDTH
+
+jm_FUNC_MBRTOWC
+AC_MBSTATE_T
if test "`uname`" = Linux; then
case "`uname -r`" in
Index: include/Makefile.am
===================================================================
RCS file: /home/lav/cvsroot/lftp/include/Makefile.am,v
retrieving revision 1.5
diff -u -r1.5 Makefile.am
--- include/Makefile.am 2001/10/03 08:14:14 1.5
+++ include/Makefile.am 2001/10/15 00:54:59
@@ -1,3 +1,3 @@
TRIO_FILES=strio.h trio.h triop.h trionan.h triodef.h
-noinst_HEADERS = xalloca.h getopt.h xstring.h filemode.h $(TRIO_FILES)
+noinst_HEADERS = xalloca.h getopt.h xstring.h filemode.h mbswidth.h $(TRIO_FILES)
DISTCLEANFILES = poll.h regex.h glob.h fnmatch.h readline libintl.h
Index: lib/Makefile.am
===================================================================
RCS file: /home/lav/cvsroot/lftp/lib/Makefile.am,v
retrieving revision 1.3
diff -u -r1.3 Makefile.am
--- lib/Makefile.am 2001/10/03 08:14:04 1.3
+++ lib/Makefile.am 2001/10/15 00:54:59
@@ -1,5 +1,5 @@
noinst_LIBRARIES = liblib.a
-liblib_a_SOURCES = poll.h glob.h fnmatch.h regex.h filemode.c
+liblib_a_SOURCES = poll.h glob.h fnmatch.h regex.h filemode.c mbswidth.c
liblib_a_LIBADD = @LIBOBJS@ @ALLOCA@
INCLUDES = -I$(top_srcdir)/include
EXTRA_DIST = fnmatch_loop.c
Index: m4/lftp.m4
===================================================================
RCS file: /home/lav/cvsroot/lftp/m4/lftp.m4,v
retrieving revision 1.2
diff -u -r1.2 lftp.m4
--- m4/lftp.m4 2001/10/11 07:54:07 1.2
+++ m4/lftp.m4 2001/10/15 00:54:59
@@ -48,11 +48,9 @@
AC_LANG_CPLUSPLUS
AC_MSG_CHECKING(if c++ compiler works)
AC_TRY_RUN([int main() { return(0); } ],
- [cxx_sane=yes], [cxx_sane=no], [cxx_sane=yes])
- AC_MSG_RESULT($cxx_sane)
- if test x$cxx_sane = xno; then
- AC_MSG_ERROR(C++ test compile failed; check your C++ compiler)
- fi
+ [AC_MSG_RESULT(yes)], [
+ AC_MSG_RESULT(no)
+ AC_MSG_ERROR(C++ test compile failed; check your C++ compiler)],
+[AC_MSG_RESULT(cross-compiling)])
AC_LANG_RESTORE
])
Index: src/ColumnOutput.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/ColumnOutput.cc,v
retrieving revision 1.4
diff -u -r1.4 ColumnOutput.cc
--- src/ColumnOutput.cc 2001/10/08 05:50:54 1.4
+++ src/ColumnOutput.cc 2001/10/15 00:54:59
@@ -28,6 +28,8 @@
#include <unistd.h>
#endif
+#include <mbswidth.h>
+
#include "SMTask.h"
#include "ColumnOutput.h"
#include "ResMgr.h"
@@ -56,6 +58,15 @@
lst[lst_cnt-1]->append(name, color);
}
+void ColumnOutput::addf(const char *fmt, const char *color, ...)
+{
+ va_list v;
+ va_start(v, color);
+ char *str = xvasprintf(fmt, v);
+ va_end(v);
+ add(str, color);
+}
+
void ColumnOutput::append()
{
if(lst_cnt >= lst_alloc) {
@@ -225,8 +236,7 @@
}
}
- /* XXX: UTF8 */
- curwidth += strlen(name);
+ curwidth += mbswidth(name, MBSW_ACCEPT_INVALID|MBSW_ACCEPT_UNPRINTABLE);
}
void datum::print(Buffer *o, bool color, int skip,
Index: src/ColumnOutput.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/ColumnOutput.h,v
retrieving revision 1.1
diff -u -r1.1 ColumnOutput.h
--- src/ColumnOutput.h 2001/10/05 06:26:50 1.1
+++ src/ColumnOutput.h 2001/10/15 00:54:59
@@ -56,6 +56,7 @@
void append();
void add(const char *name, const char *color);
+ void addf(const char *fmt, const char *color, ...);
void SetWidth(unsigned width);
void SetColor(bool color);
Index: src/FileSetOutput.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/FileSetOutput.cc,v
retrieving revision 1.2
diff -u -r1.2 FileSetOutput.cc
--- src/FileSetOutput.cc 2001/10/05 06:43:31 1.2
+++ src/FileSetOutput.cc 2001/10/15 00:54:59
@@ -31,7 +31,9 @@
#include <unistd.h>
#include <sys/stat.h>
#include <assert.h>
+#include <locale.h>
+#include <mbswidth.h>
#include "misc.h"
#include "ResMgr.h"
@@ -416,6 +418,43 @@
sprintf(sz, "%10s ", ""); /* pad */
}
c.add(sz, "");
+ }
+
+ /* We use unprec dates; doing MDTMs for each file in ls is far too
+ * slow. If someone actually wants that (to get dates on servers with
+ * unparsable dates, or more accurate dates), it wouldn't be
+ * difficult. If we did this, we could also support --full-time. */
+ if(mode & DATE) {
+ /* Consider a time to be recent if it is within the past six
+ * months. A Gregorian year has 365.2425 * 24 * 60 * 60 ==
+ * 31556952 seconds on the average. Write this value as an
+ * integer constant to avoid floating point hassles. */
+ const int six_months_ago = SMTask::now - 31556952 / 2;
+ bool recent = six_months_ago <= f->date;
+
+ /* We assume all time outputs are equal-width. */
+ static const char *long_time_format[] = {
+ dcgettext (NULL, "%b %e %Y", LC_TIME),
+ dcgettext (NULL, "%b %e %H:%M", LC_TIME)
+ };
+
+ const char *fmt = long_time_format[recent];
+ struct tm *when_local;
+ char *dt;
+ if ((f->defined&FileInfo::DATE_UNPREC) && (when_local = localtime
+(&f->date))) {
+ dt = xstrftime(fmt, when_local);
+ } else {
+ /* put an empty field; make sure it's the same width */
+ dt = xstrftime(long_time_format[0], NULL);
+ int wid = mbswidth(dt, MBSW_ACCEPT_INVALID|MBSW_ACCEPT_UNPRINTABLE);
+ xfree(dt);
+
+ dt = (char *) xmalloc(wid+1);
+ memset(dt, ' ', wid);
+ dt[wid] = 0;
+ }
+ c.addf("%s ", "", dt);
+ xfree(dt);
}
const char *nm = f->name;
Index: src/PtyShell.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/PtyShell.cc,v
retrieving revision 1.2
diff -u -r1.2 PtyShell.cc
--- src/PtyShell.cc 2001/10/12 10:48:04 1.2
+++ src/PtyShell.cc 2001/10/15 00:55:00
@@ -104,9 +104,9 @@
_exit(1);
}
}
- putenv("LC_ALL=C");
- putenv("LANG=C");
- putenv("LANGUAGE=C");
+ putenv(xstrdup("LC_ALL=C"));
+ putenv(xstrdup("LANG=C"));
+ putenv(xstrdup("LANGUAGE=C"));
if(a)
execvp(a->a0(),a->GetV());
execl("/bin/sh","sh","-c",name,NULL);
Index: src/misc.cc
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/misc.cc,v
retrieving revision 1.30
diff -u -r1.30 misc.cc
--- src/misc.cc 2001/10/12 07:05:44 1.30
+++ src/misc.cc 2001/10/15 00:55:00
@@ -21,9 +21,14 @@
/* $Id: misc.cc,v 1.30 2001/10/12 07:05:44 lav Exp $ */
#include <config.h>
+#ifdef NEED_TRIO
+#include "trio.h"
+#define vsnprintf trio_vsnprintf
+#endif
#include "xmalloc.h"
#include "xstring.h"
#include <stdio.h>
+#include <stdarg.h>
#include <pwd.h>
#include <unistd.h>
#include <errno.h>
@@ -614,3 +619,44 @@
if(usec) *usec = 0;
#endif
}
+
+char *xstrftime(const char *format, const struct tm *tm)
+{
+ char *ret = NULL;
+ int siz = 128;
+
+ struct tm dummy = { 0,0,0,0,0,0,0,0,0 };
+ if(tm == NULL) tm = &dummy;
+
+ while(1) {
+ ret = (char *) xrealloc(ret, siz);
+ if(strftime(ret, siz, format, tm) != 0)
+ return ret; /* success */
+
+ /* more space */
+ siz += siz / 2;
+ }
+}
+
+char *xvasprintf(const char *format, va_list ap)
+{
+ char *ret = NULL;
+ int siz = 128;
+
+ while(1) {
+ ret = (char *) xrealloc(ret, siz);
+ if(vsnprintf(ret, siz, format, ap) != -1) return ret;
+ siz += siz / 2;
+ }
+}
+
+char *xasprintf(const char *format, ...)
+{
+ char *ret;
+ va_list va;
+ va_start(va, format);
+ ret = xvasprintf(format, va);
+ va_end(&va);
+ return ret;
+}
+
Index: src/misc.h
===================================================================
RCS file: /home/lav/cvsroot/lftp/src/misc.h,v
retrieving revision 1.21
diff -u -r1.21 misc.h
--- src/misc.h 2001/10/09 12:48:17 1.21
+++ src/misc.h 2001/10/15 00:55:00
@@ -100,4 +100,11 @@
/* uses gettimeofday if available */
void xgettimeofday(time_t *sec, int *usec);
+/* returns malloc'd date */
+char *xstrftime(const char *format, const struct tm *tm);
+
+/* returns malloc'd formatted string */
+char *xvasprintf(const char *format, va_list ap);
+char *xasprintf(const char *format, ...);
+
#endif // MISC_H