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

Reply via email to