Commit bfdac003 authored by J.H.M. Dassen's avatar J.H.M. Dassen Committed by Niels Möller

Added Ray\'s patches. Removed the broken gc_register_global function

Rev: AUTHORS:1.5
Rev: ChangeLog:1.26
Rev: README:1.3
Rev: doc/HACKING:1.7
Rev: doc/NOTES:1.5
Rev: doc/TASKLIST:1.5
Rev: doc/TODO:1.28
Rev: src/Makefile.am.in:1.21
Rev: src/acconfig.h:1.6
Rev: src/client.c:1.47
Rev: src/configure.in:1.21
Rev: src/digit_table.c:1.3
Rev: src/gc.c:1.5
Rev: src/gc.h:1.4
Rev: src/io.c:1.41
Rev: src/lsh.c:1.35
Rev: src/lsh.h:1.2
Rev: src/lshd.c:1.30
Rev: src/resource.h:1.3
Rev: src/server.c:1.38
Rev: src/symmetric/Makefile.am.in:1.7
Rev: src/symmetric/include/Makefile.am.in:1.3
Rev: src/version.h:1.4
Rev: src/werror.c:1.18
parent fa7f41b5
......@@ -3,7 +3,7 @@ conditions of the GNU General Public License (see the file COPYING for
details). But many other people have written free code which is used
in lsh.
BLOWFISH implementetion from Werner Koch's and FSF's GNU Privacy
BLOWFISH implementation from Werner Koch's and FSF's GNU Privacy
Guard. Released under the GPL.
CAST implementation by Steve Reid. Released into the public domain.
......@@ -29,3 +29,6 @@ domain.
POLL emulation code (for systems that have select(), but not poll())
written by Sean Reifschneider, released for unlimited use.
SEXP parsing code have some parts in common with Ron Rivest's parser.
Distributed under the GPL with permission.
Sun Jan 3 16:54:29 1999 <ray@zensunni>
* digit_table.c: Fixed declaration of main.
* Makefile.am.in: remove .x files as part of maintainer-clean.
* Makefile.am.in, lib/Makefile.am.in, include/Makefile.am.in: added
support for ctags.
* configure.in: Remove superfluous spacing in compiler warnings.
* version.h, client.c (client_initiate), server.c (server_initiate):
Split PROTOCOL_VERSION in CLIENT_PROTOCOL_VERSION and
SERVER_PROTOCOL_VERSION, so the client won't report version 1.99 .
* configure.in: Look for SSH1 daemon as plain `sshd' too. Test that it
is usable.
Sat Jan 2 18:25:16 1999 <ray@zensunni>
* TODO: Added logging; Noted we can't currently use Rivest's sexp code.
* resource.h, lsh_types.h: fixed typo.
* Makefile.am.in, lib/Makefile.am.in: Added pattern rule for
dependencies; added phony target `depend' to force dependencies to be
remade; remove dependency files (.P) as part of maintainer-clean.
Fri Jan 1 18:08:03 1999 <ray@zensunni>
* acconfig.h, configure.in, server.c, version.h: Added skeleton for
SSH1 fallback support.
* TASKLIST: Added SSH1 fallback support.
* configure.in: Rewrote the comment about _GNU_SOURCE .
* AUTHORS, HACKING, NOTES, README, TODO: spelling, grammar fixes.
* TASKLIST: We have zlib support now.
Mon Jan 4 11:25:43 1999 <nisse@puck>
* werror.c: Avoid using stdio functions.
......
LSH - a free implementetion of the Secure Shell protocols.
LSH - a free implementation of the Secure Shell protocols.
LSH IS WORK IN PROGRESS. DON'T EXPECT THE CURRENT VERSION TO WORK, AND
......@@ -20,7 +20,7 @@ be found at your local GNU mirror site. scsh, Olin Shiver's Scheme
Shell, can be downloaded from <URL:
ftp://ftp-swiss.ai.mit.edu/pub/su/scsh/scsh.tar.gz>.
If you have checked out lsh from cvs, things are more complicated. You
If you have checked out lsh from CVS, things are more complicated. You
must first generate Makefile.am files from the corresponding
Makefile.am.in files. Do this by running ./make_am in the top level
directory. Next, you need to run autoconf, autoheader and automake.
......
......@@ -3,7 +3,7 @@ A Hacker's Guide to LSH
This document contains some notes, which I hope will make it easier
for you to understand and hack lsh. It is divided into four main
sections: Abstraction, Object system, Memory allocation, and a Source
roadmap.
road map.
ABSTRACTION
......@@ -125,7 +125,7 @@ OO-language. In fact, it's primary purpose is not to help with object
orientation (I could do that fairly well with plain C before
introducing the class language), but to provide the information needed
for garbage collection. The syntax is scheme s-expressions, and the
files are preprocessed by make_class, a scheme schell script a few
files are preprocessed by make_class, a scheme shell script a few
hundred lines long.
Classes are written inside C comments. For each definition the source
......@@ -198,7 +198,7 @@ sure that it is deallocated when *all* pointers to it are gone.
The method keyword defines a method, implemented as an instance
variable holding a function pointer. Keeping the pointer in an
instance variable rather than in the class is flexible; it's easy to
ocerride it in a subclass or even in a single object. So these methods
override it in a subclass or even in a single object. So these methods
are as virtual as one can get.
It is possible to use the meta-feature to place method pointers in the
......@@ -288,7 +288,7 @@ are no longer needed.
× Other objects and closures, which references each other in some
complex fashion. Except places where it is *obvious* that an object
can be freed, these objects are not freed explicitly, but are handled
by the garbage collector. The gc overhead should be farily small;
by the garbage collector. The gc overhead should be fairly small;
almost all allocated memory are strings, which *are* freed explicitly
when they are no longer used. The objects handled by the gc things
like pipes of write handlers, keyexchange state objects, etc, which
......@@ -298,7 +298,7 @@ Objects are allocated using the NEW() macro. Objects that won't be
needed anymore can be deleted explicitly by using the KILL() macro.
For the gc to work properly, it is important that there be no bogus or
uninititialized pointers. Pointers should either be NULL, or point at
uninitialized pointers. Pointers should either be NULL, or point at
some valid data, and all bignums should be initialized. Note that this
rule applies to all objects, including those KILL()ed explicitly.
......
......@@ -35,7 +35,7 @@ can map PAM's messages to the appropriate SSH packets. This problem
excludes using PAM for password authentication.
(iii) The PAM conversation function expects the server to ask the user
some question, block until a response is recieved, and then return the
some question, block until a response is received, and then return the
result to PAM. That is very unfriendly to a server using a select()
loop to manage many simultaneous tasks. This problem by itself does
not exclude using PAM for a traditional accept(); fork()-style server,
......@@ -66,14 +66,14 @@ in arbitrary order:
(i) The client sends EOF on the channel.
(ii) The server sends EOF on the channel (this happens when there are
no more processe which has the files the server has opened for stdout
no more processes which have the files the server has opened for stdout
or stderr open).
(iii) The child process created by the server dies. An exit-status or
exit-signal message is sent to the client.
lshd handles these condtions as follows: It will not send a CLOSE
message until *all* the three events above have occured. Naturally,
lshd handles these conditions as follows: It will not send a CLOSE
message until *all* the three events above have occurred. Naturally,
this could cause the channel to stay open for ever, for instance if
the child process has started a background job that happens to keep
its stdin open (perhaps without ever using it). In most cases this is
......
......@@ -11,9 +11,7 @@ Port forwarding services
UDP (not in the spec, but that's no reason why it couldn't be
implemented).
PTY support.
Compression (zlib) support.
PTY support. See NOTES.
User interface. This includes command line options, configuration
files, an escape-character mechanism and any features attached to that,
......@@ -34,6 +32,9 @@ protocol.
SSH agent (does anybody have a specification of the protocol(s) used
by ssh-agent?)
SSH1 fallback support in the daemon. This should be done through an external
SSH1 daemon. The configure part of this is already done (SSH1_FALLBACK and
SSH1D).
Projects not to do:
......
......@@ -35,11 +35,6 @@ Signature Algorithm); the GNU Privacy Guard contains a DSA implementation
S-EXPRESSIONS
Write a sexp-parser, at least for the canonical and transport forms.
Should probably be implemented as a read handler. Perhaps we can use
Ron Rivest's code at <URL:
http://theory.lcs.mit.edu/~rivest/sexp.html>.
Add newlines and pretty printing to the sexp formatter.
......@@ -58,6 +53,14 @@ CONFIGURATION
Read configuration files. Better command line options.
LOGGING
Use syslog to log server startup, shutdown, and user authentication.
Update utmp (users currently logged in), wtmp (record of logins) and lastlog
(last login time).
Use tcpwrapper's libwrap to log connections and perform access control.
ALLOCATION
Not all packet consumers free processed packets properly.
......@@ -138,7 +141,7 @@ regular lsh distribution, and make it available separately (this approach is
taken with GnuPG).
scsh is not free software. Ask Olin Shivers and the other scsh
copright owners to fix that, or rewrite the Scheme scripts so that
copyright owners to fix that, or rewrite the Scheme scripts so that
they are as portable between Scheme implementations as possible, or
switch to a free Scheme implementation, like Guile.
......
......@@ -90,8 +90,45 @@ EXTRA_DIST = atoms.in process_atoms make_class $(generated_sources) \
$(cvs_headers) $(dist_x_files) Makefile.am.in make_am \
HACKING NOTES TASKLIST
MAINTAINERCLEANFILES += $(dist_x_files)
# Class files
# SUFFIXES = .xh .xc
.PHONY: depend
depend:
rm -f $(DEP_FILES)
make $(DEP_FILES)
# This rule requires GNU-make
.deps/%.P : %.c %.c.x
$(CC) $(CPPFLAGS) $(DEFS) -M -MG $< > $@
MAINTAINERCLEANFILES += $(DEP_FILES)
.PHONY: ctags-recursive ctags
ctags-recursive:
list='$(SUBDIRS)'; for subdir in $$list; do \
(cd $$subdir && $(MAKE) ctags); \
done
ctags: ctags-recursive $(HEADERS) $(SOURCES) $(CONFIG_HEADER) $(TAGS_DEPENDENCIES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SUBDIRS)'; for subdir in $$list; do \
test -f $$subdir/tags && tags="$$tags $$here/$$subdir/tags"; \
done; \
list='$(SOURCES) $(HEADERS)'; \
unique=`for i in $$list; do echo $$i; done | \
awk ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(CTAGS_ARGS)$(CONFIG_HEADER)$$unique$(LISP)$$tags" \
|| (cd $(srcdir) && ctags -o $$here/tags $(CTAGS_ARGS) $$tags $(CONFIG_HEADER) $$unique $(LISP))
# Override the standard distclean-tags target, as this doesn't support `tags'
distclean-tags:
-rm -f TAGS ID tags
# This is GNU make specific
......
......@@ -9,6 +9,12 @@
/* Define if zlib should be used */
#undef WITH_ZLIB
/* Define to enable fallback to SSH1 */
#undef SSH1_FALLBACK
/* Location of the SSH1 daemon*/
#undef SSHD1
/* Define if IDEA should be used */
#undef WITH_IDEA
......
......@@ -87,7 +87,7 @@ static int client_initiate(struct fd_callback **c,
connection->client_version
= ssh_format("SSH-%lz-%lz %lz",
PROTOCOL_VERSION,
CLIENT_PROTOCOL_VERSION,
SOFTWARE_CLIENT_VERSION,
closure->id_comment);
......
......@@ -25,9 +25,10 @@ esac
# We want to compile the crypto lib for use with lsh
CPPFLAGS="$CPPFLAGS -I$srcdir/include -DLSH"
# _GNU_SOURCE is needed on some readhat glibc based systems, to get
# proper declarations of crypt(), initgroups() and strsignal().
# Enable it on all systems untill some harm is reported
# GNU libc defaults to supplying the ISO C library functions only.
# initgroups() and strsignal() are extensions; the _GNU_SOURCE define
# enables these extensions.
# Enable it on all systems; no problems have been reported with it so far.
CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
......@@ -47,6 +48,12 @@ AC_ARG_WITH(zlib,
dnl Checking this variable is delayed until we have checked if zlib is
dnl actually available.
AC_ARG_WITH(sshd1,
[--with-sshd1[=PROGRAM] support fallback to SSH1 daemon (broken)])
dnl Checking this variable is delayed until we have checked if an SSH1
dnl server is actually available.
AC_ARG_WITH(idea,
[--with-idea support the patent-encumbered IDEA algorithm],,
[with_idea=no])
......@@ -96,6 +103,47 @@ if test x$with_zlib = xyes ; then
AC_DEFINE(WITH_ZLIB)
fi
dnl Find the SSH1 daemon; try 'sshd1' first; if it fails, look for 'sshd'
if test x$with_sshd1 = xyes ; then
AC_DEFINE(SSH1_FALLBACK)
dnl We have to provide an additional path: regular users seldomly have
dnl the sbin directories in their $PATH.
AC_PATH_PROG(SSHD1BIN, sshd1, [],
$PATH:/usr/local/sbin:/usr/local/etc:/usr/sbin:/usr/etc:/sbin:/etc)
if test x$SSHD1BIN = x ; then
AC_PATH_PROG(SSHD1BIN, sshd, [],
$PATH:/usr/local/sbin:/usr/local/etc:/usr/sbin:/usr/etc:/sbin:/etc)
fi
if test x$SSHD1BIN = x ; then
AC_MSG_ERROR([
You requested SSH1 fallback support (--with-sshd1), but no sshd1 could be found.
You can
- supply the location of sshd1, i.e. --with-sshd1=/home/foo/sbin/sshd ,
- install sshd1 (ftp://ftp.cs.hut.fi/pub/ssh/); note that it is not free
software, or
- configure --without-sshd1 .
])
fi
dnl OK. We've found an SSH1 daemon. Now we need to make sure it is
dnl recent enough to be used as a fallback daemon. This means it has to
dnl support the -V option, which tells it not to read the client version
dnl string as lshd has already done that, and which supplies that version
dnl string.
AC_MSG_CHECKING(if $SSHD1BIN has the -V compatibility flag)
changequote(<<, >>)dnl
if ($SSHD1BIN -h 2>&1 ; true) | grep '^[ \t]*-V' > /dev/null ; then
changequote([, ])dnl
AC_MSG_RESULT(yes)
else
AC_MSG_ERROR([
Found SSH1 daemon ]$SSHD1BIN[, but it does not support the -V option.
This means we cannot use it as a fallback daemon. Please install a more recent
SSH1 daemon (ftp://ftp.cs.hut.fi/pub/ssh/), or configure --without-sshd1 .])
fi
AC_DEFINE_UNQUOTED(SSHD1,"$SSHD1BIN")
fi
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_TYPE_UID_T
......@@ -113,6 +161,7 @@ AC_FUNC_MEMCMP
AC_FUNC_VPRINTF
AC_CHECK_FUNCS(select socket strerror strtol)
AC_CHECK_FUNCS(getspnam)
AC_CHECK_FUNCS(vsnprintf)
AC_CHECK_FUNCS(poll,,[LIBOBJS = jpoll.c $LIBOBJS])
......@@ -231,8 +280,8 @@ if test x$GCC = xyes ; then
-Waggregate-return \
-Wpointer-arith -Wbad-function-cast -Wnested-externs"
# Don't enable -Wcast-align results in tons of warnings in the DES
# code. And when using stdio.
# Don't enable -Wcast-align as it results in tons of warnings in the
# DES code. And when using stdio.
fi
......
......@@ -49,7 +49,7 @@ static void write_table(int *table)
printf("\n}");
}
int main(int argc UNUSED, char *argv UNUSED)
int main(int argc UNUSED, char **argv UNUSED)
{
int table[0x100];
unsigned i;
......
......@@ -32,10 +32,12 @@
/* Global variables */
static struct lsh_object *all_objects = NULL;
static struct lsh_object *globals = NULL;
static unsigned number_of_objects = 0;
static unsigned live_objects = 0;
#if 0
static struct lsh_object *globals = NULL;
#endif
#ifdef DEBUG_ALLOC
static void sanity_check_object_list(void)
......@@ -152,7 +154,10 @@ void gc_register(struct lsh_object *o)
#endif
}
/* FIXME: This function is utterly broken, and should be deleted. */
#if 0
/* FIXME: This function is utterly broken, and should be deleted. The
* problem is that the object must be unlinked from the all_objects
* list before linked into the globals list. */
void gc_register_global(struct lsh_object *o)
{
#ifdef DEBUG_ALLOC
......@@ -166,7 +171,8 @@ void gc_register_global(struct lsh_object *o)
sanity_check_object_list();
#endif
}
#endif
/* FIXME: This function should really deallocate and forget the object
* early. But we keep it until the next gc, in order to catch any
* references to killed objects. */
......@@ -188,20 +194,18 @@ void gc_kill(struct lsh_object *o)
#endif
}
void gc(void)
void gc(struct lsh_object *root)
{
unsigned before = number_of_objects;
struct lsh_object *o;
for (o = globals; o; o = o->next)
gc_mark(o);
gc_mark(root);
gc_sweep();
verbose("Objects alive: %d, garbage collected: %d\n", live_objects,
before - live_objects);
}
void gc_maybe(int busy)
void gc_maybe(struct lsh_object *root, int busy)
{
#ifdef DEBUG_ALLOC
sanity_check_object_list();
......@@ -210,6 +214,6 @@ void gc_maybe(int busy)
if (number_of_objects > (100 + live_objects*(2+busy)))
{
verbose("Garbage collecting while %s...\n", busy ? "busy" : "idle");
gc();
gc(root);
}
}
......@@ -28,12 +28,14 @@
#include "lsh.h"
#if 0
void gc_register_global(struct lsh_object *o);
#endif
void gc_register(struct lsh_object *o);
void gc_kill(struct lsh_object *o);
void gc(void);
void gc_maybe(int busy);
void gc(struct lsh_object *root);
void gc_maybe(struct lsh_object *root, int busy);
#endif /* LSH_GC_H_INCLUDED */
/* io.c
*
*
*
* $Id$ */
......@@ -158,6 +156,8 @@ int io_iter(struct io_backend *b)
gc_maybe(&b->super, 0);
res = poll(fds, nfds, -1);
}
else
gc_maybe(&b->super, 1);
if (!res)
{
......
......@@ -127,7 +127,6 @@ int main(int argc, char **argv)
int in, out, err;
NEW(io_backend, backend);
gc_register_global(&backend->super);
/* For filtering messages. Could perhaps also be used when converting
* strings to and from UTF8. */
......
......@@ -136,7 +136,7 @@ struct callback
* filedescriptor closed. */
#define LSH_DIE 4
/* Close all other filedescriptors immediately. MAinly used when forking.
/* Close all other filedescriptors immediately. Mainly used when forking.
* Can be combined with LSH_FAIL or LSH_DIE or both. */
#define LSH_KILL_OTHERS 8
......
......@@ -143,7 +143,6 @@ int main(int argc, char **argv)
struct packet_handler *kexinit_handler;
NEW(io_backend, backend);
gc_register_global(&backend->super);
/* For filtering messages. Could perhaps also be used when converting
* strings to and from UTF8. */
......
......@@ -89,7 +89,7 @@ struct resource_node
#define KILL_RESOURCE_NODE(l, n) ((l)->kill_resource((l), (n)))
#define KILL_RESOURCE_LIST(l) ((l)->kill_all((l)))
/* Allcoates an empty list. */
/* Allocates an empty list. */
struct resource_list *empty_resource_list(void);
#endif /* LSH_RESOURCE_H_INCLUDED */
......@@ -146,12 +146,18 @@ static int server_initiate(struct fd_callback **c,
connection->server_version
= ssh_format("SSH-%lz-%lz %lz",
PROTOCOL_VERSION,
SERVER_PROTOCOL_VERSION,
SOFTWARE_SERVER_VERSION,
closure->id_comment);
#ifdef SSH1_FALLBACK
/* "In this mode the server SHOULD NOT send carriage return character (ascii
* 13) after the version identification string." */
res = A_WRITE(connection->raw,
ssh_format("%lS\n", connection->server_version));
#else
res = A_WRITE(connection->raw,
ssh_format("%lS\r\n", connection->server_version));
#endif
if (LSH_CLOSEDP(res))
return res;
......@@ -207,7 +213,15 @@ static struct read_handler *do_line(struct line_handler **h,
return new;
}
else
#ifdef SSH1_FALLBACK
if ( ((length >= 6) && !memcmp(line + 4, "1.", 2)) )
{
/* TODO: fork a SSH1 server to handle this connection */
werror("Falling back to ssh1 not implemented.\n");
}
else
#endif /* SSH1_FALLBACK */
{
wwrite("Unsupported protocol version: ");
werror_safe(length, line);
......
......@@ -54,3 +54,37 @@ desQuickCore.c:
LOADCORE,KEYMAPQUICK,SAVECORE)'
EXTRA_DIST = $(cvs_headers) Makefile.am.in descore.README
.PHONY: depend
depend:
rm -f $(DEP_FILES)
make $(DEP_FILES)
.deps/%.P : %.c
$(CC) $(CPPFLAGS) $(DEFS) -M -MG $< > $@
MAINTAINERCLEANFILES += $(DEP_FILES)
.PHONY: ctags-recursive ctags
ctags-recursive:
list='$(SUBDIRS)'; for subdir in $$list; do \
(cd $$subdir && $(MAKE) ctags); \
done
ctags: ctags-recursive $(HEADERS) $(SOURCES) $(CONFIG_HEADER) $(TAGS_DEPENDENCIES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SUBDIRS)'; for subdir in $$list; do \
test -f $$subdir/tags && tags="$$tags $$here/$$subdir/tags"; \
done; \
list='$(SOURCES) $(HEADERS)'; \
unique=`for i in $$list; do echo $$i; done | \
awk ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(CTAGS_ARGS)$(CONFIG_HEADER)$$unique$(LISP)$$tags" \
|| (cd $(srcdir) && ctags -o $$here/tags $(CTAGS_ARGS) $$tags $(CONFIG_HEADER) $$unique $(LISP))
# Override the standard distclean-tags target, as this doesn't support `tags'
distclean-tags:
-rm -f TAGS ID tags
......@@ -3,3 +3,27 @@ CVS_HEADERS: cvs_headers
BODY:
EXTRA_DIST = $(cvs_headers) Makefile.am.in
.PHONY: ctags-recursive ctags
ctags-recursive:
list='$(SUBDIRS)'; for subdir in $$list; do \
(cd $$subdir && $(MAKE) ctags); \
done
ctags: ctags-recursive $(HEADERS) $(SOURCES) $(CONFIG_HEADER) $(TAGS_DEPENDENCIES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SUBDIRS)'; for subdir in $$list; do \
test -f $$subdir/tags && tags="$$tags $$here/$$subdir/tags"; \
done; \
list='$(SOURCES) $(HEADERS)'; \
unique=`for i in $$list; do echo $$i; done | \
awk ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(CTAGS_ARGS)$(CONFIG_HEADER)$$unique$(LISP)$$tags" \
|| (cd $(srcdir) && ctags -o $$here/tags $(CTAGS_ARGS) $$tags $(CONFIG_HEADER) $$unique $(LISP))
# Override the standard distclean-tags target, as this doesn't support `tags'
distclean-tags:
-rm -f TAGS ID tags
......@@ -21,6 +21,13 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define PROTOCOL_VERSION "2.0"
#define CLIENT_PROTOCOL_VERSION "2.0"
#ifdef SSH1_FALLBACK
# define SERVER_PROTOCOL_VERSION "1.99"
#else
# define SERVER_PROTOCOL_VERSION "2.0"
#endif
#define SOFTWARE_CLIENT_VERSION "lsh_0.0"
#define SOFTWARE_SERVER_VERSION "lshd_0.0"
......@@ -73,6 +73,29 @@ void set_error_stream(int fd, int with_poll)
error_write = with_poll ? write_raw_with_poll : write_raw;
}
void wwrite(char *msg)
{
if (!quiet_flag)
{
UINT32 size = strlen(msg);
if (error_pos + size <= BUF_SIZE)
{
memcpy(error_buffer + error_pos, msg, size);
error_pos += size;
if (size && (msg[size-1] == '\n'))
werror_flush();
}
else
{
werror_flush();
WERROR(size, msg);
}
}
}
#ifdef HAVE_VSNPRINTF
/* FIXME: Too bad we can't create a custom FILE * using werror_putc to
* output each character. */
static void w_vnprintf(unsigned size, const char *format, va_list args)
......@@ -98,29 +121,17 @@ static void w_vnprintf(unsigned size, const char *format, va_list args)
WERROR(size, s);
}
}
#else /* !HAVE_VSNPRINTF */
void wwrite(char *msg)
{
if (!quiet_flag)
{
UINT32 size = strlen(msg);
#warning No vsnprintf. Some output to stderr will be lost.
if (error_pos + size <= BUF_SIZE)
{
memcpy(error_buffer + error_pos, msg, size);