Drop MzString

Change-Id: Id0b0541e5069379ef387b3c9309cdda38929b97f
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/150194
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
This commit is contained in:
Mike Kaganski
2023-04-10 14:54:00 +02:00
parent 8b6d167608
commit b454107b87
7 changed files with 97 additions and 533 deletions

View File

@@ -60,7 +60,6 @@ $(eval $(call gb_Library_add_exception_objects,hwp,\
hwpfilter/source/hwpread \
hwpfilter/source/hwpreader \
hwpfilter/source/lexer \
hwpfilter/source/mzstring \
hwpfilter/source/solver \
))

View File

@@ -20,7 +20,6 @@
#include "formula.h"
#include "grammar.hxx"
#include "mzstring.h"
#include "nodes.h"
#include "mapping.h"
#include "hwpeq.h"
@@ -569,22 +568,21 @@ void Formula::parse()
Node *res = nullptr;
if( !eq ) return;
MzString a;
OString a;
// fprintf(stderr,"\n\n[BEFORE]\n[%s]\n",eq);
eq2latex(a,eq);
int idx=a.find(sal::static_int_cast<char>(0xff));
while(idx){
int idx=a.indexOf('\xff');
while(idx >= 0){
//printf("idx = [%d]\n",idx);
a.replace(idx,0x20);
if((idx = a.find(sal::static_int_cast<char>(0xff),idx+1)) < 0)
break;
a = a.replaceAt(idx, 1, "\x20");
idx = a.indexOf('\xff', idx + 1);
}
char *buf = static_cast<char *>(malloc(a.length()+1));
char *buf = static_cast<char *>(malloc(a.getLength()+1));
bool bStart = false;
int i, j;
for( i = 0, j=0 ; i < a.length() ; i++){ // rtrim and ltrim 32 10 13
for( i = 0, j=0 ; i < a.getLength() ; i++){ // rtrim and ltrim 32 10 13
if( bStart ){
buf[j++] = a[i];
}
@@ -605,7 +603,7 @@ void Formula::parse()
}
// fprintf(stderr,"\n\n[RESULT]\n[%s]\n",a.c_str());
if( buf[0] != '\0' )
res = mainParse( a.c_str() );
res = mainParse( a.getStr() );
else
res = nullptr;
free(buf);

View File

@@ -523,7 +523,7 @@ static void getOutlineNumStr(int style, int level, int num, hchar * hstr)
if (fmt & NUM)
{
auto const numbuf = OString::number(num);
str2hstr(numbuf.getStr(), hstr);
str2hstr(numbuf.buf, hstr);
hstr += numbuf.length;
}
else if (fmt & (U_ROM | L_ROM))

View File

@@ -27,7 +27,6 @@
#include <istream>
#include <sstream>
#include "mzstring.h"
#include "hwpeq.h"
#include <sal/types.h>
#include <sal/macros.h>
@@ -52,17 +51,11 @@ static bool IS_BINARY(std::istream::int_type ch) {
&& strchr("+-<=>", std::istream::traits_type::to_char_type(ch));
}
#ifdef _WIN32
#define STRICMP stricmp
#else
#define STRICMP strcasecmp
#endif
// sub and sup script status
enum { SCRIPT_NONE, SCRIPT_SUB, SCRIPT_SUP, SCRIPT_ALL};
static int eq_word(MzString& outs, std::istream *strm, int script = SCRIPT_NONE);
static bool eq_sentence(MzString& outs, std::istream *strm, const char *end = nullptr);
static int eq_word(OString& outs, std::istream *strm, int script = SCRIPT_NONE);
static bool eq_sentence(OString& outs, std::istream *strm, const char *end = nullptr);
namespace {
@@ -410,21 +403,21 @@ static const hwpeq *lookup_eqn(char const *str)
}
/* If only the first character is uppercase or all characters are uppercase, change to lowercase */
static void make_keyword( char *keyword, const char *token)
static void make_keyword( char *keyword, std::string_view token)
{
char* ptr;
bool result = true;
int len = strlen(token);
int len = token.length();
assert(keyword);
if( 255 < len )
{
len = 255;
}
memcpy(keyword, token, len);
memcpy(keyword, token.data(), len);
keyword[len] = 0;
if( (token[0] & 0x80) || rtl::isAsciiLowerCase(static_cast<unsigned char>(token[0])) || strlen(token) < 2 )
if( (token[0] & 0x80) || rtl::isAsciiLowerCase(static_cast<unsigned char>(token[0])) || token.length() < 2 )
return;
bool capital = rtl::isAsciiUpperCase(
@@ -456,14 +449,14 @@ namespace {
// token reading function
struct eq_stack {
MzString white;
MzString token;
OString white;
OString token;
std::istream *strm;
eq_stack() { strm = nullptr; };
bool state(std::istream const *s) {
if( strm != s) { white = nullptr; token = nullptr; }
return token.length() != 0;
if( strm != s) { white.clear(); token.clear(); }
return token.getLength() != 0;
}
};
@@ -471,10 +464,10 @@ struct eq_stack {
static eq_stack *stk = nullptr;
static void push_token(MzString const &white, MzString const &token, std::istream *strm)
static void push_token(OString const &white, OString const &token, std::istream *strm)
{
// one time stack
assert(stk->token.length() == 0);
assert(stk->token.getLength() == 0);
stk->white = white;
stk->token = token;
@@ -486,20 +479,20 @@ static void push_token(MzString const &white, MzString const &token, std::istrea
*
* control char, control sequence, binary sequence,
* alphabet string, single character */
static int next_token(MzString &white, MzString &token, std::istream *strm)
static int next_token(OString &white, OString &token, std::istream *strm)
{
std::istream::int_type ch = 0;
if( stk->state(strm) ) {
white = stk->white;
token = stk->token;
stk->token = nullptr;
stk->white = nullptr;
return token.length();
stk->token.clear();
stk->white.clear();
return token.getLength();
}
token = nullptr;
white = nullptr;
token.clear();
white.clear();
if( !strm->good() )
return 0;
ch = strm->get();
@@ -510,7 +503,7 @@ static int next_token(MzString &white, MzString &token, std::istream *strm)
if( IS_WS(ch) ) {
do
{
white << static_cast<char>(ch);
white += OStringChar(static_cast<char>(ch));
ch = strm->get();
} while (IS_WS(ch));
}
@@ -519,11 +512,11 @@ static int next_token(MzString &white, MzString &token, std::istream *strm)
|| (ch != std::istream::traits_type::eof() && rtl::isAsciiAlpha(ch)) )
{
if( ch == '\\' ) {
token << static_cast<char>(ch);
token += OStringChar(static_cast<char>(ch));
ch = strm->get();
}
do {
token << static_cast<char>(ch);
token += OStringChar(static_cast<char>(ch));
ch = strm->get();
} while( ch != std::istream::traits_type::eof()
&& (ch & 0x80 || rtl::isAsciiAlpha(ch)) ) ;
@@ -531,24 +524,24 @@ static int next_token(MzString &white, MzString &token, std::istream *strm)
/* special treatment of sub, sub, over, atop
The reason for this is that affect next_state().
*/
if( !STRICMP("sub", token) || !STRICMP("from", token) ||
!STRICMP("sup", token) || !STRICMP("to", token) ||
!STRICMP("over", token) || !STRICMP("atop", token) ||
!STRICMP("left", token) || !STRICMP("right", token) )
if( token.equalsIgnoreAsciiCase("sub") || token.equalsIgnoreAsciiCase("from") ||
token.equalsIgnoreAsciiCase("sup") || token.equalsIgnoreAsciiCase("to") ||
token.equalsIgnoreAsciiCase("over") || token.equalsIgnoreAsciiCase("atop") ||
token.equalsIgnoreAsciiCase("left") || token.equalsIgnoreAsciiCase("right") )
{
char buf[256];
make_keyword(buf, token);
token = buf;
}
if( !token.compare("sub") || !token.compare("from") )
if( token == "sub" || token == "from" )
token = "_";
if( !token.compare("sup") || !token.compare("to") )
if( token == "sup" || token == "to" )
token = "^";
}
else if( IS_BINARY(ch) ) {
do
{
token << static_cast<char>(ch);
token += OStringChar(static_cast<char>(ch));
ch = strm->get();
}
while( IS_BINARY(ch) );
@@ -556,24 +549,24 @@ static int next_token(MzString &white, MzString &token, std::istream *strm)
}
else if( ch != std::istream::traits_type::eof() && rtl::isAsciiDigit(ch) ) {
do {
token << static_cast<char>(ch);
token += OStringChar(static_cast<char>(ch));
ch = strm->get();
} while( ch != std::istream::traits_type::eof() && rtl::isAsciiDigit(ch) );
strm->putback(static_cast<char>(ch));
}
else
token << static_cast<char>(ch);
token += OStringChar(static_cast<char>(ch));
return token.length();
return token.getLength();
}
static std::istream::int_type read_white_space(MzString& outs, std::istream *strm)
static std::istream::int_type read_white_space(OString& outs, std::istream *strm)
{
std::istream::int_type result;
if( stk->state(strm) ) {
outs << stk->white;
stk->white = nullptr;
outs += stk->white;
stk->white.clear();
result = std::istream::traits_type::to_int_type(stk->token[0]);
}
else {
@@ -583,7 +576,7 @@ static std::istream::int_type read_white_space(MzString& outs, std::istream *str
ch = strm->get();
if (!IS_WS(ch))
break;
outs << static_cast<char>(ch);
outs += OStringChar(static_cast<char>(ch));
}
strm->putback(static_cast<char>(ch));
result = ch;
@@ -605,37 +598,37 @@ static std::istream::int_type read_white_space(MzString& outs, std::istream *str
a over b -> {a} over {b}
*/
static int eq_word(MzString& outs, std::istream *strm, int status)
static int eq_word(OString& outs, std::istream *strm, int status)
{
MzString token, white, state;
OString token, white, state;
int result;
char keyword[256];
const hwpeq *eq;
next_token(white, token, strm);
if (token.length() <= 0)
if (token.getLength() <= 0)
return 0;
result = token[0];
if( token.compare("{") == 0 ) {
state << white << token;
if( token == "{" ) {
state += white + token;
eq_sentence(state, strm, "}");
}
else if( token.compare("left") == 0 ) {
state << white << token;
else if( token == "left" ) {
state += white + token;
next_token(white, token, strm);
state << white << token;
state += white + token;
eq_sentence(state, strm, "right");
next_token(white, token, strm);
state << white << token;
state += white + token;
}
else {
/* Normal token */
int script_status = SCRIPT_NONE;
while( true ) {
state << white << token;
state += white + token;
make_keyword(keyword, token);
if( token[0] == '^' )
script_status |= SCRIPT_SUP;
@@ -648,9 +641,9 @@ static int eq_word(MzString& outs, std::istream *strm, int status)
int nargs = eq->nargs;
while( nargs-- ) {
const std::istream::int_type ch = read_white_space(state, strm);
if( ch != '{' ) state << '{';
if( ch != '{' ) state += OStringChar('{');
eq_word(state, strm, script_status);
if( ch != '{' ) state << '}';
if( ch != '{' ) state += OStringChar('}');
}
}
@@ -659,52 +652,52 @@ static int eq_word(MzString& outs, std::istream *strm, int status)
// end loop and restart with this
if( (token[0] == '^' && status && !(status & SCRIPT_SUP)) ||
(token[0] == '_' && status && !(status & SCRIPT_SUB)) ||
strcmp("over", token) == 0 || strcmp("atop", token) == 0 ||
"over" == token || "atop" == token ||
strchr("{}#&`", token[0]) ||
(!strchr("^_", token[0]) && white.length()) )
(!strchr("^_", token[0]) && white.getLength()) )
{
push_token(white, token, strm);
break;
}
}
}
outs << state;
outs += state;
return result;
}
static bool eq_sentence(MzString& outs, std::istream *strm, const char *end)
static bool eq_sentence(OString& outs, std::istream *strm, const char *end)
{
MzString state;
MzString white, token;
OString state;
OString white, token;
bool multiline = false;
read_white_space(outs, strm);
while( eq_word(state, strm) ) {
if( !next_token(white, token, strm) ||
(end && strcmp(token.c_str(), end) == 0) )
(end && token == end) )
{
state << white << token;
state += white + token;
break;
}
push_token(white, token, strm);
if( !token.compare("atop") || !token.compare("over") )
outs << '{' << state << '}';
if( token == "atop" || token == "over" )
outs += OStringChar('{') + state + OStringChar('}');
else {
if( !token.compare("#") )
if( token == "#" )
multiline = true;
outs << state;
outs += state;
}
state = nullptr;
state.clear();
read_white_space(outs, strm);
}
outs << state;
outs += state;
return multiline;
}
static char eq2ltxconv(MzString& sstr, std::istream *strm, const char *sentinel)
static char eq2ltxconv(OString& sstr, std::istream *strm, const char *sentinel)
{
MzString white, token;
OString white, token;
char key[256];
std::istream::int_type ch;
int result;
@@ -729,67 +722,67 @@ static char eq2ltxconv(MzString& sstr, std::istream *strm, const char *sentinel)
}
if( token[0] == '{' ) { // grouping
sstr << white << token;
sstr += white + token;
eq2ltxconv(sstr, strm, "}");
sstr << '}';
sstr += OStringChar('}');
}
else if( eq && (eq->flag & EQ_ENV) ) {
next_token(white, token, strm);
if( token[0] != '{' )
return 0;
sstr << "\\begin" << "{" << eq->key << "}" << SAL_NEWLINE_STRING ;
sstr += OString::Concat("\\begin{") + eq->key + "}" SAL_NEWLINE_STRING ;
eq2ltxconv(sstr, strm, "}");
if( sstr[sstr.length() - 1] != '\n' )
sstr << SAL_NEWLINE_STRING ;
sstr << "\\end" << "{" << eq->key << "}" << SAL_NEWLINE_STRING ;
if( sstr[sstr.getLength() - 1] != '\n' )
sstr += SAL_NEWLINE_STRING ;
sstr += OString::Concat("\\end{") + eq->key + "}" SAL_NEWLINE_STRING ;
}
else if( eq && (eq->flag & EQ_ATOP) ) {
if( sstr.length() == 0 )
sstr << '{';
if( sstr.getLength() == 0 )
sstr += OStringChar('{');
else {
int pos = sstr.rfind('}');
int pos = sstr.lastIndexOf('}');
if( 0 < pos)
sstr.replace(pos, ' ');
sstr = sstr.replaceAt(pos, 1, " ");
}
sstr << token;
sstr += token;
for (;;)
{
ch = strm->get();
if ( ch == std::istream::traits_type::eof() || !IS_WS(ch) )
break;
sstr << static_cast<char>(ch);
sstr += OStringChar(static_cast<char>(ch));
}
if( ch != '{' )
sstr << "{}";
sstr += "{}";
else {
eq2ltxconv(sstr, strm, "}");
sstr << '}';
sstr += OStringChar('}');
}
}
else
sstr << white << token;
sstr += white + token;
}
return token[0];
}
void eq2latex(MzString& outs, char const *s)
void eq2latex(OString& outs, char const *s)
{
assert(s);
if( stk == nullptr )
stk = new eq_stack;
MzString tstr;
OString tstr;
std::istringstream tstrm(s);
bool eqnarray = eq_sentence(tstr, &tstrm);
std::istringstream strm(tstr.c_str());
std::istringstream strm(tstr.getStr());
if( eqnarray )
outs << "\\begin{array}{rllll}" << SAL_NEWLINE_STRING;
outs += "\\begin{array}{rllll}" SAL_NEWLINE_STRING;
eq2ltxconv(outs, &strm, nullptr);
outs << SAL_NEWLINE_STRING;
outs += SAL_NEWLINE_STRING;
if( eqnarray )
outs << "\\end{array}" << SAL_NEWLINE_STRING;
outs += "\\end{array}" SAL_NEWLINE_STRING;
delete stk;
stk = nullptr;
}

View File

@@ -20,9 +20,11 @@
#ifndef INCLUDED_HWPFILTER_SOURCE_HWPEQ_H
#define INCLUDED_HWPFILTER_SOURCE_HWPEQ_H
#include "mzstring.h"
#include <sal/config.h>
void eq2latex(MzString& mstr, char const* str);
#include <rtl/string.hxx>
void eq2latex(OString& outs, char const* s);
#endif // INCLUDED_HWPFILTER_SOURCE_HWPEQ_H

View File

@@ -1,261 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
/* PURPOSE
* supposed to be used instead of std::string
*/
#include "mzstring.h"
#ifdef _WIN32
# if !defined WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <rtl/string.hxx>
const int AllocSize = 8;
static int get_alloc_size(int len)
{
return (len + AllocSize - 1) / AllocSize * AllocSize;
}
MzString::MzString()
{
Length = 0;
Allocated = 0;
Data = nullptr;
}
MzString::~MzString()
{
if (Data)
free(Data);
}
MzString &MzString::operator=(const MzString &s)
{
if(this == &s)
return *this;
int n = s.length();
if (allocate(n))
{
if (n > 0) memcpy(Data, s.Data, n);
Length = n;
}
return *this;
}
MzString &MzString::operator = (const char *s)
{
if (s == nullptr)
s = "";
int n = strlen(s);
if (allocate(n))
{
if (n > 0) memcpy(Data, s, n);
Length = n;
}
return *this;
}
void MzString::append(const char *s, int slen)
{
if(!s || slen <= 0)
return;
int new_len = Length + slen;
if (allocate(new_len))
{
memcpy(Data + Length, s, slen);
Length = new_len;
}
}
void MzString::append(MzString const &s)
{
if (s.Data)
append(s.Data, s.length());
}
void MzString::append(const char *s)
{
if (!s) return;
append(s, strlen(s));
}
int MzString::compare(const char *s)
{
if (!Data) return -1;
if (s==nullptr) return 1;
Data[Length] = 0;
return strcmp(Data, s);
}
int MzString::find(char ch)
{
return find(ch,0);
}
int MzString::find(char ch, int pos)
{
for (int i = pos; i < Length; i++)
{
if (Data[i] == ch)
return i;
}
return -1;
}
int MzString::rfind(char ch)
{
return rfind(ch, Length - 1);
}
int MzString::rfind(char ch, int pos)
{
if (pos >= Length)
return -1;
while (pos >= 0)
{
if (Data[pos] == ch)
return pos;
pos--;
}
return -1;
}
// << operator
MzString &MzString::operator << (const char *str)
{
append(str);
return *this;
}
MzString &MzString::operator << (char ch)
{
append(&ch, 1);
return *this;
}
MzString &MzString::operator << (int i)
{
auto const str = OString::number(i);
append(str.getStr(), str.length);
return *this;
}
MzString &MzString::operator << (tools::Long l)
{
auto const str = OString::number(l);
append(str.getStr(), str.length);
return *this;
}
MzString &MzString::operator << (MzString const &s)
{
append(s);
return *this;
}
char MzString::operator [] (int n)
{
if (Data && 0 <= n && n < Length)
return Data[n];
return 0;
}
void MzString::replace(int pos, char ch)
{
if (Data && 0 <= pos && pos < Length)
Data[pos] = ch;
}
// Private Methods.
bool MzString::allocate(int len)
{
len++; // In case we want to add a null.
if (len < 0)
return false;
if (Data)
{
if (len < Allocated)
return true;
else
{
int n = get_alloc_size(len);
char *p = static_cast<char *>(realloc(Data, n));
if (p)
{
Data = p;
Allocated = n;
return true;
}
}
}
else
{
// In case we want to add a null.
int n = get_alloc_size(len);
Data = static_cast<char *>(malloc(n));
if (Data)
{
Allocated = n;
return true;
}
}
return false;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@@ -1,167 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#ifndef INCLUDED_HWPFILTER_SOURCE_MZSTRING_H
#define INCLUDED_HWPFILTER_SOURCE_MZSTRING_H
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <tools/long.hxx>
/** @name MzString class
It was supposed to be used instead of std::string.
Notes for usage:
When you declare an MzString, it is initially empty. There is no need to
do things like #MzString a = "";#, especially not in constructors.
If you want to use a default empty MzString as a parameter, use
#void foo(MzString par = MzString()); // Correct#
rather than
#void foo(MzString par = ""); // WRONG!#
#void foo(MzString par = 0); // WRONG!#
(The last one is only wrong because some compilers can't handle it.)
Methods that take an index as parameter all follow this rule: Valid indexes
go from 0 to length()-1.
\begin{tabular}{rl}
Correct: & #foo.substr(0, length()-1);# \\
Wrong: & #bar.substr(0, length());#
\end{tabular}
It is important that you declare MzStrings as const if possible, because
some methods are much more efficient in const versions.
If you want to check whether a string is empty, do
#if (foo.empty()) something right#
rather than something along the lines of
#if (!foo) completely wrong#
When you use the #.copy()# method, MzString calls "#new []#", so you have to
release the memory with #delete[]#. Don't preallocate memory.
When you want to copy an MzString, just do
#MzString a, b = "String";#
#a = b; // That's it!#
not something like
#MzString a, b = "String";#
#a = b.copy();#
The class automatically handles deep copying when required.
*/
class MzString
{
public:
MzString(); // Create an empty string
~MzString();
int length() const;
const char* c_str() const;
operator char*() { return const_cast<char *>(c_str()); }
// Assignment
MzString &operator = (const MzString &s);
MzString &operator = (const char *s);
// Appending
MzString &operator << (const char *);
MzString &operator << (char);
MzString &operator << (unsigned char c) { return *this<<static_cast<char>(c); }
MzString &operator << (int);
MzString &operator << (tools::Long);
MzString &operator << (short i) { return *this<<static_cast<int>(i); }
MzString &operator << (MzString const &);
/* MzString &operator << (MzString *s) { return *this<<*s; }
// Removing
char operator >> (char &c);
*/
// Access to specific characters
//char &operator [] (int n);
char operator [] (int n);
// Comparison
// Return:
// 0 : 'this' is equal to 's'.
// -1 : 'this' is less than 's'.
// 1 : 'this' is greater than 's'.
int compare(const char *s);
// Searching for parts
int find (char c);
int find (char c, int pos);
int rfind (char c);
int rfind (char c, int pos);
// Manipulation
void replace(int, char c);
void append (MzString const &s);
void append (const char *s);
void append (const char *s, int n);
private:
int Length; // Current Length
int Allocated; // Total space allocated
char *Data; // The actual contents
// Allocate some space for the data.
// Delete Data if it has been allocated.
bool allocate(int len);
};
inline int MzString::length() const
{
return Length;
}
inline const char* MzString::c_str() const
{
if (Data)
{
Data[Length] = '\0'; // We always leave room for this.
return Data;
}
else
return "";
}
// Non friend, non member operators
#endif // INCLUDED_HWPFILTER_SOURCE_MZSTRING_H
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */