/* -*- 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 .
*/
#include <rtl/ustrbuf.hxx>
#include <svl/adrparse.hxx>
namespace
{
enum ElementType { ELEMENT_START, ELEMENT_DELIM, ELEMENT_ITEM, ELEMENT_END };
struct ParsedAddrSpec
{
sal_Unicode const * m_pBegin;
sal_Unicode const * m_pEnd;
ElementType m_eLastElem;
bool m_bAtFound;
bool m_bReparse;
ParsedAddrSpec() { reset(); }
bool isPoorlyValid() const { return m_eLastElem >= ELEMENT_ITEM; }
bool isValid() const { return isPoorlyValid() && m_bAtFound; }
void reset();
void finish();
};
void ParsedAddrSpec::reset()
{
m_pBegin = nullptr;
m_pEnd = nullptr;
m_eLastElem = ELEMENT_START;
m_bAtFound = false ;
m_bReparse = false ;
}
void ParsedAddrSpec::finish()
{
if (isPoorlyValid())
m_eLastElem = ELEMENT_END;
else
reset();
}
}
class SvAddressParser_Impl
{
enum State { BEFORE_COLON, BEFORE_LESS, AFTER_LESS, AFTER_GREATER };
enum TokenType: sal_uInt32 {
TOKEN_QUOTED = 0 x80000000, TOKEN_DOMAIN, TOKEN_COMMENT, TOKEN_ATOM };
sal_Unicode const * m_pInputPos;
sal_Unicode const * m_pInputEnd;
sal_uInt32 m_nCurToken;
sal_Unicode const * m_pCurTokenBegin;
sal_Unicode const * m_pCurTokenEnd;
ParsedAddrSpec m_aOuterAddrSpec;
ParsedAddrSpec m_aInnerAddrSpec;
ParsedAddrSpec * m_pAddrSpec;
State m_eState;
TokenType m_eType;
inline void reset();
void addTokenToAddrSpec(ElementType eTokenElem);
bool readToken();
static OUString reparse(sal_Unicode const * pBegin,
sal_Unicode const * pEnd);
public :
SvAddressParser_Impl(SvAddressParser * pParser, const OUString& rIn);
};
inline void SvAddressParser_Impl::reset()
{
m_aOuterAddrSpec.reset();
m_aInnerAddrSpec.reset();
m_pAddrSpec = &m_aOuterAddrSpec;
m_eState = BEFORE_COLON;
m_eType = TOKEN_ATOM;
}
void SvAddressParser_Impl::addTokenToAddrSpec(ElementType eTokenElem)
{
if (!m_pAddrSpec->m_pBegin)
m_pAddrSpec->m_pBegin = m_pCurTokenBegin;
else if (m_pAddrSpec->m_pEnd < m_pCurTokenBegin)
m_pAddrSpec->m_bReparse = true ;
m_pAddrSpec->m_pEnd = m_pCurTokenEnd;
m_pAddrSpec->m_eLastElem = eTokenElem;
}
// SvAddressParser_Impl
bool SvAddressParser_Impl::readToken()
{
m_nCurToken = m_eType;
switch (m_eType)
{
case TOKEN_QUOTED:
{
m_pCurTokenBegin = m_pInputPos - 1 ;
bool bEscaped = false ;
for (;;)
{
if (m_pInputPos >= m_pInputEnd)
return false ;
sal_Unicode cChar = *m_pInputPos++;
if (bEscaped)
{
bEscaped = false ;
}
else if (cChar == '"' )
{
m_pCurTokenEnd = m_pInputPos;
return true ;
}
else if (cChar == '\\' )
bEscaped = true ;
}
}
case TOKEN_DOMAIN:
{
m_pCurTokenBegin = m_pInputPos - 1 ;
bool bEscaped = false ;
for (;;)
{
if (m_pInputPos >= m_pInputEnd)
return false ;
sal_Unicode cChar = *m_pInputPos++;
if (bEscaped)
bEscaped = false ;
else if (cChar == ']' )
{
m_pCurTokenEnd = m_pInputPos;
return true ;
}
else if (cChar == '\\' )
bEscaped = true ;
}
}
case TOKEN_COMMENT:
{
m_pCurTokenBegin = m_pInputPos - 1 ;
bool bEscaped = false ;
int nLevel = 0 ;
for (;;)
{
if (m_pInputPos >= m_pInputEnd)
return false ;
sal_Unicode cChar = *m_pInputPos++;
if (bEscaped)
{
bEscaped = false ;
}
else if (cChar == '(' )
{
++nLevel;
}
else if (cChar == ')' )
if (nLevel)
{
--nLevel;
}
else
return true ;
else if (cChar == '\\' )
{
bEscaped = true ;
}
}
}
default :
{
sal_Unicode cChar;
for (;;)
{
if (m_pInputPos >= m_pInputEnd)
return false ;
cChar = *m_pInputPos++;
if (cChar > ' ' && cChar != 0 x7F) // DEL
break ;
}
m_pCurTokenBegin = m_pInputPos - 1 ;
if (cChar == '"' || cChar == '(' || cChar == ')' || cChar == ','
|| cChar == '.' || cChar == ':' || cChar == ';'
|| cChar == '<' || cChar == '>' || cChar == '@'
|| cChar == '[' || cChar == '\\' || cChar == ']' )
{
m_nCurToken = cChar;
m_pCurTokenEnd = m_pInputPos;
return true ;
}
else
for (;;)
{
if (m_pInputPos >= m_pInputEnd)
{
m_pCurTokenEnd = m_pInputPos;
return true ;
}
cChar = *m_pInputPos++;
if (cChar <= ' ' || cChar == '"' || cChar == '('
|| cChar == ')' || cChar == ',' || cChar == '.'
|| cChar == ':' || cChar == ';' || cChar == '<'
|| cChar == '>' || cChar == '@' || cChar == '['
|| cChar == '\\' || cChar == ']'
|| cChar == 0 x7F) // DEL
{
m_pCurTokenEnd = --m_pInputPos;
return true ;
}
}
}
}
}
// static
OUString SvAddressParser_Impl::reparse(sal_Unicode const * pBegin,
sal_Unicode const * pEnd)
{
OUStringBuffer aResult;
TokenType eMode = TOKEN_ATOM;
bool bEscaped = false ;
int nLevel = 0 ;
while (pBegin < pEnd)
{
sal_Unicode cChar = *pBegin++;
switch (eMode)
{
case TOKEN_QUOTED:
if (bEscaped)
{
aResult.append(cChar);
bEscaped = false ;
}
else if (cChar == '"' )
{
aResult.append(cChar);
eMode = TOKEN_ATOM;
}
else if (cChar == '\\' )
{
aResult.append(cChar);
bEscaped = true ;
}
else
aResult.append(cChar);
break ;
case TOKEN_DOMAIN:
if (bEscaped)
{
aResult.append(cChar);
bEscaped = false ;
}
else if (cChar == ']' )
{
aResult.append(cChar);
eMode = TOKEN_ATOM;
}
else if (cChar == '\\' )
{
aResult.append(cChar);
bEscaped = true ;
}
else
aResult.append(cChar);
break ;
case TOKEN_COMMENT:
if (bEscaped)
bEscaped = false ;
else if (cChar == '(' )
++nLevel;
else if (cChar == ')' )
if (nLevel)
--nLevel;
else
eMode = TOKEN_ATOM;
else if (cChar == '\\' )
bEscaped = true ;
break ;
case TOKEN_ATOM:
if (cChar <= ' ' || cChar == 0 x7F) // DEL
{
}
else if (cChar == '(' )
{
eMode = TOKEN_COMMENT;
}
else
{
if (cChar == '"' )
{
aResult.append(cChar);
eMode = TOKEN_QUOTED;
}
else if (cChar == '[' )
{
aResult.append(cChar);
eMode = TOKEN_QUOTED;
}
else
aResult.append(cChar);
}
break ;
}
}
return aResult.makeStringAndClear();
}
SvAddressParser_Impl::SvAddressParser_Impl(SvAddressParser * pParser,
const OUString& rInput)
: m_pCurTokenBegin(nullptr)
, m_pCurTokenEnd(nullptr)
{
m_pInputPos = rInput.getStr();
m_pInputEnd = m_pInputPos + rInput.getLength();
reset();
bool bDone = false ;
for (;;)
{
if (!readToken())
{
if (m_eState == AFTER_LESS)
m_nCurToken = '>' ;
else
{
m_nCurToken = ',' ;
bDone = true ;
}
}
switch (m_nCurToken)
{
case TOKEN_QUOTED:
if (m_pAddrSpec->m_eLastElem != ELEMENT_END)
{
if (m_pAddrSpec->m_bAtFound
|| m_pAddrSpec->m_eLastElem <= ELEMENT_DELIM)
m_pAddrSpec->reset();
addTokenToAddrSpec(ELEMENT_ITEM);
}
m_eType = TOKEN_ATOM;
break ;
case TOKEN_DOMAIN:
if (m_pAddrSpec->m_eLastElem != ELEMENT_END)
{
if (m_pAddrSpec->m_bAtFound && m_pAddrSpec->m_eLastElem == ELEMENT_DELIM)
addTokenToAddrSpec(ELEMENT_ITEM);
else
m_pAddrSpec->reset();
}
m_eType = TOKEN_ATOM;
break ;
case TOKEN_COMMENT:
m_eType = TOKEN_ATOM;
break ;
case TOKEN_ATOM:
if (m_pAddrSpec->m_eLastElem != ELEMENT_END)
{
if (m_pAddrSpec->m_eLastElem != ELEMENT_DELIM)
m_pAddrSpec->reset();
addTokenToAddrSpec(ELEMENT_ITEM);
}
break ;
case '(' :
m_eType = TOKEN_COMMENT;
break ;
case ')' :
case '\\' :
case ']' :
m_pAddrSpec->finish();
break ;
case '<' :
switch (m_eState)
{
case BEFORE_COLON:
case BEFORE_LESS:
m_aOuterAddrSpec.finish();
m_pAddrSpec = &m_aInnerAddrSpec;
m_eState = AFTER_LESS;
break ;
case AFTER_LESS:
m_aInnerAddrSpec.finish();
break ;
case AFTER_GREATER:
m_aOuterAddrSpec.finish();
break ;
}
break ;
case '>' :
if (m_eState == AFTER_LESS)
{
m_aInnerAddrSpec.finish();
if (m_aInnerAddrSpec.isValid())
m_aOuterAddrSpec.m_eLastElem = ELEMENT_END;
m_pAddrSpec = &m_aOuterAddrSpec;
m_eState = AFTER_GREATER;
}
else
{
m_aOuterAddrSpec.finish();
}
break ;
case '@' :
if (m_pAddrSpec->m_eLastElem != ELEMENT_END)
{
if (!m_pAddrSpec->m_bAtFound
&& m_pAddrSpec->m_eLastElem == ELEMENT_ITEM)
{
addTokenToAddrSpec(ELEMENT_DELIM);
m_pAddrSpec->m_bAtFound = true ;
}
else
m_pAddrSpec->reset();
}
break ;
case ',' :
case ';' :
if (m_eState == AFTER_LESS)
if (m_nCurToken == ',' )
{
if (m_aInnerAddrSpec.m_eLastElem != ELEMENT_END)
m_aInnerAddrSpec.reset();
}
else
m_aInnerAddrSpec.finish();
else
{
if (m_aInnerAddrSpec.isValid() || (!m_aOuterAddrSpec.isValid() && m_aInnerAddrSpec.isPoorlyValid()))
{
m_pAddrSpec = &m_aInnerAddrSpec;
}
else if (m_aOuterAddrSpec.isPoorlyValid())
{
m_pAddrSpec = &m_aOuterAddrSpec;
}
else
{
m_pAddrSpec = nullptr;
}
if (m_pAddrSpec)
{
OUString aTheAddrSpec;
if (m_pAddrSpec->m_bReparse)
aTheAddrSpec = reparse(m_pAddrSpec->m_pBegin, m_pAddrSpec->m_pEnd);
else
{
sal_Int32 nLen = m_pAddrSpec->m_pEnd - m_pAddrSpec->m_pBegin;
if (nLen == rInput.getLength())
aTheAddrSpec = rInput;
else
aTheAddrSpec = rInput.copy( (m_pAddrSpec->m_pBegin - rInput.getStr()),
nLen);
}
pParser->m_vAddresses.emplace_back( aTheAddrSpec );
}
if (bDone)
return ;
reset();
}
break ;
case ':' :
switch (m_eState)
{
case BEFORE_COLON:
m_aOuterAddrSpec.reset();
m_eState = BEFORE_LESS;
break ;
case BEFORE_LESS:
case AFTER_GREATER:
m_aOuterAddrSpec.finish();
break ;
case AFTER_LESS:
m_aInnerAddrSpec.reset();
break ;
}
break ;
case '"' :
m_eType = TOKEN_QUOTED;
break ;
case '.' :
if (m_pAddrSpec->m_eLastElem != ELEMENT_END)
{
if (m_pAddrSpec->m_eLastElem != ELEMENT_DELIM)
addTokenToAddrSpec(ELEMENT_DELIM);
else
m_pAddrSpec->reset();
}
break ;
case '[' :
m_eType = TOKEN_DOMAIN;
break ;
}
}
}
SvAddressParser::SvAddressParser(const OUString& rInput)
{
SvAddressParser_Impl aDoParse(this , rInput);
}
SvAddressParser::~SvAddressParser()
{
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=99 H=95 G=96
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-10-01)
¤
*© Formatika GbR, Deutschland