mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-26 18:31:29 +00:00
283 lines
7.0 KiB
C
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;
|
|
}
|
|
|