gecko-dev/lib/xp/xp_mesg.c
1998-03-28 02:44:41 +00:00

283 lines
7.0 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "xp_mesg.h"
#include "xp_mesgp.h"
#include "xpassert.h"
#include "xp_trace.h"
#include <stddef.h>
#include <stdarg.h>
#ifdef DEBUG
int DebugMessages = 1;
#endif
/*-----------------------------------------------------------------------------
stdarg random access
The stdarg interface requires you to access the argument in order and
specifying the types. We thus build an array first so that when we later
want random access to an argument, we know the types of all the ones
before it.
-----------------------------------------------------------------------------*/
INLINE xpm_Integer
integer_argument (xpm_Args * args, int arg)
{
va_list stack;
xpm_Integer value;
int i;
#ifdef VA_START_UGLINESS
stack = args->stack;
#else
va_start (stack, (*(args->start)));
#endif
#ifdef DEBUG
XP_ASSERT (stack == args->stack);
#endif
for (i = 0; i <= arg; i++) {
if (args->types[i] == matInteger)
value = va_arg (stack, xpm_Integer);
else
va_arg (stack, char*);
}
va_end (stack);
XP_LTRACE(DebugMessages,1,("message: integer %i is %i\n", arg, value));
return value;
}
INLINE char *
stack_string (xpm_Args * args, int arg)
{
va_list stack;
char * value;
int i;
#ifdef VA_START_UGLINESS
stack = args->stack;
#else
va_start (stack, (*(args->start)));
#endif
#ifdef DEBUG
XP_ASSERT (stack == args->stack);
#endif
for (i = 0; i <= arg; i++) {
if (args->types[i] == matInteger)
va_arg (stack, xpm_Integer);
else
value = va_arg (stack, char*);
}
va_end (stack);
XP_LTRACE(DebugMessages,1,("message: string %i is %s\n", arg, value));
return value;
}
/*-----------------------------------------------------------------------------
flow of control
process_message runs through the format string (such as "open %1s with %2s"
and sends normal characters through the "literal" output function and
string and integer arguments though the "argument" function.
-----------------------------------------------------------------------------*/
static void
process_message (const char * format, OutputStream * o)
{
const char * current = format;
char c = *current;
while (c) {
if (c == '%') {
c = *++current;
XP_ASSERT (c);
if (c == '%') {
(o->writeLiteral) (o, '%');
c = *++current;
}
else {
int i = (c - '1');
XP_ASSERT (0 <= i && i < MessageArgsMax);
c = *++current;
XP_ASSERT (c);
(o->writeArgument) (o, c, i);
c = *++current;
}
}
else {
(o->writeLiteral) (o, c);
c = *++current;
}
}
}
/*-----------------------------------------------------------------------------
ScanStream runs through the format string and builds the stdarg random
access table.
-----------------------------------------------------------------------------*/
typedef struct ScanStream_ {
OutputStream o;
} ScanStream;
static void
xpm_scan_literal (OutputStream * o, char c)
{
ScanStream * s = (ScanStream *) o;
}
static void
xpm_scan_argument (OutputStream * o, char c, int i)
{
ScanStream * s = (ScanStream *) o;
if (c == 's') {
s->o.args->sizes[i] = sizeof (char*);
s->o.args->types[i] = matString;
}
else if (c == 'i') {
s->o.args->sizes[i] = sizeof (xpm_Integer);
s->o.args->types[i] = matInteger;
}
else
XP_ABORT(("message: invalid specifier `%c'\n", c));
}
static void
xpm_ScanStack (const char * format, xpm_Args * args)
{
ScanStream s;
s.o.writeLiteral = & xpm_scan_literal;
s.o.writeArgument = & xpm_scan_argument;
s.o.args = args;
process_message (format, & s.o);
}
/*-----------------------------------------------------------------------------
CountStream counts the total number of characters in the message.
-----------------------------------------------------------------------------*/
typedef struct CountStream_ {
OutputStream o;
int total;
} CountStream;
static void
xpm_count_literal (OutputStream * o, char c)
{
CountStream * s = (CountStream *) o;
s->total ++;
}
static void
xpm_count_argument (OutputStream * o, char c, int i)
{
CountStream * s = (CountStream *) o;
if (c == 's') {
char * value = stack_string (o->args, i);
int len = value? strlen (value): 0;
s->total += len;
}
else if (c == 'i') {
s->total += 10;
}
}
static int
xpm_MessageLen (const char * format, xpm_Args * args)
{
CountStream s;
s.o.writeLiteral = & xpm_count_literal;
s.o.writeArgument = & xpm_count_argument;
s.o.args = args;
s.total = 0;
xpm_ScanStack (format, args);
process_message (format, & s.o);
return s.total + 1 /* NULL */;
}
/*-----------------------------------------------------------------------------
Public API
These just build stdarg structures and call the internal functions.
-----------------------------------------------------------------------------*/
int
XP_MessageLen (const char * format, ...)
{
xpm_Args args;
int len;
#ifdef VA_START_UGLINESS
va_start (args.stack, format);
#endif
args.start = &format;
len = xpm_MessageLen (format, &args);
#ifdef VA_START_UGLINESS
va_end (args.stack);
#endif
return len;
}
void
XP_Message (char * buffer, int bufferLen, const char * format, ...)
{
/*
This is a positional format string function. It's almost like
sprintf, except that sprintf takes the format arguments in the
order they are pushed on the stack. This won't work for
il8n purposes. In other languages the arguments may have to be
used in a different order. Therefore we need a format string
which specifies the argument explicitly (say by number).
sprintf fails us here:
English: format = "open %1s with %2s"
Japanese: format = "with %2s open %1s"
printf (format, "foo.gif", "LView") ->
English: "open foo.gif with LView"
Japanese: "with foo.gif open LView" (wrong)
XP_Message will work like this:
English: format = "open %1s with %2s"
Japanese: format = "with %2s open %1s"
XP_Message (format, "foo.gif", "LView") ->
English: "open foo.gif with LView"
Japanese: "with LView open foo.gif" (arguments in correct positions)
The syntax is: %NT, N = number of argument, T = type (only s and i
supported). So %1s is the first argument as a string, %2i is the second
argument as a number. No other printf modifiers (field widths, etc) are
supported. It is legal to reference the same argument multiple times.
%% outputs %.
There is a maximum of 9 arguments.
*/
}
const char *
XP_StaticMessage (const char * format, ...)
{
return NULL;
}