swad-core/swad_string.c

2943 lines
97 KiB
C
Raw Normal View History

2014-12-01 23:55:08 +01:00
// swad_string.c: string processing
/*
SWAD (Shared Workspace At a Distance),
is a web platform developed at the University of Granada (Spain),
and used to support university teaching.
This file is part of SWAD core.
2019-01-07 21:52:19 +01:00
Copyright (C) 1999-2019 Antonio Ca<EFBFBD>as Vargas
2014-12-01 23:55:08 +01:00
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*****************************************************************************/
/********************************* Headers ***********************************/
/*****************************************************************************/
#include <linux/stddef.h> // For NULL
#include <ctype.h> // For isprint, isspace, etc.
#include <locale.h> // For setlocale
2015-03-11 14:35:33 +01:00
#include <math.h> // For log10, floor, ceil, modf, sqrt...
2014-12-01 23:55:08 +01:00
#include <stdlib.h> // For malloc and free
#include <string.h> // For string functions
2018-11-09 20:47:39 +01:00
#include "swad_form.h"
2014-12-01 23:55:08 +01:00
#include "swad_global.h"
#include "swad_ID.h"
#include "swad_parameter.h"
#include "swad_string.h"
/*****************************************************************************/
/******************** Global variables from other modules ********************/
/*****************************************************************************/
extern struct Globals Gbl; // Declaration in swad.c
/*****************************************************************************/
/****************************** Internal types *******************************/
/*****************************************************************************/
/*****************************************************************************/
/*************************** Internal prototypes *****************************/
/*****************************************************************************/
2016-05-01 12:48:22 +02:00
static unsigned Str_GetNextASCIICharFromStr (const char *Ptr,unsigned char *Ch);
2017-01-19 03:15:35 +01:00
static unsigned Str_FindHTMLEntity (const char *Ptr);
2014-12-01 23:55:08 +01:00
static int Str_ReadCharAndSkipComments (FILE *FileSrc,Str_SkipHTMLComments_t SkipHTMLComments);
static int Str_ReadCharAndSkipCommentsWriting (FILE *FileSrc,FILE *FileTgt,Str_SkipHTMLComments_t SkipHTMLComments);
static int Str_ReadCharAndSkipCommentsBackward (FILE *FileSrc,Str_SkipHTMLComments_t SkipHTMLComments);
/*****************************************************************************/
/**************************** Internal constants *****************************/
/*****************************************************************************/
/***** Conversion to Base64URL *****/
// base64url is described in document http://tools.ietf.org/html/rfc4648.
// It uses '-' and '_' because they are safe for URL/parameters (without enconding) and for filenames.
2019-03-15 15:25:31 +01:00
const char Str_BIN_TO_BASE64URL[64 + 1] =
2014-12-01 23:55:08 +01:00
{'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
2019-03-15 15:25:31 +01:00
'0','1','2','3','4','5','6','7','8','9','-','_',
'\0'}; // NULL-terminated string
2014-12-01 23:55:08 +01:00
static const char Str_LF[2] = {10,0};
static const char Str_CR[2] = {13,0};
/*****************************************************************************/
/****************************** Internal types *******************************/
/*****************************************************************************/
/*****************************************************************************/
2016-01-25 00:19:21 +01:00
/****************** Insert a link in every URL or nickname *******************/
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
/*
Insertion example:
2016-01-24 22:08:23 +01:00
The web site of @rms is https://stallman.org/
The web site of <a href="https://openswad.org/?usr=@rms">@rms</a> is <a href="https://stallman.org/" target="_blank">https://stallman.org/</a>
2014-12-01 23:55:08 +01:00
*/
2016-01-26 12:14:16 +01:00
/*
<form id="form_z7FY4oFr-yot9R9xRVwDhIntnod3geLj36nyQ6jlJJs_53"
action="https://localhost/swad/es" method="post">
<input type="hidden" name="ses" value="2jb9CGhIJ81_qhDyeQ6MWDFKQ5ZaA_F68tq22ZAjYww">
<input type="hidden" name="usr" value="@acanas">
2016-01-26 13:45:45 +01:00
<a href="" onclick="document.getElementById('form_z7FY4oFr-yot9R9xRVwDhIntnod3geLj36nyQ6jlJJs_53').submit();return false;">
2016-01-26 12:14:16 +01:00
@acanas
</a>
</form>
*/
2016-01-24 22:08:23 +01:00
#define ANCHOR_1_URL "<a href=\""
#define ANCHOR_2_URL "\" target=\"_blank\">"
2016-01-26 13:45:45 +01:00
#define ANCHOR_3_URL "</a>"
2016-01-26 14:23:16 +01:00
#define ANCHOR_3_NICK "</a></form>"
2016-01-24 22:08:23 +01:00
#define MAX_LINKS 1000
2017-01-17 03:10:43 +01:00
#define MAX_BYTES_LIMITED_URL (1024 - 1) // Max. number of bytes of the URL shown on screen
2014-12-01 23:55:08 +01:00
2016-01-25 00:19:21 +01:00
void Str_InsertLinks (char *Txt,unsigned long MaxLength,size_t MaxCharsURLOnScreen)
2014-12-01 23:55:08 +01:00
{
2018-12-08 16:43:13 +01:00
extern const char *Lan_STR_LANG_ID[1 + Lan_NUM_LANGUAGES];
2018-11-09 20:47:39 +01:00
char ParamsStr[Frm_MAX_BYTES_PARAMS_STR];
2018-10-17 01:08:42 +02:00
char Anchor1Nick[256 + 256 + 256 + Cns_BYTES_SESSION_ID + 256 + 256];
2017-03-07 01:56:41 +01:00
char Anchor2Nick[256 + Cry_BYTES_ENCRYPTED_STR_SHA256_BASE64];
2016-01-25 00:19:21 +01:00
size_t TxtLength;
size_t TxtLengthWithInsertedAnchors;
2016-01-26 13:45:45 +01:00
2016-01-25 00:19:21 +01:00
size_t Anchor1URLLength;
size_t Anchor2URLLength;
2016-01-26 13:45:45 +01:00
size_t Anchor3URLLength;
2016-01-25 00:19:21 +01:00
size_t AnchorURLTotalLength;
2016-01-26 13:45:45 +01:00
size_t Anchor1NickLength = 0; // Initialized only to avoid warning
size_t Anchor2NickLength = 0; // Initialized only to avoid warning
size_t Anchor3NickLength;
size_t AnchorNickTotalLength = 0; // Initialized only to avoid warning
2016-01-24 22:08:23 +01:00
char *PtrSrc;
char *PtrDst;
2014-12-01 23:55:08 +01:00
bool URLStartFound;
2016-01-24 22:08:23 +01:00
bool IsNickname;
int NumLinks = 0;
int NumLink;
2014-12-01 23:55:08 +01:00
struct
{
2016-01-26 13:45:45 +01:00
char *PtrStart; // Pointer to the first char of URL/nickname in original text
char *PtrEnd; // Pointer to the last char of URL/nickname in original text
2016-01-24 22:08:23 +01:00
size_t NumActualBytes; // Actual length of the URL/nickname
2016-01-26 13:45:45 +01:00
char *Anchor1Nick;
char *Anchor2Nick;
size_t Anchor1NickLength;
size_t Anchor2NickLength;
size_t AddedLengthUntilHere; // Total length of extra HTML code added until this link (included)
2016-01-24 22:08:23 +01:00
} Links[MAX_LINKS];
2016-01-26 12:14:16 +01:00
size_t LengthVisibleLink;
2016-01-24 22:08:23 +01:00
size_t Length;
size_t i;
2016-01-26 12:14:16 +01:00
size_t NumChars1;
size_t NumChars2;
2014-12-01 23:55:08 +01:00
size_t NumBytesToCopy;
size_t NumBytesToShow; // Length of the link displayed on screen (may be shorter than actual length)
2017-01-28 15:58:46 +01:00
char LimitedURL[MAX_BYTES_LIMITED_URL + 1];
2016-05-01 12:48:22 +02:00
unsigned char Ch;
2014-12-01 23:55:08 +01:00
2016-01-26 14:23:16 +01:00
/****** Initialize constant anchors and their lengths *****/
TxtLength = strlen (Txt);
2016-01-26 13:45:45 +01:00
2016-01-26 14:23:16 +01:00
// For URLs the length of anchor is fixed
2016-01-26 13:45:45 +01:00
// so it can be calculated once
2016-01-26 14:23:16 +01:00
Anchor1URLLength = strlen (ANCHOR_1_URL);
Anchor2URLLength = strlen (ANCHOR_2_URL);
Anchor3URLLength = strlen (ANCHOR_3_URL);
AnchorURLTotalLength = Anchor1URLLength +
Anchor2URLLength +
Anchor3URLLength;
// For nicknames the length of anchor is variable
// so it can be calculated for each link,
// except the third part that is fixed
Anchor3NickLength = strlen (ANCHOR_3_NICK);
/**************************************************************/
2016-01-25 00:19:21 +01:00
/***** Find starts and ends of links (URLs and nicknames) *****/
2016-01-26 14:23:16 +01:00
/**************************************************************/
2014-12-01 23:55:08 +01:00
for (PtrSrc = Txt;
*PtrSrc;)
2016-01-24 22:08:23 +01:00
/* Check if the next char is the start of a URL */
2014-12-01 23:55:08 +01:00
if (tolower ((int) *PtrSrc) == (int) 'h')
{
URLStartFound = false;
2016-01-24 22:08:23 +01:00
Links[NumLinks].PtrStart = PtrSrc;
2014-12-01 23:55:08 +01:00
if (tolower ((int) *++PtrSrc) == (int) 't') // ht...
{
if (tolower ((int) *++PtrSrc) == (int) 't') // htt...
{
if (tolower ((int) *++PtrSrc) == (int) 'p') // http...
{
PtrSrc++;
if (*PtrSrc == ':') // http:...
{
if (*++PtrSrc == '/') // http:/...
if (*++PtrSrc == '/') // http://...
URLStartFound = true;
}
else if (tolower ((int) *PtrSrc) == (int) 's') // https...
{
if (*++PtrSrc == ':') // https:...
{
if (*++PtrSrc == '/') // https:/...
if (*++PtrSrc == '/') // https://...
URLStartFound = true;
}
}
}
}
}
if (URLStartFound)
{
/* Find URL end */
PtrSrc++; // Points to first character after http:// or https://
for (;;)
{
NumChars1 = Str_GetNextASCIICharFromStr (PtrSrc,&Ch);
PtrSrc += NumChars1;
2016-05-01 12:48:22 +02:00
if (Ch <= 32 || Ch == '<' || Ch == '"')
2014-12-01 23:55:08 +01:00
{
2016-01-24 22:08:23 +01:00
Links[NumLinks].PtrEnd = PtrSrc - NumChars1 - 1;
2014-12-01 23:55:08 +01:00
break;
}
else if (Ch == ',' || Ch == '.' || Ch == ';' || Ch == ':' || Ch == ')' || Ch == ']' || Ch == '}')
{
NumChars2 = Str_GetNextASCIICharFromStr (PtrSrc,&Ch);
PtrSrc += NumChars2;
2016-05-01 12:48:22 +02:00
if (Ch <= 32 || Ch == '<' || Ch == '"')
2014-12-01 23:55:08 +01:00
{
2016-01-24 22:08:23 +01:00
Links[NumLinks].PtrEnd = PtrSrc - NumChars2 - NumChars1 - 1;
2014-12-01 23:55:08 +01:00
break;
}
}
}
2016-01-26 13:45:45 +01:00
/* Initialize anchors for this link */
Links[NumLinks].Anchor1Nick = NULL;
Links[NumLinks].Anchor2Nick = NULL;
2014-12-01 23:55:08 +01:00
/* Calculate length of this URL */
2016-01-25 11:43:14 +01:00
Links[NumLinks].NumActualBytes = (size_t) (Links[NumLinks].PtrEnd + 1 - Links[NumLinks].PtrStart);
2016-01-24 22:08:23 +01:00
if (Links[NumLinks].NumActualBytes <= MaxCharsURLOnScreen)
2016-01-26 12:14:16 +01:00
LengthVisibleLink = Links[NumLinks].NumActualBytes;
2014-12-01 23:55:08 +01:00
else // If URL is too long to be displayed ==> short it
{
/* Make a copy of this URL */
2016-01-24 22:08:23 +01:00
NumBytesToCopy = (Links[NumLinks].NumActualBytes < MAX_BYTES_LIMITED_URL) ? Links[NumLinks].NumActualBytes :
MAX_BYTES_LIMITED_URL;
2017-01-17 03:10:43 +01:00
strncpy (LimitedURL,Links[NumLinks].PtrStart,NumBytesToCopy);
LimitedURL[NumBytesToCopy] = '\0';
2014-12-01 23:55:08 +01:00
/* Limit the number of characters on screen of the copy, and calculate its length in bytes */
2016-01-26 12:14:16 +01:00
LengthVisibleLink = Str_LimitLengthHTMLStr (LimitedURL,MaxCharsURLOnScreen);
2014-12-01 23:55:08 +01:00
}
2016-01-24 22:08:23 +01:00
if (NumLinks == 0)
2016-01-26 12:14:16 +01:00
Links[NumLinks].AddedLengthUntilHere = AnchorURLTotalLength + LengthVisibleLink;
2016-01-24 22:08:23 +01:00
else
2016-01-26 12:14:16 +01:00
Links[NumLinks].AddedLengthUntilHere = Links[NumLinks - 1].AddedLengthUntilHere +
AnchorURLTotalLength + LengthVisibleLink;
2016-01-24 22:08:23 +01:00
2016-01-26 13:45:45 +01:00
/* Increment number of found links */
2016-01-24 22:08:23 +01:00
NumLinks++;
if (NumLinks == MAX_LINKS)
2014-12-01 23:55:08 +01:00
break;
}
}
2016-01-24 22:08:23 +01:00
/* Check if the next char is the start of a nickname */
else if ((int) *PtrSrc == (int) '@')
{
Links[NumLinks].PtrStart = PtrSrc;
/* Find nickname end */
PtrSrc++; // Points to first character after @
/* A nick can have digits, letters and '_' */
for (;
*PtrSrc;
PtrSrc++)
if (!((*PtrSrc >= 'a' && *PtrSrc <= 'z') ||
(*PtrSrc >= 'A' && *PtrSrc <= 'Z') ||
(*PtrSrc >= '0' && *PtrSrc <= '9') ||
(*PtrSrc == '_')))
break;
/* Calculate length of this nickname */
Links[NumLinks].PtrEnd = PtrSrc - 1;
2016-01-25 11:43:14 +01:00
Links[NumLinks].NumActualBytes = (size_t) (PtrSrc - Links[NumLinks].PtrStart);
2016-01-24 22:08:23 +01:00
/* A nick (without arroba) must have a number of characters
2017-03-07 01:56:41 +01:00
Nck_MIN_BYTES_NICKNAME_WITHOUT_ARROBA <= Length <= Nck_MAX_BYTES_NICKNAME_WITHOUT_ARROBA */
2016-01-24 22:08:23 +01:00
Length = Links[NumLinks].NumActualBytes - 1; // Do not count the initial @
2017-03-07 01:56:41 +01:00
IsNickname = (Length >= Nck_MIN_BYTES_NICKNAME_WITHOUT_ARROBA &&
Length <= Nck_MAX_BYTES_NICKNAME_WITHOUT_ARROBA);
2016-01-24 22:08:23 +01:00
if (IsNickname)
{
2016-01-26 13:45:45 +01:00
/* Initialize anchors for this link */
Links[NumLinks].Anchor1Nick = NULL;
Links[NumLinks].Anchor2Nick = NULL;
2016-01-26 14:23:16 +01:00
/* Create id for this form */
Gbl.Form.Num++;
if (Gbl.Usrs.Me.Logged)
2018-10-18 02:02:32 +02:00
snprintf (Gbl.Form.UniqueId,sizeof (Gbl.Form.UniqueId),
"form_%s_%d",
Gbl.UniqueNameEncrypted,Gbl.Form.Num);
2016-01-26 14:23:16 +01:00
else
2018-10-18 02:02:32 +02:00
snprintf (Gbl.Form.Id,sizeof (Gbl.Form.Id),
"form_%d",
Gbl.Form.Num);
2016-01-26 14:23:16 +01:00
/* Store first part of anchor */
2018-11-09 20:47:39 +01:00
Frm_SetParamsForm (ParamsStr,ActSeeOthPubPrf,true);
2018-10-18 02:02:32 +02:00
snprintf (Anchor1Nick,sizeof (Anchor1Nick),
"<form method=\"post\" action=\"%s/%s\" id=\"%s\">"
"%s"
"<input type=\"hidden\" name=\"usr\" value=\"",
Cfg_URL_SWAD_CGI,
2018-12-08 16:43:13 +01:00
Lan_STR_LANG_ID[Gbl.Prefs.Language],
2018-10-18 02:02:32 +02:00
Gbl.Usrs.Me.Logged ? Gbl.Form.UniqueId :
Gbl.Form.Id,
ParamsStr);
2016-01-26 14:23:16 +01:00
Anchor1NickLength = strlen (Anchor1Nick);
2017-01-28 15:58:46 +01:00
if ((Links[NumLinks].Anchor1Nick = (char *) malloc (Anchor1NickLength + 1)) == NULL)
2018-10-18 20:06:54 +02:00
Lay_NotEnoughMemoryExit ();
2017-01-17 03:10:43 +01:00
strcpy (Links[NumLinks].Anchor1Nick,Anchor1Nick);
2016-01-26 14:23:16 +01:00
Links[NumLinks].Anchor1NickLength = Anchor1NickLength;
/* Store second part of anchor */
2018-10-18 02:02:32 +02:00
snprintf (Anchor2Nick,sizeof (Anchor2Nick),
"\">"
"<a href=\"\""
" onclick=\"document.getElementById('%s').submit();"
"return false;\">",
Gbl.Usrs.Me.Logged ? Gbl.Form.UniqueId :
Gbl.Form.Id);
2016-01-26 14:23:16 +01:00
Anchor2NickLength = strlen (Anchor2Nick);
2017-01-28 15:58:46 +01:00
if ((Links[NumLinks].Anchor2Nick = (char *) malloc (Anchor2NickLength + 1)) == NULL)
2018-10-18 20:06:54 +02:00
Lay_NotEnoughMemoryExit ();
2017-01-17 03:10:43 +01:00
strcpy (Links[NumLinks].Anchor2Nick,Anchor2Nick);
2016-01-26 14:23:16 +01:00
Links[NumLinks].Anchor2NickLength = Anchor2NickLength;
AnchorNickTotalLength = Anchor1NickLength + Anchor2NickLength + Anchor3NickLength;
2016-01-26 13:45:45 +01:00
2016-01-26 14:23:16 +01:00
LengthVisibleLink = Links[NumLinks].NumActualBytes;
2016-01-24 22:08:23 +01:00
if (NumLinks == 0)
2016-01-26 12:14:16 +01:00
Links[NumLinks].AddedLengthUntilHere = AnchorNickTotalLength + LengthVisibleLink;
2016-01-24 22:08:23 +01:00
else
2016-01-26 12:14:16 +01:00
Links[NumLinks].AddedLengthUntilHere = Links[NumLinks - 1].AddedLengthUntilHere +
AnchorNickTotalLength + LengthVisibleLink;
2016-01-24 22:08:23 +01:00
2016-01-26 13:45:45 +01:00
/* Increment number of found links */
2016-01-24 22:08:23 +01:00
NumLinks++;
if (NumLinks == MAX_LINKS)
break;
}
}
/* The next char is not the start of URL or nickname */
2016-01-25 11:43:14 +01:00
else // Character distinct to 'h' or '@'
2014-12-01 23:55:08 +01:00
PtrSrc++;
2016-01-26 14:23:16 +01:00
/**********************************************************************/
2016-01-24 22:08:23 +01:00
/***** If there are one or more links (URLs or nicknames) in text *****/
2016-01-26 14:23:16 +01:00
/**********************************************************************/
2016-01-24 22:08:23 +01:00
if (NumLinks)
2014-12-01 23:55:08 +01:00
{
2016-01-24 22:08:23 +01:00
/***** Insert links from end to start of text,
only if there is enough space available in text *****/
2016-01-26 12:14:16 +01:00
TxtLengthWithInsertedAnchors = TxtLength + Links[NumLinks - 1].AddedLengthUntilHere;
2016-01-24 22:08:23 +01:00
if (TxtLengthWithInsertedAnchors <= MaxLength)
for (NumLink = NumLinks - 1;
NumLink >= 0;
NumLink--)
2014-12-01 23:55:08 +01:00
{
2016-01-24 22:08:23 +01:00
IsNickname = (*(Links[NumLink].PtrStart) == '@');
2016-01-26 14:23:16 +01:00
/***** Step 1: Move forward the text after the link (URL or nickname)
(it's mandatory to do the copy in reverse order
to avoid overwriting source) *****/
2014-12-01 23:55:08 +01:00
for (i = 0,
2016-01-24 22:08:23 +01:00
PtrSrc = (NumLink == NumLinks - 1) ? Txt + TxtLength :
Links[NumLink + 1].PtrStart - 1,
2016-01-26 12:14:16 +01:00
PtrDst = PtrSrc + Links[NumLink].AddedLengthUntilHere,
2016-01-24 22:08:23 +01:00
Length = PtrSrc - Links[NumLink].PtrEnd;
2014-12-01 23:55:08 +01:00
i < Length;
i++)
*PtrDst-- = *PtrSrc--;
2016-01-26 14:23:16 +01:00
/***** Step 2: Insert ANCHOR_3_NICK or ANCHOR_3_URL *****/
2016-01-26 13:45:45 +01:00
if (IsNickname)
{
Length = Anchor3NickLength;
2016-01-26 14:23:16 +01:00
PtrSrc = ANCHOR_3_NICK + Length - 1;
2016-01-26 13:45:45 +01:00
}
else
{
Length = Anchor3URLLength;
PtrSrc = ANCHOR_3_URL + Length - 1;
}
for (i = 0;
i < Length;
i++)
2014-12-01 23:55:08 +01:00
*PtrDst-- = *PtrSrc--;
2016-01-26 14:23:16 +01:00
/***** Step 3: Move forward the link (URL or nickname)
to be shown on screen *****/
2016-01-24 22:08:23 +01:00
if (IsNickname ||
Links[NumLink].NumActualBytes <= MaxCharsURLOnScreen)
2014-12-01 23:55:08 +01:00
{
2016-01-24 22:08:23 +01:00
NumBytesToShow = Links[NumLink].NumActualBytes;
2016-01-26 12:14:16 +01:00
PtrSrc = Links[NumLink].PtrEnd; // PtrSrc must point to end of complete nickname
2014-12-01 23:55:08 +01:00
}
else // If URL is too long to be displayed ==> short it
{
/* Make a copy of this URL */
2016-01-24 22:08:23 +01:00
NumBytesToCopy = (Links[NumLink].NumActualBytes < MAX_BYTES_LIMITED_URL) ? Links[NumLink].NumActualBytes :
MAX_BYTES_LIMITED_URL;
2017-01-17 03:10:43 +01:00
strncpy (LimitedURL,Links[NumLink].PtrStart,NumBytesToCopy);
LimitedURL[NumBytesToCopy] = '\0';
2014-12-01 23:55:08 +01:00
/* Limit the length of the copy */
NumBytesToShow = Str_LimitLengthHTMLStr (LimitedURL,MaxCharsURLOnScreen);
2016-01-26 12:14:16 +01:00
PtrSrc = LimitedURL + NumBytesToShow - 1; // PtrSrc must point to end of limited URL
2014-12-01 23:55:08 +01:00
}
for (i = 0;
i < NumBytesToShow;
i++)
*PtrDst-- = *PtrSrc--;
2016-01-26 14:23:16 +01:00
/***** Step 4: Insert Anchor2Nick or ANCHOR_2_URL *****/
2016-01-24 22:08:23 +01:00
if (IsNickname)
2016-01-25 00:19:21 +01:00
{
2016-01-26 14:23:16 +01:00
Length = Links[NumLink].Anchor2NickLength;
PtrSrc = Links[NumLink].Anchor2Nick + Length - 1;
2016-01-25 00:19:21 +01:00
}
2016-01-24 22:08:23 +01:00
else
2016-01-25 00:19:21 +01:00
{
Length = Anchor2URLLength;
2016-01-26 13:45:45 +01:00
PtrSrc = ANCHOR_2_URL + Length - 1;
2016-01-25 00:19:21 +01:00
}
for (i = 0;
i < Length;
i++)
*PtrDst-- = *PtrSrc--;
2016-01-24 22:08:23 +01:00
2016-01-26 14:23:16 +01:00
/***** Step 5: Insert link into directive A
(it's mandatory to do the copy in reverse order
to avoid overwriting source URL or nickname) *****/
2016-01-24 22:08:23 +01:00
for (i = 0, PtrSrc = Links[NumLink].PtrEnd;
i < Links[NumLink].NumActualBytes;
2014-12-01 23:55:08 +01:00
i++)
*PtrDst-- = *PtrSrc--;
2016-01-26 14:23:16 +01:00
/***** Step 6: Insert Anchor1Nick or ANCHOR_1_URL *****/
2016-01-24 22:08:23 +01:00
if (IsNickname)
2016-01-25 00:19:21 +01:00
{
2016-01-26 14:23:16 +01:00
Length = Links[NumLink].Anchor1NickLength;
PtrSrc = Links[NumLink].Anchor1Nick + Length - 1;
2016-01-25 00:19:21 +01:00
}
2016-01-24 22:08:23 +01:00
else
2016-01-25 00:19:21 +01:00
{
Length = Anchor1URLLength;
2016-01-26 13:45:45 +01:00
PtrSrc = ANCHOR_1_URL + Length - 1;
2016-01-25 00:19:21 +01:00
}
for (i = 0;
i < Length;
i++)
*PtrDst-- = *PtrSrc--;
2014-12-01 23:55:08 +01:00
}
}
2016-01-26 13:45:45 +01:00
2016-01-26 14:23:16 +01:00
/***********************************/
2016-01-26 13:45:45 +01:00
/***** Free memory for anchors *****/
2016-01-26 14:23:16 +01:00
/***********************************/
2016-01-26 13:45:45 +01:00
for (NumLink = 0;
NumLink < NumLinks;
NumLink++)
{
if (Links[NumLink].Anchor1Nick)
free ((void *) Links[NumLink].Anchor1Nick);
if (Links[NumLink].Anchor2Nick)
free ((void *) Links[NumLink].Anchor2Nick);
}
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
/** Get next ASCII character from a string converting &#number; to character */
/*****************************************************************************/
// Returns number of char analyzed
2016-05-01 12:48:22 +02:00
static unsigned Str_GetNextASCIICharFromStr (const char *Ptr,unsigned char *Ch)
2014-12-01 23:55:08 +01:00
{
unsigned NumChars;
unsigned Num;
if (*Ptr == '\0')
{
*Ch = '\0';
return 0;
}
else if (*Ptr == '&') // It's a &#num; character?
{
Ptr++;
if (*Ptr == '#')
{
Ptr++;
for (NumChars = 2, Num = 0;
*Ptr >= '0' && *Ptr <= '9';
Ptr++, NumChars++)
2017-01-19 03:15:35 +01:00
if (Num < 100000) // To avoid overflow
2014-12-01 23:55:08 +01:00
Num = Num * 10 + (unsigned) (*Ptr - '0');
if (*Ptr == ';') // &#num; found
{
NumChars++;
switch (Num)
{
case 32: *Ch = ' '; return NumChars;
case 33: *Ch = '!'; return NumChars;
case 34: *Ch = '\"'; return NumChars;
case 35: *Ch = '#'; return NumChars;
case 36: *Ch = '$'; return NumChars;
case 37: *Ch = '%'; return NumChars;
case 38: *Ch = '&'; return NumChars;
case 39: *Ch = '\''; return NumChars;
case 40: *Ch = '('; return NumChars;
case 41: *Ch = ')'; return NumChars;
case 42: *Ch = '*'; return NumChars;
case 43: *Ch = '+'; return NumChars;
case 44: *Ch = ','; return NumChars;
case 45: *Ch = '-'; return NumChars;
case 46: *Ch = '.'; return NumChars;
case 47: *Ch = '/'; return NumChars;
case 58: *Ch = ':'; return NumChars;
case 59: *Ch = ';'; return NumChars;
case 60: *Ch = ' '; return NumChars; // '<'
case 61: *Ch = '='; return NumChars;
case 62: *Ch = ' '; return NumChars; // '>'
case 63: *Ch = '?'; return NumChars;
case 64: *Ch = '@'; return NumChars;
case 91: *Ch = '['; return NumChars;
case 92: *Ch = '\\'; return NumChars;
case 93: *Ch = ']'; return NumChars;
case 94: *Ch = '^'; return NumChars;
case 95: *Ch = '_'; return NumChars;
case 96: *Ch = '`'; return NumChars;
case 123: *Ch = '{'; return NumChars;
case 124: *Ch = '|'; return NumChars;
case 125: *Ch = '}'; return NumChars;
case 126: *Ch = '~'; return NumChars;
2017-01-19 03:15:35 +01:00
default: *Ch = '?'; return NumChars; // Unknown character
2014-12-01 23:55:08 +01:00
}
}
else
{
2016-05-01 12:48:22 +02:00
*Ch = (unsigned char) *Ptr;
2014-12-01 23:55:08 +01:00
return NumChars;
}
}
else
{
2016-05-01 12:48:22 +02:00
*Ch = (unsigned char) *Ptr;
2014-12-01 23:55:08 +01:00
return 2;
}
}
else
{
2016-05-01 12:48:22 +02:00
*Ch = (unsigned char) *Ptr;
2014-12-01 23:55:08 +01:00
return 1;
}
}
/*****************************************************************************/
/*************** Limit number of chars on screen of a string *****************/
/*****************************************************************************/
// Returns length of resulting string in bytes (a special char counts for several bytes)
size_t Str_LimitLengthHTMLStr (char *Str,size_t MaxCharsOnScreen)
{
char *Ptr;
size_t NumCharsOnScreen;
size_t Length;
2017-01-19 03:15:35 +01:00
size_t LengthHTMLEntity;
2014-12-01 23:55:08 +01:00
if (MaxCharsOnScreen < 3)
MaxCharsOnScreen = 3; // Length of "..."
/***** Calculate length counting "&...;" as one character *****/
for (Ptr = Str, NumCharsOnScreen = 0, Length = 0;
*Ptr;
Ptr++, NumCharsOnScreen++, Length++)
2017-01-19 03:15:35 +01:00
/* Check if an HTML entity is present */
if (*Ptr == '&') // Possible HTML entity
if ((LengthHTMLEntity = Str_FindHTMLEntity (Ptr)))
{
/* if Ptr points to &ntilde; ==> Length = 8 */
Ptr += LengthHTMLEntity - 1; // Now Ptr point to ';'
Length += LengthHTMLEntity - 1;
}
2014-12-01 23:55:08 +01:00
if (NumCharsOnScreen <= MaxCharsOnScreen) // Don't limit string
return Length;
/***** Limit length of string *****/
for (Ptr = Str, NumCharsOnScreen = 0, Length = 0;
*Ptr;
Ptr++, NumCharsOnScreen++, Length++)
{
if (NumCharsOnScreen == MaxCharsOnScreen - 3)
{
*Ptr++ = '.';
*Ptr++ = '.';
*Ptr++ = '.';
*Ptr = '\0'; // ...limit length...
Length += 3;
break;
}
2017-01-19 03:15:35 +01:00
/* Check if an HTML entity or directive is present */
if (*Ptr == '&') // Possible HTML entity
{
if ((LengthHTMLEntity = Str_FindHTMLEntity (Ptr)))
{
/* if Ptr points to &ntilde; ==> Length = 8 */
Ptr += LengthHTMLEntity - 1; // Now Ptr point to ';'
Length += LengthHTMLEntity - 1;
}
}
else if (*Ptr == '<') // HTML directive "<...>"
2014-12-01 23:55:08 +01:00
for (Ptr++, Length++;
*Ptr && *Ptr != '>';
2017-01-19 03:15:35 +01:00
Ptr++, Length++); // While not end of HTML directive "<...>"
2014-12-01 23:55:08 +01:00
}
return Length;
}
2017-01-19 03:15:35 +01:00
/*****************************************************************************/
/******** Return the length of a possible HTML entity inside a string ********/
/*****************************************************************************/
// For example, if Ptr points to "&ntilde;..." or "&#38754;...", return 8
// If Ptr points to no HTML entity, return 0
static unsigned Str_FindHTMLEntity (const char *Ptr)
{
size_t Length = 0;
char Ch;
2017-01-19 18:32:28 +01:00
/***** The first character must be '&' *****/
2017-01-19 03:15:35 +01:00
if (Ptr[Length] != '&')
return 0; // No HTML entity found
2017-01-19 18:32:28 +01:00
/***** The second character can be '#' *****/
2017-01-19 03:15:35 +01:00
Length++;
if (Ptr[Length] == '#')
/* Go to third character */
Length++;
2017-01-19 18:32:28 +01:00
/***** Now one or more alphanumeric characters are expected *****/
2017-01-19 03:15:35 +01:00
/* Check second/third character */
Ch = Ptr[Length];
if (!((Ch >= '0' && Ch <= '9') ||
(Ch >= 'a' && Ch <= 'z') ||
(Ch >= 'A' && Ch <= 'Z')))
return 0; // No HTML entity found
/* Go to first non alphanumeric character */
do
{
Length++;
Ch = Ptr[Length];
}
while ((Ch >= '0' && Ch <= '9') ||
(Ch >= 'a' && Ch <= 'z') ||
(Ch >= 'A' && Ch <= 'Z'));
/***** An HTML entity must end by ';' *****/
return (Ptr[Length] == ';') ? Length + 1 : // HTML entity found (return Length including the final ';')
0; // No HTML entity found
}
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
/**************** Check if a URL adreess looks as** valid ********************/
/*****************************************************************************/
/*
bool Str_URLLooksValid (const char *URL)
{
***** If it's a NULL pointer *****
if (!URL)
return false;
***** If it's the empty string *****
if (!URL[0])
return false;
***** Check if start by http:// or https:// *****
if (!strncasecmp (URL,"http://",7) || !strncasecmp (URL,"https://",8))
return (bool) (strchr (URL,(int) '.') != NULL); // There is any . in the URL
else // There's no http:// nor https://
return false;
}
*/
/*****************************************************************************/
/***** Convert a string to title: first uppercase and the rest lowercase *****/
/*****************************************************************************/
void Str_ConvertToTitleType (char *Str)
{
2017-11-13 16:34:33 +01:00
char Ch;
char *Ptr;
char *Ptr2;
unsigned LengthStr;
2014-12-01 23:55:08 +01:00
bool FirstLetter = true;
for (Ptr = Str;
*Ptr;
Ptr++)
{
Ch = *Ptr;
if (isspace ((int) Ch) ||
2015-03-20 14:30:28 +01:00
Ch == '\xA0' || // Unicode translation for &nbsp;
2017-11-13 16:34:33 +01:00
Ch == '-' ||
Ch == '(' ||
Ch == ')' ||
Ch == ',' ||
// Ch == ';' || // Do not start a new word on ';'.
// Example: akto&#287;an should be converted to Akto&#287;an
Ch == '.' ||
Ch == ':' ||
Ch == '<EFBFBD>' ||
Ch == '<EFBFBD>')
2014-12-01 23:55:08 +01:00
FirstLetter = true;
else
{
if (FirstLetter)
{
/* Check if it's "de", "del", "la", "y" */ // This should be internationalized!!!!!
for (Ptr2 = Ptr, LengthStr = 0;
*Ptr2;
Ptr2++, LengthStr++)
2015-03-20 14:30:28 +01:00
if (isspace ((int) *Ptr2) ||
*Ptr2 == '\xA0') // Unicode translation for &nbsp;
2014-12-01 23:55:08 +01:00
break;
if (LengthStr == 1)
{
if (!strncasecmp (Ptr,"y",1))
FirstLetter = false;
}
else if (LengthStr == 2)
{
if (!strncasecmp (Ptr,"de",2))
FirstLetter = false;
else if (!strncasecmp (Ptr,"la",2))
FirstLetter = false;
}
else if (LengthStr == 3)
if (!strncasecmp (Ptr,"del",3))
FirstLetter = false;
}
if (FirstLetter)
{
*Ptr = Str_ConvertToUpperLetter (*Ptr);
FirstLetter = false;
}
else
*Ptr = Str_ConvertToLowerLetter (*Ptr);
}
}
}
/*****************************************************************************/
/*** Changes a string to made it comparable with others (removing tildes) ****/
/*****************************************************************************/
void Str_ConvertToComparable (char *Str)
{
char *Ptr;
for (Ptr = Str;
*Ptr;
Ptr++)
{
*Ptr = Str_ConvertToLowerLetter (*Ptr);
switch (*Ptr)
{
case '<EFBFBD>': *Ptr = 'a'; break;
case '<EFBFBD>': *Ptr = 'e'; break;
case '<EFBFBD>': *Ptr = 'i'; break;
case '<EFBFBD>': *Ptr = 'o'; break;
case '<EFBFBD>': *Ptr = 'u'; break;
case '<EFBFBD>': *Ptr = 'a'; break;
case '<EFBFBD>': *Ptr = 'e'; break;
case '<EFBFBD>': *Ptr = 'i'; break;
case '<EFBFBD>': *Ptr = 'o'; break;
case '<EFBFBD>': *Ptr = 'u'; break;
}
}
}
/*****************************************************************************/
/********************** Convert a string to uppercase ************************/
/*****************************************************************************/
char *Str_ConvertToUpperText (char *Str)
{
char *Ptr;
for (Ptr = Str;
*Ptr;
Ptr++)
*Ptr = Str_ConvertToUpperLetter (*Ptr);
return Str;
}
/*****************************************************************************/
/********************** Convert a string to lowercase ************************/
/*****************************************************************************/
char *Str_ConvertToLowerText (char *Str)
{
char *Ptr;
for (Ptr = Str;
*Ptr;
Ptr++)
*Ptr = Str_ConvertToLowerLetter (*Ptr);
return Str;
}
/*****************************************************************************/
/*********************** Convert a character to uppercase ********************/
/*****************************************************************************/
char Str_ConvertToUpperLetter (char Ch)
{
switch (Ch)
{
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
default: return (char) toupper ((int) Ch);
}
}
/*****************************************************************************/
/*********************** Convert a character to lowercase ********************/
/*****************************************************************************/
char Str_ConvertToLowerLetter (char Ch)
{
switch (Ch)
{
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
case '<EFBFBD>': return '<EFBFBD>';
default: return (char) tolower ((int) Ch);
}
}
2015-03-11 14:35:33 +01:00
/*****************************************************************************/
/******** Write a number in floating point with the correct accuracy *********/
/*****************************************************************************/
2016-10-03 14:12:01 +02:00
void Str_WriteFloatNum (FILE *FileDst,float Number)
2015-03-11 14:35:33 +01:00
{
double IntegerPart;
double FractionaryPart;
char *Format;
FractionaryPart = modf ((double) Number,&IntegerPart);
if (FractionaryPart == 0.0)
2016-10-03 14:12:01 +02:00
fprintf (FileDst,"%.0f",IntegerPart);
2015-03-11 14:35:33 +01:00
else
{
2019-03-05 14:13:27 +01:00
if (IntegerPart != 0.0)
Format = "%.1f";
2019-03-06 10:13:39 +01:00
else if (FractionaryPart >= 0.095)
2019-03-05 14:13:27 +01:00
Format = "%.2f";
2019-03-23 13:10:31 +01:00
else if (FractionaryPart >= 0.0095)
2019-03-05 14:13:27 +01:00
Format = "%.3f";
2019-03-23 13:10:31 +01:00
else if (FractionaryPart >= 0.00095)
2019-03-05 14:13:27 +01:00
Format = "%.4f";
2019-03-23 13:10:31 +01:00
else if (FractionaryPart >= 0.000095)
2019-03-05 14:13:27 +01:00
Format = "%.5f";
2019-03-23 13:10:31 +01:00
else if (FractionaryPart >= 0.0000095)
2019-03-05 14:13:27 +01:00
Format = "%.6f";
2015-03-11 14:35:33 +01:00
else
Format = "%e";
2016-10-03 14:12:01 +02:00
fprintf (FileDst,Format,Number);
2015-03-11 14:35:33 +01:00
}
}
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
/********** Convert a string that holds a number in floating comma ***********/
/********** to another in floating point changing commas to points ***********/
/*****************************************************************************/
void Str_ConvertStrFloatCommaToStrFloatPoint (char *Str)
{
for (;
*Str;
Str++)
if (*Str == ',')
*Str = '.';
}
/*****************************************************************************/
/************** Read a number in floating point from a string ****************/
/*****************************************************************************/
float Str_GetFloatNumFromStr (const char *Str)
{
float Num;
if (Str)
{
2016-06-04 14:21:01 +02:00
Str_SetDecimalPointToUS (); // To get the decimal point as a dot
2014-12-01 23:55:08 +01:00
if (sscanf (Str,"%f",&Num) != 1)
Lay_ShowErrorAndExit ("Bad floating point format.");
2016-06-04 14:21:01 +02:00
Str_SetDecimalPointToLocal (); // Return to local system
2014-12-01 23:55:08 +01:00
}
else // Str == NULL
Num = 0.0;
return Num;
}
/*****************************************************************************/
2016-06-04 14:21:01 +02:00
/**** Change decimal point to US system in order to get/print it as a dot ****/
/*****************************************************************************/
void Str_SetDecimalPointToUS (void)
{
if (!setlocale (LC_NUMERIC,"en_US.utf8")) // To get/print the floating point as a dot
if (Gbl.Layout.HTMLStartWritten)
2019-02-16 18:25:41 +01:00
Ale_ShowAlert (Ale_ERROR,"Can not set locale to en_US.");
2016-06-04 14:21:01 +02:00
}
/*****************************************************************************/
/****************** Change decimal point to local system *********************/
/*****************************************************************************/
void Str_SetDecimalPointToLocal (void)
{
// TODO: this should be internationalized!!!!!!!
if (!setlocale (LC_NUMERIC,"es_ES.utf8")) // Return to local system
if (Gbl.Layout.HTMLStartWritten)
2019-02-16 18:25:41 +01:00
Ale_ShowAlert (Ale_ERROR,"Can not set locale to es_ES.");
2016-06-04 14:21:01 +02:00
}
/*****************************************************************************/
2014-12-01 23:55:08 +01:00
/*************** Add a string to a Query, changing ' for &#39; ***************/
/*****************************************************************************/
void Str_AddStrToQuery (char *Query,const char *Str,size_t SizeOfQuery)
{
size_t LengthOfQuery = strlen (Query);
size_t SizeOfQuery_6 = SizeOfQuery - 6;
size_t SizeOfQuery_2 = SizeOfQuery - 2;
// Query can not have a ' character
for (;
*Str;
Str++)
if (*Str == '\'') // It's a ' ...
{
if (LengthOfQuery > SizeOfQuery_6)
break;
Query[LengthOfQuery++] = '&';
Query[LengthOfQuery++] = '#';
Query[LengthOfQuery++] = '3';
Query[LengthOfQuery++] = '9';
Query[LengthOfQuery++] = ';'; // ...add the HTML special character &#39;
}
else // It's another character...
{
if (LengthOfQuery > SizeOfQuery_2)
break;
Query[LengthOfQuery++] = *Str; // ...add the character without changes
}
Query[LengthOfQuery] = '\0';
}
/*****************************************************************************/
/******************** Change the format of a string **************************/
/*****************************************************************************/
/*
ChangeFrom can be:
Str_FROM_FORM
Str_FROM_TEXT
Str_FROM_HTML
ChangeTo can be:
Str_DONT_CHANGE
Str_TO_TEXT
2015-04-07 21:44:24 +02:00
Str_TO_MARKDOWN
2014-12-01 23:55:08 +01:00
Str_TO_HTML
Str_TO_RIGOROUS_HTML
For example the string "Nueva++de+San+Ant%F3n"
is converted to:
"Nueva &nbsp;de San Ant<6E>n" if ChangeTo == Str_TO_RIGOROUS_HTML
"Nueva de San Ant<6E>n" if ChangeTo == Str_TO_HTML
"Nueva de San Ant<6E>n" if ChangeTo == Str_TO_TEXT
*/
2017-03-08 01:21:21 +01:00
#define Str_MAX_BYTES_SPECIAL_CHAR (256 - 1)
2017-01-15 22:58:26 +01:00
2014-12-01 23:55:08 +01:00
void Str_ChangeFormat (Str_ChangeFrom_t ChangeFrom,Str_ChangeTo_t ChangeTo,
char *Str,size_t MaxLengthStr,bool RemoveLeadingAndTrailingSpaces)
{
char *StrDst;
char *PtrSrc;
char *PtrDst;
2016-04-08 00:50:30 +02:00
unsigned char Ch;
2014-12-01 23:55:08 +01:00
unsigned int SpecialChar;
size_t LengthSpecStrSrc = 0;
size_t LengthSpecStrDst;
unsigned long LengthStrDst = 0;
unsigned NumSpacesTab;
unsigned i;
unsigned NumPrintableCharsFromReturn = 0; // To substitute tabs for spaces
bool IsSpecialChar = false;
bool ThereIsSpaceChar = true; // Indicates if the character before was a space. Set to true to respect the initial spaces.
2017-03-08 01:21:21 +01:00
char StrSpecialChar[Str_MAX_BYTES_SPECIAL_CHAR + 1];
2014-12-01 23:55:08 +01:00
/*
2017-06-04 14:22:04 +02:00
if (Gbl.Usrs.Me.Roles.LoggedRole == Rol_SYS_ADM)
2014-12-01 23:55:08 +01:00
{
2018-10-16 23:08:04 +02:00
snprintf (Gbl.Alert.Txt,sizeof (Gbl.Alert.Txt),
2018-10-16 21:56:01 +02:00
"Str_ChangeFormat (&quot;%s&quot;)",
Str);
2017-05-10 10:25:01 +02:00
Lay_ShowAlert (Lay_INFO,Gbl.Alert.Txt);
2014-12-01 23:55:08 +01:00
}
*/
if (ChangeTo != Str_DONT_CHANGE)
{
/***** Allocate memory for a destination string where to do the changes *****/
2017-01-17 03:10:43 +01:00
if ((StrDst = (char *) malloc (MaxLengthStr + 1)) == NULL)
2018-10-18 20:06:54 +02:00
Lay_NotEnoughMemoryExit ();
2014-12-01 23:55:08 +01:00
/***** Make the change *****/
for (PtrSrc = Str, PtrDst = StrDst;
*PtrSrc;)
{
2016-04-08 00:50:30 +02:00
Ch = (unsigned char) *PtrSrc;
2014-12-01 23:55:08 +01:00
switch (ChangeFrom)
{
case Str_FROM_FORM:
2016-10-21 00:38:34 +02:00
if (Gbl.ContentReceivedByCGI == Act_CONT_DATA)
2016-04-07 23:35:47 +02:00
// The form contained data and was sent with content type multipart/form-data
2016-04-08 00:50:30 +02:00
switch (Ch)
2016-04-07 23:35:47 +02:00
{
case 0x20: /* Space */
case 0x22: /* Change double comilla --> "&#34;" */
case 0x23: /* '#' */
case 0x26: /* Change '&' --> "&#38;" */
case 0x27: /* Change single comilla --> "&#39;" to avoid SQL code injection */
case 0x2C: /* ',' */
case 0x2F: /* '/' */
case 0x3A: /* ':' */
case 0x3B: /* ';' */
case 0x3C: /* '<' --> "&#60;" */
case 0x3E: /* '>' --> "&#62;" */
case 0x3F: /* '?' */
case 0x40: /* '@' */
case 0x5C: /* '\\' */
IsSpecialChar = true;
LengthSpecStrSrc = 1;
2016-04-08 00:50:30 +02:00
SpecialChar = (unsigned int) Ch;
break;
2016-04-07 23:35:47 +02:00
default:
2016-04-08 00:50:30 +02:00
if (Ch < 0x20 || Ch > 0x7F)
2016-04-07 23:35:47 +02:00
{
IsSpecialChar = true;
LengthSpecStrSrc = 1;
2016-04-08 00:50:30 +02:00
SpecialChar = (unsigned int) Ch;
2016-04-07 23:35:47 +02:00
}
else
{
IsSpecialChar = false;
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
}
break;
}
else // Gbl.ContentReceivedByCGI == Act_CONTENT_NORM
// The form contained text and was sent with content type application/x-www-form-urlencoded
2016-04-08 00:50:30 +02:00
switch (Ch)
2016-04-07 23:35:47 +02:00
{
case '+': /* Change every '+' to a space */
IsSpecialChar = true;
LengthSpecStrSrc = 1;
SpecialChar = 0x20;
break;
2017-03-08 03:48:23 +01:00
case '%': /* Change "%XX" --> "&#decimal_number;" (from 0 to 255) */
2016-04-07 23:35:47 +02:00
IsSpecialChar = true;
2017-03-08 03:48:23 +01:00
/* Some special characters, like a chinese character,
are received from a form in a format like this:
%26%2335753%3B --> %26 %23 3 5 7 5 3 %3B --> &#35753;
^ ^ ^
| | |
SpecialChar SpecialChar SpecialChar
Here one chinese character is converted
to 2 special chars + 5 normal chars + 1 special char,
and finally is stored as the following 8 bytes: &#35753;
*/
2017-01-28 15:58:46 +01:00
sscanf (PtrSrc + 1,"%2X",&SpecialChar);
2016-04-07 23:35:47 +02:00
LengthSpecStrSrc = 3;
break;
case 0x27: /* Change single comilla --> "&#39;" to avoid SQL code injection */
case 0x5C: /* '\\' */
IsSpecialChar = true;
LengthSpecStrSrc = 1;
2016-04-08 00:50:30 +02:00
SpecialChar = (unsigned int) Ch;
2016-04-07 23:35:47 +02:00
break;
default:
IsSpecialChar = false;
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
}
2014-12-01 23:55:08 +01:00
break;
case Str_FROM_HTML:
case Str_FROM_TEXT:
2016-04-08 00:50:30 +02:00
switch (Ch)
2014-12-01 23:55:08 +01:00
{
2016-04-07 23:35:47 +02:00
case 0x20: /* Space */
case 0x27: /* Change single comilla --> "&#39;" to avoid SQL code injection */
case 0x5C: /* '\\' */
2015-04-07 21:44:24 +02:00
IsSpecialChar = true;
LengthSpecStrSrc = 1;
2016-04-08 00:50:30 +02:00
SpecialChar = (unsigned int) Ch;
2015-04-07 21:44:24 +02:00
break;
2014-12-01 23:55:08 +01:00
default:
2016-04-08 00:50:30 +02:00
if (Ch < 0x20)
2014-12-01 23:55:08 +01:00
{
IsSpecialChar = true;
LengthSpecStrSrc = 1;
2016-04-08 00:50:30 +02:00
SpecialChar = (unsigned int) Ch;
2014-12-01 23:55:08 +01:00
}
else
{
IsSpecialChar = false;
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
}
break;
}
break;
}
2016-04-08 00:50:30 +02:00
2014-12-01 23:55:08 +01:00
if (IsSpecialChar)
{
switch (SpecialChar)
{
case 0x09: /* Tab */
if (ChangeTo == Str_TO_RIGOROUS_HTML)
{
/* Change tab to spaces (so many spaces as left until the next multiple of 8) */
StrSpecialChar[0] = '\0';
NumSpacesTab = 8 - NumPrintableCharsFromReturn % 8;
// Insert a space to separate former string from &nbsp;
// This space is not printed on screen in HTML,
// but it is necessary to mark the end of a possible previous URL
2017-01-16 01:51:01 +01:00
Str_Concat (StrSpecialChar,
ThereIsSpaceChar ? "&nbsp;" :
" ", // The first space
2017-03-08 01:21:21 +01:00
Str_MAX_BYTES_SPECIAL_CHAR);
2014-12-01 23:55:08 +01:00
for (i = 1;
i < NumSpacesTab;
i++) // Rest of spaces, except the first
2017-01-16 01:51:01 +01:00
Str_Concat (StrSpecialChar,"&nbsp;", // Add a space
2017-03-08 01:21:21 +01:00
Str_MAX_BYTES_SPECIAL_CHAR);
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn += NumSpacesTab;
}
else
2017-01-15 22:58:26 +01:00
{
StrSpecialChar[0] = '\t';
StrSpecialChar[1] = '\0';
}
2014-12-01 23:55:08 +01:00
ThereIsSpaceChar = true;
break;
case 0x0A: /* \n */
2017-01-15 22:58:26 +01:00
if (ChangeTo == Str_TO_RIGOROUS_HTML)
Str_Copy (StrSpecialChar,"<br />",
2017-03-08 01:21:21 +01:00
Str_MAX_BYTES_SPECIAL_CHAR);
2017-01-15 22:58:26 +01:00
else
{
StrSpecialChar[0] = Str_LF[0];
StrSpecialChar[1] = '\0';
}
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn = 0;
ThereIsSpaceChar = true;
break;
case 0x0D: /* "%0D" --> "" */
2017-01-15 22:58:26 +01:00
if (ChangeTo == Str_TO_RIGOROUS_HTML)
StrSpecialChar[0] = '\0';
else
{
StrSpecialChar[0] = Str_CR[0];
StrSpecialChar[1] = '\0';
}
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn = 0;
ThereIsSpaceChar = true;
break;
case 0x20: /* Space */
2017-01-15 22:58:26 +01:00
if (ChangeTo == Str_TO_RIGOROUS_HTML && ThereIsSpaceChar)
Str_Copy (StrSpecialChar,"&nbsp;",
2017-03-08 01:21:21 +01:00
Str_MAX_BYTES_SPECIAL_CHAR);
2017-01-15 22:58:26 +01:00
else
{
StrSpecialChar[0] = ' ';
StrSpecialChar[1] = '\0';
}
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = true;
break;
case 0x22: /* "%22" --> "&#34;" (double comilla) */
2015-04-07 21:44:24 +02:00
if (ChangeTo == Str_TO_MARKDOWN)
{ // Escape sequence for database, two characters
StrSpecialChar[0] = '\\'; // 1. An inverted bar
StrSpecialChar[1] = '\"'; // 2. A double comilla
StrSpecialChar[2] = '\0'; // End of string
}
else
2018-10-18 02:02:32 +02:00
Str_Copy (StrSpecialChar,"&#34;", // Double comilla is stored as HTML code to avoid problems when displaying it
Str_MAX_BYTES_SPECIAL_CHAR);
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0x23: /* "%23" --> "#" */
2017-03-08 03:48:23 +01:00
StrSpecialChar[0] = '#'; // '#' must be converted to '#' to allow HTML entities like &#20998;
2017-01-15 22:58:26 +01:00
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
2017-01-19 03:15:35 +01:00
case 0x26: /* "%26" --> "&" */
StrSpecialChar[0] = '&'; // '&' must be converted to '&' to allow HTML entities like &#20998;
2017-01-15 22:58:26 +01:00
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0x27: /* "%27" --> "&#39;" (single comilla) */
2015-04-07 21:44:24 +02:00
if (ChangeTo == Str_TO_MARKDOWN)
{ // Escape sequence for database, two characters
StrSpecialChar[0] = '\\'; // 1. An inverted bar
StrSpecialChar[1] = '\''; // 2. A single comilla
StrSpecialChar[2] = '\0'; // End of string
}
else
2018-10-18 02:02:32 +02:00
Str_Copy (StrSpecialChar,"&#39;", // Single comilla is stored as HTML entity to avoid problem when querying database (SQL code injection)
Str_MAX_BYTES_SPECIAL_CHAR);
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0x2C: /* "%2C" --> "," */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = ',';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0x2F: /* "%2F" --> "/" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '/';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0x3A: /* "%3A" --> ":" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = ':';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0x3B: /* "%3B" --> ";" */
2017-03-08 03:48:23 +01:00
StrSpecialChar[0] = ';'; // ';' must be converted to ';' to allow HTML entities like &#20998;
2017-01-15 22:58:26 +01:00
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0x3C: /* "%3C" --> "&#60;" (<) */
2015-04-07 21:44:24 +02:00
if (ChangeTo == Str_TO_MARKDOWN)
2017-01-15 22:58:26 +01:00
{
StrSpecialChar[0] = '<';
StrSpecialChar[1] = '\0';
}
2015-04-07 21:44:24 +02:00
else
2017-01-15 22:58:26 +01:00
Str_Copy (StrSpecialChar,"&#60;", // "<" is stored as HTML code to avoid problems when displaying it
2017-03-08 01:21:21 +01:00
Str_MAX_BYTES_SPECIAL_CHAR);
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0x3E: /* "%3E" --> "&#62;" (>) */
2015-04-07 21:44:24 +02:00
if (ChangeTo == Str_TO_MARKDOWN)
2017-01-15 22:58:26 +01:00
{
StrSpecialChar[0] = '>';
StrSpecialChar[1] = '\0';
}
2015-04-07 21:44:24 +02:00
else
2017-01-15 22:58:26 +01:00
Str_Copy (StrSpecialChar,"&#62;", // ">" is stored as HTML code to avoid problems when displaying it
2017-03-08 01:21:21 +01:00
Str_MAX_BYTES_SPECIAL_CHAR);
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0x3F: /* "%3F" --> "?" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '?';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
2016-01-24 22:08:23 +01:00
case 0x40: /* "%40" --> "@" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '@';
StrSpecialChar[1] = '\0';
2016-01-24 22:08:23 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0x5C: /* "%5C" --> "&#92;" (\) */
2015-04-07 21:44:24 +02:00
if (ChangeTo == Str_TO_MARKDOWN)
{ // Escape sequence for database, two characters
StrSpecialChar[0] = '\\'; // 1. An inverted bar
StrSpecialChar[1] = '\\'; // 2. An inverted bar
StrSpecialChar[2] = '\0'; // End of string
}
else
2017-01-15 22:58:26 +01:00
Str_Copy (StrSpecialChar,"&#92;", // "\" is stored as HTML code to avoid problems when displaying it
2017-03-08 01:21:21 +01:00
Str_MAX_BYTES_SPECIAL_CHAR);
2015-04-07 21:44:24 +02:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
2014-12-01 23:55:08 +01:00
case 0xC1: /* "%C1" --> "<22>" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '<EFBFBD>';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0xC9: /* "%C9" --> "<22>" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '<EFBFBD>';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0xCD: /* "%CD" --> "<22>" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '<EFBFBD>';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0xD3: /* "%D3" --> "<22>" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '<EFBFBD>';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0xDA: /* "%DA" --> "<22>" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '<EFBFBD>';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0xD1: /* "%D1" --> "<22>" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '<EFBFBD>';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0xC7: /* "%C7" --> "<22>" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '<EFBFBD>';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0xE1: /* "%E1" --> "<22>" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '<EFBFBD>';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0xE9: /* "%E9" --> "<22>" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '<EFBFBD>';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0xED: /* "%ED" --> "<22>" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '<EFBFBD>';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0xF3: /* "%F3" --> "<22>" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '<EFBFBD>';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0xFA: /* "%FA" --> "<22>" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '<EFBFBD>';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0xF1: /* "%F1" --> "<22>" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '<EFBFBD>';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
case 0xE7: /* "%E7" --> "<22>" */
2017-01-15 22:58:26 +01:00
StrSpecialChar[0] = '<EFBFBD>';
StrSpecialChar[1] = '\0';
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
default: /* The rest of special chars are stored as special code */
2018-10-18 02:02:32 +02:00
snprintf (StrSpecialChar,sizeof (StrSpecialChar),
(ChangeTo == Str_TO_TEXT ||
ChangeTo == Str_TO_MARKDOWN) ? "%c" :
"&#%u;",
SpecialChar);
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn++;
ThereIsSpaceChar = false;
break;
}
/* Compute length of destination special char */
LengthSpecStrDst = strlen (StrSpecialChar);
/* Check new length of destination string after the copy */
if (LengthStrDst + LengthSpecStrDst > MaxLengthStr)
Lay_ShowErrorAndExit ("Space allocated to string is full.");
/* Copy to appropiate place the special character string */
2017-01-17 03:10:43 +01:00
strncpy (PtrDst,StrSpecialChar,LengthSpecStrDst);
2014-12-01 23:55:08 +01:00
/* Increment pointer to character after ';' */
PtrSrc += LengthSpecStrSrc;
PtrDst += LengthSpecStrDst;
/* Increment length of destination string */
LengthStrDst += LengthSpecStrDst;
}
else // Not a special char ==> copy char from source to destination
{
/* Check new length of destination string after the copy */
if (LengthStrDst >= MaxLengthStr)
Lay_ShowErrorAndExit ("Space allocated to string is full.");
/* Copy char from source to destination and increment pointers */
*PtrDst++ = *PtrSrc++;
/* Increment length of destination string */
LengthStrDst++;
}
}
2017-01-17 03:10:43 +01:00
StrDst[LengthStrDst] = '\0';
2014-12-01 23:55:08 +01:00
/***** Copy destination string with changes to source string *****/
2017-01-17 03:10:43 +01:00
strncpy (Str,StrDst,LengthStrDst); // Str <-- StrDst
Str[LengthStrDst] = '\0';
2014-12-01 23:55:08 +01:00
/***** Free memory used for the destination string *****/
free ((void *) StrDst);
}
if (RemoveLeadingAndTrailingSpaces)
{
/***** Remove leading spaces *****/
Str_RemoveLeadingSpacesHTML (Str);
/***** Remove trailing spaces *****/
Str_RemoveTrailingSpacesHTML (Str);
}
}
/*****************************************************************************/
/****************** Remove the spaces iniciales of a string ******************/
/*****************************************************************************/
void Str_RemoveLeadingSpacesHTML (char *Str)
{
char *Ptr;
/***** Find the first character no space from left to right *****/
for (Ptr = Str;
*Ptr;
Ptr++)
{
/* If it's space ==> continue in the loop */
2015-03-20 14:30:28 +01:00
if (isspace ((int) *Ptr) ||
*Ptr == '\xA0') // Unicode translation for &nbsp;
2014-12-01 23:55:08 +01:00
continue;
/* Check forward if it's a <br> or <br /> */
if (*Ptr == '<')
{
Ptr++;
if (tolower ((int) *Ptr) != 'b')
{
Ptr--;
break;
}
if (!*Ptr)
break;
Ptr++;
if (tolower ((int) *Ptr) != 'r')
{
Ptr -= 2;
break;
}
if (!*Ptr)
break;
Ptr++;
if (*Ptr == '>')
continue; // It's <br>
if (*Ptr != ' ')
{
Ptr -= 3;
break;
}
if (!*Ptr)
break;
Ptr++;
if (*Ptr != '/')
{
Ptr -= 4;
break;
}
if (!*Ptr)
break;
Ptr++;
if (*Ptr != '>')
{
Ptr -= 5;
break;
}
continue; // It's <br />
}
/* Check forward if it's &nbsp; */
if (*Ptr == '&')
{
Ptr++;
if (tolower ((int) *Ptr) != 'n')
{
Ptr--;
break;
}
if (!*Ptr)
break;
Ptr++;
if (tolower ((int) *Ptr) != 'b')
{
Ptr -= 2;
break;
}
if (!*Ptr)
break;
Ptr++;
if (tolower ((int) *Ptr) != 's')
{
Ptr -= 3;
break;
}
if (!*Ptr)
break;
Ptr++;
if (tolower ((int) *Ptr) != 'p')
{
Ptr -= 4;
break;
}
if (!*Ptr)
break;
Ptr++;
if (*Ptr != ';')
{
Ptr -= 5;
break;
}
/* It's &nbsp; */
continue;
}
/* It's not space */
break;
}
/***** Ptr is the first character no space *****/
if (Ptr != Str)
{
/* Copy string pointed by Ptr to Str.
Do not use strcpy because according to strcpy manual:
"The source and destination strings should not overlap,
as the behavior is undefined" */
while (*Ptr)
*Str++ = *Ptr++;
*Str = '\0';
}
}
/*****************************************************************************/
/******************* Remove the trailing spaces of a string ******************/
/*****************************************************************************/
void Str_RemoveTrailingSpacesHTML (char *Str)
{
char *Ptr;
/* Find the first not space character starting at the end */
for (Ptr = Str + strlen (Str) - 1;
Ptr >= Str;
Ptr--)
{
/* If it's space ==> continue in the loop */
2015-03-20 14:30:28 +01:00
if (isspace ((int) *Ptr) ||
*Ptr == '\xA0') // Unicode translation for &nbsp;
2014-12-01 23:55:08 +01:00
continue;
/* Check backward if it's <br> or <br /> */
if (*Ptr == '>')
{
if (Ptr == Str)
break;
Ptr--;
if (*Ptr == '/') // Possible <br />
{
if (Ptr == Str)
break;
Ptr--;
if (*Ptr != ' ')
{
Ptr += 2;
break;
}
if (Ptr == Str)
break;
Ptr--;
if (tolower ((int) *Ptr) != 'r')
{
Ptr += 3;
break;
}
if (Ptr == Str)
break;
Ptr--;
if (tolower ((int) *Ptr) != 'b')
{
Ptr += 4;
break;
}
if (Ptr == Str)
break;
Ptr--;
if (*Ptr != '<')
{
Ptr += 5;
break;
}
/* It's <br /> */
continue;
}
else // Possible <br>
{
if (tolower ((int) *Ptr) != 'r')
{
Ptr++;
break;
}
if (Ptr == Str)
break;
Ptr--;
if (tolower ((int) *Ptr) != 'b')
{
Ptr += 2;
break;
}
if (Ptr == Str)
break;
Ptr--;
if (*Ptr != '<')
{
Ptr += 3;
break;
}
/* It's <br> */
continue;
}
}
/* Check backward if it's &nbsp; */
if (*Ptr == ';')
{
if (Ptr == Str)
break;
Ptr--;
if (tolower ((int) *Ptr) != 'p')
{
Ptr ++;
break;
}
if (Ptr == Str)
break;
Ptr--;
if (tolower ((int) *Ptr) != 's')
{
Ptr += 2;
break;
}
if (Ptr == Str)
break;
Ptr--;
if (tolower ((int) *Ptr) != 'b')
{
Ptr += 3;
break;
}
if (Ptr == Str)
break;
Ptr--;
if (tolower ((int) *Ptr) != 'n')
{
Ptr += 4;
break;
}
if (Ptr == Str)
break;
Ptr--;
if (*Ptr != '&')
{
Ptr += 5;
break;
}
/* It's &nbsp; */
continue;
}
/* It's not space */
break;
}
2017-01-28 15:58:46 +01:00
*(Ptr + 1) = '\0';
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
2015-10-07 19:55:13 +02:00
/********* Remove the leading zeros ('0', not '\0') from a string ************/
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
void Str_RemoveLeadingZeros (char *Str)
{
2015-10-07 19:55:13 +02:00
size_t NumLeadingZeros;
2014-12-01 23:55:08 +01:00
2015-10-07 19:55:13 +02:00
if (Str)
if (Str[0])
{
NumLeadingZeros = strspn (Str,"0");
if (NumLeadingZeros)
// Do not use strcpy / memcpy because the strings overlap
memmove (Str,&Str[NumLeadingZeros],
strlen (Str) - NumLeadingZeros + 1);
}
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
/***************** Delete @'s at the start of a string ***********************/
/*****************************************************************************/
2015-10-07 19:55:13 +02:00
#include "swad_database.h"
2014-12-01 23:55:08 +01:00
void Str_RemoveLeadingArrobas (char *Str)
{
2015-10-07 19:55:13 +02:00
size_t NumLeadingArrobas;
2014-12-01 23:55:08 +01:00
2015-10-07 19:55:13 +02:00
if (Str)
if (Str[0])
{
NumLeadingArrobas = strspn (Str,"@");
if (NumLeadingArrobas)
// Do not use strcpy / memcpy because the strings overlap
memmove (Str,&Str[NumLeadingArrobas],
strlen (Str) - NumLeadingArrobas + 1);
}
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
/*************** Find string in a HTML file skipping comments ****************/
/*****************************************************************************/
/*
Search (case insensitive) the string Str in HTML file FileSrc, skipping comments.
Returns true if found.
Returns false if not found.
*/
bool Str_FindStrInFile (FILE *FileSrc,const char *Str,Str_SkipHTMLComments_t SkipHTMLComments)
{
int i;
int Length = strlen (Str);
int Ch;
long CurPos;
for (;;)
{
/* Skip possible comments */
if ((Ch = Str_ReadCharAndSkipComments (FileSrc,SkipHTMLComments)) == EOF) // Set pointer to '<' if not comment, or to first character after comment if comment
return false;
CurPos = ftell (FileSrc);
if (Str_ConvertToLowerLetter ((char) Ch) == Str_ConvertToLowerLetter (Str[0])) // First char found
{
for (i = 1;
i < Length;
i++)
{
/* Skip possible comments */
if ((Ch = Str_ReadCharAndSkipComments (FileSrc,SkipHTMLComments)) == EOF) // Set pointer to '<' if not comment, or to first character after comment if comment
return false;
if (Str_ConvertToLowerLetter ((char) Ch) != Str_ConvertToLowerLetter (Str[i]))
break;
}
if (i == Length) // Found!
return true;
else // Not found, continue on next character
fseek (FileSrc,CurPos,SEEK_SET);
}
}
return false; // Not reached
}
/*****************************************************************************/
/******************* Search a string in a file backward **********************/
/*****************************************************************************/
/* The file queda posicionado in:
If found --> in the character anterior to the first of the string
If no found --> to the principio of the file */
bool Str_FindStrInFileBack (FILE *FileSrc,const char *Str,Str_SkipHTMLComments_t SkipHTMLComments)
{
int i;
int Length = strlen (Str);
int Ch;
int ChFinal;
long CurPos;
ChFinal = Str_ConvertToLowerLetter (Str[Length-1]);
if (fseek (FileSrc,-1L,SEEK_CUR)) // Go to the previous character
return false;
for (;;)
{
if ((Ch = Str_ReadCharAndSkipCommentsBackward (FileSrc,SkipHTMLComments)) == EOF) // Set pointer to '>' if not comment, or to character before start of comment if comment
return false;
CurPos = ftell (FileSrc);
if (Str_ConvertToLowerLetter ((char) Ch) == (char) ChFinal)
{
for (i = Length - 2;
i >= 0;
i--)
{
if (fseek (FileSrc,-2L,SEEK_CUR))
return false;
if ((Ch = Str_ReadCharAndSkipCommentsBackward (FileSrc,SkipHTMLComments)) == EOF) // Set pointer to '>' if not comment, or to character before start of comment if comment
return false;
if (Str_ConvertToLowerLetter ((char) Ch) != Str_ConvertToLowerLetter (Str[i]))
break;
}
if (i<0) // Found!
{
fseek (FileSrc,-1L,SEEK_CUR); // Move to start of found string in FileSrc
return true;
}
else // Not found, continue on next character
fseek (FileSrc,CurPos,SEEK_SET);
}
if (fseek (FileSrc,-2L,SEEK_CUR)) // Move to previous character
return false;
}
return false; // Not reached
}
/*****************************************************************************/
/************* Search a string in a file writing in FileTgt ******************/
/*****************************************************************************/
/*
Search in the file FileSrc the string Str without distinguish uppercase from
lowercase. Write in FileTgt what is read from the file FileSrc.
*/
bool Str_WriteUntilStrFoundInFileIncludingStr (FILE *FileTgt,FILE *FileSrc,const char *Str,Str_SkipHTMLComments_t SkipHTMLComments)
{
int i;
int StrLength = strlen (Str);
int Ch;
long CurPos1,CurPos2;
for (;;)
{
/* Skip possible comments */
if ((Ch = Str_ReadCharAndSkipCommentsWriting (FileSrc,FileTgt,SkipHTMLComments)) == EOF) // Set pointer to '<' if not comment, or to first character after comment if comment
return false;
CurPos1 = ftell (FileSrc);
fputc (Ch,FileTgt);
if (Str_ConvertToLowerLetter ((char) Ch) == Str_ConvertToLowerLetter (Str[0])) // Found first character
{
for (i = 1;
i < StrLength;
i++)
{
/* Skip possible comments */
if ((Ch = Str_ReadCharAndSkipComments (FileSrc,SkipHTMLComments)) == EOF) // Set pointer to '<' if not comment, or to first character after comment if comment
return false;
if (Str_ConvertToLowerLetter ((char) Ch) != Str_ConvertToLowerLetter (Str[i]))
break;
}
if (i == StrLength) // Found!
{
CurPos2 = ftell (FileSrc);
fseek (FileSrc,CurPos1,SEEK_SET);
while (ftell (FileSrc) < CurPos2)
{
Ch = fgetc (FileSrc);
fputc (Ch,FileTgt);
}
return true;
}
else // Not found, continue on next character
fseek (FileSrc,CurPos1,SEEK_SET);
}
}
return false; // Not reached
}
/*****************************************************************************/
/*********************** Skip HTML comments in FileSrc ***********************/
/*****************************************************************************/
// Set pointer to '<' if not comment, or to first character after comment if comment
// Returns last char read after comments
static int Str_ReadCharAndSkipComments (FILE *FileSrc,Str_SkipHTMLComments_t SkipHTMLComments)
{
int Ch;
2017-01-28 15:58:46 +01:00
char StrAux[1 + 1]; /* To check "!--" string */
2014-12-01 23:55:08 +01:00
Ch = fgetc (FileSrc);
if (SkipHTMLComments)
while (Ch == (int) '<')
{
/***** Check if "<!--" *****/
if (strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,1),"!"))
{
Str_FindStrInFileBack (FileSrc,"<",Str_NO_SKIP_HTML_COMMENTS); // Not a comment. Return to start of directive
return fgetc (FileSrc);
}
if (strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,1),"-"))
{
Str_FindStrInFileBack (FileSrc,"<",Str_NO_SKIP_HTML_COMMENTS); // Not a comment. Return to start of directive
return fgetc (FileSrc);
}
if (strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,1),"-"))
{
Str_FindStrInFileBack (FileSrc,"<",Str_NO_SKIP_HTML_COMMENTS); // Not a comment. Return to start of directive
return fgetc (FileSrc);
}
/***** It's a comment ==> skip comment *****/
Str_FindStrInFile (FileSrc,"-->",Str_NO_SKIP_HTML_COMMENTS);
/***** Get next character *****/
Ch = fgetc (FileSrc);
}
return Ch;
}
/*****************************************************************************/
/************* Skip HTML comments in FileSrc writing into FileTgt ************/
/*****************************************************************************/
// Set pointer to '<' if not comment, or to first character after comment if comment
// Returns last char read after comments
static int Str_ReadCharAndSkipCommentsWriting (FILE *FileSrc,FILE *FileTgt,Str_SkipHTMLComments_t SkipHTMLComments)
{
int Ch;
2017-01-28 15:58:46 +01:00
char StrAux[1 + 1]; /* To check "!--" string */
2014-12-01 23:55:08 +01:00
Ch = fgetc (FileSrc);
if (SkipHTMLComments)
while (Ch == (int) '<')
{
/***** Check if "<!--" *****/
if (strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,1),"!"))
{
Str_FindStrInFileBack (FileSrc,"<",Str_NO_SKIP_HTML_COMMENTS); // Not a comment. Return to start of directive
return fgetc (FileSrc);
}
if (strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,1),"-"))
{
Str_FindStrInFileBack (FileSrc,"<",Str_NO_SKIP_HTML_COMMENTS); // Not a comment. Return to start of directive
return fgetc (FileSrc);
}
if (strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,1),"-"))
{
Str_FindStrInFileBack (FileSrc,"<",Str_NO_SKIP_HTML_COMMENTS); // Not a comment. Return to start of directive
return fgetc (FileSrc);
}
/***** It's a comment ==> skip comment *****/
Str_FindStrInFileBack (FileSrc,"<",Str_NO_SKIP_HTML_COMMENTS); // Return to start of comment
Str_WriteUntilStrFoundInFileIncludingStr (FileTgt,FileSrc,"-->",Str_NO_SKIP_HTML_COMMENTS); // Skip comment in search, and write it to FileTgt
/***** Get next character *****/
Ch = fgetc (FileSrc);
}
return Ch;
}
/*****************************************************************************/
/******************************* Skip a comment ******************************/
/*****************************************************************************/
// Set pointer to '>' if not comment, or to character previous to start of comment if comment
// Returns char preceeding comments, of EOF if start of file reached
static int Str_ReadCharAndSkipCommentsBackward (FILE *FileSrc,Str_SkipHTMLComments_t SkipHTMLComments)
{
int Ch;
2017-01-28 15:58:46 +01:00
char StrAux[3 + 1]; /* To check "--" string */
2014-12-01 23:55:08 +01:00
Ch = fgetc (FileSrc);
if (SkipHTMLComments)
while (Ch == (int) '>')
{
/* Check if "-->" */
// Now: "<!--example of comment-->..."
// ^
if (fseek (FileSrc,-3L,SEEK_CUR)) // Go to start of possible "-->" */
return EOF;
// Now: "<!--example of comment-->..."
// ^
if (strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,3),"-->")) // Not a comment
return '>';
/***** It's a comment end *****/
Str_FindStrInFileBack (FileSrc,"<!--",Str_NO_SKIP_HTML_COMMENTS); // Skip comment backwards
if (fseek (FileSrc,-1L,SEEK_CUR)) // Return to character previous to '<'
return EOF;
/***** Get character previous to '<' *****/
Ch = fgetc (FileSrc);
}
return Ch;
}
/*****************************************************************************/
/****** Scan next string in file FileSrc until find </td> *******/
2015-02-27 13:12:57 +01:00
/****** ( skipping comments <!--...--> and directives <...> ) *******/
2014-12-01 23:55:08 +01:00
/****** and write string into Str (MaxLength characters as much) *******/
/*****************************************************************************/
char *Str_GetCellFromHTMLTableSkipComments (FILE *FileSrc,char *Str,int MaxLength)
{
long CurPos;
long PosNextTR;
long PosTD;
int i = 0;
int Ch;
2016-06-13 20:15:31 +02:00
bool EndCellFound = false;
bool DirectiveFound;
2015-02-27 13:12:57 +01:00
bool SpaceFound;
2017-01-28 15:58:46 +01:00
char StrAux[1 + 1]; // To find next "/td>" or "nbsp;"
2014-12-01 23:55:08 +01:00
Str[0] = '\0';
/***** Find next <td ...> inside current row (before next <tr) *****/
CurPos = ftell (FileSrc);
Str_FindStrInFile (FileSrc,"<tr",Str_NO_SKIP_HTML_COMMENTS);
PosNextTR = ftell (FileSrc) - 3;
fseek (FileSrc,CurPos,SEEK_SET);
Str_FindStrInFile (FileSrc,"<td",Str_NO_SKIP_HTML_COMMENTS);
PosTD = ftell (FileSrc);
if (PosTD > PosNextTR)
{
// <td not found in this row ==> leave current position unchanged
fseek (FileSrc,CurPos,SEEK_SET);
return Str;
}
Str_FindStrInFile (FileSrc,">",Str_NO_SKIP_HTML_COMMENTS);
2016-06-13 20:15:31 +02:00
for (EndCellFound = false;
!EndCellFound;
)
2014-12-01 23:55:08 +01:00
{
if ((Ch = Str_ReadCharAndSkipComments (FileSrc,Str_SKIP_HTML_COMMENTS)) == EOF) // Set pointer to '<' if not comment, or to first character after comment if comment
break;
/***** Skip directives except </td> *****/
2016-06-13 20:15:31 +02:00
DirectiveFound = (Ch == (int) '<');
if (DirectiveFound) // Start of directive, not a comment
2014-12-01 23:55:08 +01:00
{
/* Check if it's </td> */
2016-06-13 20:15:31 +02:00
if (!strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,1),"/")) // It's </
if (!strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,1),"t")) // It's </t
if (!strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,1),"d")) // It's </td
if (!strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,1),">")) // It's </td>
EndCellFound = true; // </td> found
if (!EndCellFound)
{
/* Skip directive */
Str_FindStrInFileBack (FileSrc,"<",Str_NO_SKIP_HTML_COMMENTS);
2014-12-01 23:55:08 +01:00
Str_FindStrInFile (FileSrc,">",Str_NO_SKIP_HTML_COMMENTS);
}
}
2016-06-13 20:15:31 +02:00
if (!EndCellFound)
2014-12-01 23:55:08 +01:00
{
2016-06-13 20:15:31 +02:00
if (DirectiveFound)
Ch = (int) ' '; // Replace directive for ' ' (separator)
else
{
/***** Check for space or &nbsp; *****/
SpaceFound = false;
if (Ch == (int) '&')
{
/* Check for &nbsp; (case insensitive) */
if (strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,1),"n"))
{ // It's not &n
Str_FindStrInFileBack (FileSrc,"&",Str_NO_SKIP_HTML_COMMENTS); // Back until &
Str_FindStrInFile (FileSrc,"&",Str_NO_SKIP_HTML_COMMENTS); // Skip &
}
else if (strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,1),"b"))
{ // It's not &nb
Str_FindStrInFileBack (FileSrc,"&",Str_NO_SKIP_HTML_COMMENTS); // Back until &
Str_FindStrInFile (FileSrc,"&",Str_NO_SKIP_HTML_COMMENTS); // Skip &
}
else if (strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,1),"s"))
{ // It's not &nbs
Str_FindStrInFileBack (FileSrc,"&",Str_NO_SKIP_HTML_COMMENTS); // Back until &
Str_FindStrInFile (FileSrc,"&",Str_NO_SKIP_HTML_COMMENTS); // Skip &
}
else if (strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,1),"p"))
{ // It's not &nbsp
Str_FindStrInFileBack (FileSrc,"&",Str_NO_SKIP_HTML_COMMENTS); // Back until &
Str_FindStrInFile (FileSrc,"&",Str_NO_SKIP_HTML_COMMENTS); // Skip &
}
else if (strcasecmp (Str_GetNextStrFromFileConvertingToLower (FileSrc,StrAux,1),";"))
{ // It's not &nbsp;
Str_FindStrInFileBack (FileSrc,"&",Str_NO_SKIP_HTML_COMMENTS); // Back until &
Str_FindStrInFile (FileSrc,"&",Str_NO_SKIP_HTML_COMMENTS); // Skip &
}
else // It's &nbsp;
SpaceFound = true;
}
2014-12-01 23:55:08 +01:00
2016-06-13 20:15:31 +02:00
/***** Skip spaces *****/
if (isspace (Ch) ||
Ch == 0xA0) // Unicode translation for &nbsp;
SpaceFound = true;
2015-02-27 13:12:57 +01:00
2016-06-13 20:15:31 +02:00
if (SpaceFound)
Ch = (int) ' '; // Replace any kind of space for ' ' (separator)
}
2014-12-01 23:55:08 +01:00
2016-06-13 20:15:31 +02:00
if (i < MaxLength)
Str[i++] = (char) Ch;
}
2014-12-01 23:55:08 +01:00
}
Str[i] = '\0';
return Str;
}
/*****************************************************************************/
/******************* Read the next N characters in a string ******************/
/*****************************************************************************/
/*
Read from the file FileSrc the next N characters converting them
to lowercase and store them in Str. Return a pointer to the string.
*/
char *Str_GetNextStrFromFileConvertingToLower (FILE *FileSrc,char *Str, int N)
{
int i,Ch;
for (i = 0;
i < N;
i++)
{
if ((Ch = fgetc (FileSrc)) == EOF)
break;
Str[i] = Str_ConvertToLowerLetter ((char) Ch);
}
Str[i] = '\0';
return Str;
}
/*****************************************************************************/
/********** Get from StrSrc into StrDst the next string until space **********/
/*****************************************************************************/
// Modifies *StrSrc
void Str_GetNextStringUntilSpace (const char **StrSrc,char *StrDst,size_t MaxLength)
{
size_t i = 0;
int Ch;
/***** Skip leading spaces *****/
do
{
if ((Ch = (int) **StrSrc) != 0)
(*StrSrc)++;
}
2015-03-20 14:30:28 +01:00
while (isspace (Ch) ||
Ch == 0xA0); // Unicode translation for &nbsp;
2014-12-01 23:55:08 +01:00
/***** Copy string while non-space characters *****/
2015-03-20 14:30:28 +01:00
while (Ch &&
!(isspace (Ch) ||
Ch == 0xA0)) // Unicode translation for &nbsp;
2014-12-01 23:55:08 +01:00
{
if (i < MaxLength)
StrDst[i++] = (char) Ch;
if ((Ch = (int) **StrSrc) != 0)
(*StrSrc)++;
}
StrDst[i] = '\0';
}
/*****************************************************************************/
/******* Get from StrSrc into StrDst the next string until separator *********/
/*****************************************************************************/
// Modifies *StrSrc
void Str_GetNextStringUntilSeparator (const char **StrSrc,char *StrDst,size_t MaxLength)
{
size_t i = 0;
int Ch;
/***** Skip separators *****/
do
{
if ((Ch = (int) **StrSrc) != 0)
(*StrSrc)++;
}
2015-03-20 14:30:28 +01:00
while (isspace (Ch) ||
Ch == 0xA0 || // Unicode translation for &nbsp;
Ch == (int) ',' ||
Ch == (int) ';');
2014-12-01 23:55:08 +01:00
/***** Copy string while no separator found *****/
2015-03-20 14:30:28 +01:00
while (Ch &&
!(isspace (Ch) ||
Ch == 0xA0 || // Unicode translation for &nbsp;
Ch == (int) ',' ||
Ch == (int) ';'))
2014-12-01 23:55:08 +01:00
{
if (i < MaxLength)
StrDst[i++] = (char) Ch;
if ((Ch = (int) **StrSrc) != 0)
(*StrSrc)++;
}
StrDst[i] = '\0';
}
/*****************************************************************************/
/********** Get from StrSrc into StrDst the next string until comma **********/
/*****************************************************************************/
// Modifies *StrSrc
// Leading spaces are not copied
// Trailing spaces are removed at end
void Str_GetNextStringUntilComma (const char **StrSrc,char *StrDst,size_t MaxLength)
{
int Ch;
char *Ptr;
size_t i = 0;
/***** Skip leading spaces and ',' *****/
Ch = (int) **StrSrc;
2015-03-20 14:30:28 +01:00
while (isspace (Ch) ||
Ch == 0xA0 || // Unicode translation for &nbsp;
Ch == (int) ',')
2014-12-01 23:55:08 +01:00
{
(*StrSrc)++;
Ch = (int) **StrSrc;
}
/***** Copy string until ',' or end *****/
Ptr = StrDst;
while (Ch && Ch != (int) ',')
{
if (i < MaxLength)
{
*Ptr++ = (char) Ch;
i++;
}
(*StrSrc)++;
Ch = (int) **StrSrc;
}
/***** Remove trailing spaces *****/
for (Ptr--;
Ptr >= *StrSrc;
Ptr--)
2015-03-20 14:30:28 +01:00
if (!(isspace ((int) *Ptr) ||
*Ptr == '\xA0')) // Unicode translation for &nbsp;
2014-12-01 23:55:08 +01:00
break;
2017-01-28 15:58:46 +01:00
*(Ptr + 1) = '\0';
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
/***************** Replace several spaces of a string for one ****************/
/*****************************************************************************/
void Str_ReplaceSeveralSpacesForOne (char *Str)
{
char *PtrSrc, *PtrDst;
bool PreviousWasSpace = false;
/***** Do the replacing *****/
for (PtrDst = PtrSrc = Str;
*PtrSrc;)
2015-03-20 14:30:28 +01:00
if (isspace ((int) *PtrSrc) ||
*PtrSrc == '\xA0') // Unicode translation for &nbsp;
2014-12-01 23:55:08 +01:00
{
if (!PreviousWasSpace)
*PtrDst++ = ' ';
PreviousWasSpace = true;
PtrSrc++;
}
else
{
PreviousWasSpace = false;
*PtrDst++ = *PtrSrc++;
}
*PtrDst = '\0';
}
/*****************************************************************************/
/************* Copy a string to another changing ' ' to '%20' ****************/
/*****************************************************************************/
void Str_CopyStrChangingSpaces (const char *StringWithSpaces,char *StringWithoutSpaces,unsigned MaxLength)
{
const char *PtrSrc;
char *PtrDst;
unsigned Length = 0;
for (PtrSrc = StringWithSpaces, PtrDst = StringWithoutSpaces;
*PtrSrc && Length <= MaxLength;
PtrSrc++)
if (*PtrSrc == ' ')
{
Length += 3;
if (Length <= MaxLength)
{
*PtrDst++ = '%';
*PtrDst++ = '2';
*PtrDst++ = '0';
}
}
else
{
Length++;
if (Length <= MaxLength)
*PtrDst++ = *PtrSrc;
}
*PtrDst = '\0';
if (Length > MaxLength)
Lay_ShowErrorAndExit ("Path is too long.");
}
/*****************************************************************************/
/* Convert string with a code (of group type, group, degree, etc.) to long **/
/*****************************************************************************/
// Return -1L if code not found in Str
long Str_ConvertStrCodToLongCod (const char *Str)
{
long Code;
2016-12-30 01:20:49 +01:00
if (!Str)
return -1L;
2014-12-01 23:55:08 +01:00
if (Str[0] == '\0')
return -1L;
if (sscanf (Str,"%ld",&Code) != 1)
return -1L;
return Code;
}
/*****************************************************************************/
/**** Compute length of root (all except extension) of the name of a file ****/
/*****************************************************************************/
2016-03-28 19:30:37 +02:00
size_t Str_GetLengthRootFileName (const char *FileName)
2014-12-01 23:55:08 +01:00
{
2016-03-28 19:30:37 +02:00
char *PtrToDot = strrchr (FileName,(int) '.');
size_t LengthFileName = strlen (FileName);
if (PtrToDot)
return LengthFileName - strlen (PtrToDot);
else
return LengthFileName;
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
/************** Get the name of a file from a complete path ******************/
/*****************************************************************************/
// Split a full path in path (without ending '/' ) and a file name
2017-01-17 03:10:43 +01:00
void Str_SplitFullPathIntoPathAndFileName (const char FullPath[PATH_MAX + 1],
char PathWithoutFileName[PATH_MAX + 1],
char FileName[NAME_MAX + 1])
2014-12-01 23:55:08 +01:00
{
const char *PtrFileName;
size_t LengthUntilFileName;
/***** Find the start of filename *****/
if ((PtrFileName = strrchr (FullPath,(int) '/')) != NULL)
PtrFileName++;
else if ((PtrFileName = strrchr (FullPath,(int) '\\')) != NULL)
PtrFileName++;
else
PtrFileName = FullPath;
/***** Get PathWithoutFileName *****/
2017-01-17 03:10:43 +01:00
LengthUntilFileName = (size_t) (PtrFileName - FullPath); // Last slash included
2014-12-01 23:55:08 +01:00
if (LengthUntilFileName > 1)
2017-01-17 03:10:43 +01:00
{
2017-01-15 18:02:52 +01:00
Str_Copy (PathWithoutFileName,FullPath,
2017-01-17 03:10:43 +01:00
PATH_MAX);
PathWithoutFileName[LengthUntilFileName - 1] = '\0'; // Do not copy ending slash
}
2014-12-01 23:55:08 +01:00
else
PathWithoutFileName[0] = '\0';
/***** Get FileName *****/
2017-01-17 03:10:43 +01:00
Str_Copy (FileName,PtrFileName,
NAME_MAX);
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
/************** Check if the extension of a file is .Extension ***************/
/*****************************************************************************/
// Return true if FileName ends by .Extension
// Else return false
bool Str_FileIs (const char *FileName,const char *Extension)
{
2016-03-28 19:30:37 +02:00
int i;
int j;
size_t LengthExtension = strlen (Extension);
2014-12-01 23:55:08 +01:00
2016-03-28 19:30:37 +02:00
/***** Check length of extension. Extension valid are, for example "zip", "html", "mhtml" *****/
2017-03-07 11:03:05 +01:00
if (LengthExtension < Fil_MIN_BYTES_FILE_EXTENSION ||
LengthExtension > Fil_MAX_BYTES_FILE_EXTENSION)
2014-12-01 23:55:08 +01:00
return false;
/***** Check the extension *****/
2016-03-28 19:30:37 +02:00
for (i = strlen (FileName) - 1, j = LengthExtension - 1;
2014-12-01 23:55:08 +01:00
i > 0 && j >= 0;
i--, j--)
if (Str_ConvertToLowerLetter (FileName[i]) != Str_ConvertToLowerLetter (Extension[j]))
return false;
if (j >= 0) /* If all the characters of the extension have not been checked
due to the name of the file is too short */
return false;
/***** Check the dot before the extension *****/
return (FileName[i] == '.');
}
/*****************************************************************************/
/**************** Check if the extension of a file is .html ******************/
/*****************************************************************************/
// Return true if FileName ends by .htm or .html
// Else return false
bool Str_FileIsHTML (const char *FileName)
{
if (Str_FileIs (FileName,"htm"))
return true;
return Str_FileIs (FileName,"html");
}
/*****************************************************************************/
/********************* Check if Path1 starts by Path2 ************************/
/*****************************************************************************/
bool Str_Path1BeginsByPath2 (const char *Path1,const char *Path2)
{
/* The string Path1 must start by the complete string Path2 */
while (*Path2)
if (*Path2++ != *Path1++)
return false;
/* The string Path1 starts by the complete string Path2 */
/* Check that the next character of Path1 is '\0' or '/' */
return (bool) (*Path1 == '\0' || *Path1 == '/');
}
/*****************************************************************************/
/** Skip spaces in a file seeking it before of reading the first non-blank ***/
/*****************************************************************************/
void Str_SkipSpacesInFile (FILE *FileSrc)
{
int Ch;
while ((Ch = fgetc (FileSrc)) != EOF)
2015-03-20 14:30:28 +01:00
if (!(isspace (Ch) ||
Ch == 0xA0)) // Unicode translation for &nbsp;
2014-12-01 23:55:08 +01:00
{
fseek (FileSrc,-1L,SEEK_CUR);
break;
}
}
/*****************************************************************************/
/***************** Write a string to a file changing *************************/
/***************** <br> or <br /> for return *************************/
/***************** and &nbsp; for space *************************/
/*****************************************************************************/
void Str_FilePrintStrChangingBRToRetAndNBSPToSpace (FILE *FileTgt,const char *Str)
{
while (*Str)
{
/* Is &nbsp;? */
if (*Str == '&')
{
2017-01-28 15:58:46 +01:00
if (*(Str + 1) == 'N' || *(Str + 1) == 'n')
if (*(Str + 2) == 'B' || *(Str + 2) == 'b')
if (*(Str + 3) == 'S' || *(Str + 3) == 's')
if (*(Str + 4) == 'P' || *(Str + 4) == 'p')
if (*(Str + 5) == ';')
2014-12-01 23:55:08 +01:00
{
fputc ((int) ' ',FileTgt);
Str += 6;
continue;
}
}
/* Is <br> or <br />? */
else if (*Str == '<')
{
2017-01-28 15:58:46 +01:00
if (*(Str + 1) == 'B' || *(Str + 1) == 'b')
if (*(Str + 2) == 'R' || *(Str + 2) == 'r')
2014-12-01 23:55:08 +01:00
{
2017-01-28 15:58:46 +01:00
if (*(Str + 3) == '>')
2014-12-01 23:55:08 +01:00
{
fputc ((int) '\n',FileTgt);
Str += 4;
continue;
}
2017-01-28 15:58:46 +01:00
else if (*(Str + 3) == ' ')
2014-12-01 23:55:08 +01:00
{
2017-01-28 15:58:46 +01:00
if (*(Str + 4) == '/')
if (*(Str + 5) == '>')
2014-12-01 23:55:08 +01:00
{
fputc ((int) '\n',FileTgt);
Str += 6;
continue;
}
}
}
}
fputc ((int) *Str,FileTgt);
Str++;
}
}
/*****************************************************************************/
/*************** Search a string in a file and/or in a string ****************/
/*****************************************************************************/
/*
Search in the file FileSrc the string StrDelimit.
Write in the file FileTgt and/or StrDst the characters read from FileSrc, not including StrDelimit!.
2016-04-01 03:09:45 +02:00
StrDst can be NULL if you don't want to use them.
2014-12-01 23:55:08 +01:00
If StrDelimit is found, return 1.
If what is read exceed MaxLength, abort and return 0.
If StrDelimit is not found, return -1.
*/
2017-03-08 01:21:21 +01:00
#define Str_MAX_BYTES_BOUNDARY_STR 100
2016-03-31 10:11:36 +02:00
2016-04-01 03:09:45 +02:00
int Str_ReadFileUntilBoundaryStr (FILE *FileSrc,char *StrDst,
const char *BoundaryStr,
unsigned LengthBoundaryStr,
unsigned long long MaxLength)
2014-12-01 23:55:08 +01:00
{
2016-04-01 03:09:45 +02:00
unsigned NumBytesIdentical; // Number of characters identical in each iteration of the loop
unsigned NumBytesReadButNotDiscarded; // Number of characters read from the source file...
// ...and not fully discarded in search
2017-03-08 01:21:21 +01:00
int Buffer[Str_MAX_BYTES_BOUNDARY_STR + 1];
2016-04-01 03:09:45 +02:00
unsigned StartIndex;
unsigned i;
2014-12-01 23:55:08 +01:00
char *Ptr; // Pointer used to go through StrDst writing characters
2016-04-01 03:09:45 +02:00
unsigned long long LengthDst;
2014-12-01 23:55:08 +01:00
2016-04-01 03:09:45 +02:00
/***** Checkings on boundary string *****/
if (!LengthBoundaryStr)
2014-12-01 23:55:08 +01:00
{
if (StrDst != NULL)
*StrDst = '\0';
return 1;
}
2017-03-08 01:21:21 +01:00
if (LengthBoundaryStr > Str_MAX_BYTES_BOUNDARY_STR)
2014-12-01 23:55:08 +01:00
Lay_ShowErrorAndExit ("Delimiter string too large.");
Ptr = StrDst;
2016-04-01 03:09:45 +02:00
StartIndex = 0;
NumBytesReadButNotDiscarded = 0;
LengthDst = 0;
2014-12-01 23:55:08 +01:00
for (;;)
{
2016-04-01 03:09:45 +02:00
if (!NumBytesReadButNotDiscarded)
2014-12-01 23:55:08 +01:00
{ // Read next character
Buffer[StartIndex] = fgetc (FileSrc);
if (feof (FileSrc))
{
if (StrDst != NULL)
*Ptr = '\0';
return -1;
}
2016-04-01 03:09:45 +02:00
NumBytesReadButNotDiscarded++;
2014-12-01 23:55:08 +01:00
}
2016-04-01 03:09:45 +02:00
if (Buffer[StartIndex] == (int) BoundaryStr[0]) // First character identical
2014-12-01 23:55:08 +01:00
{
2016-04-01 03:09:45 +02:00
for (NumBytesIdentical = 1, i = (StartIndex + 1) % LengthBoundaryStr;
NumBytesIdentical < LengthBoundaryStr;
NumBytesIdentical++, i = (i + 1) % LengthBoundaryStr)
2014-12-01 23:55:08 +01:00
{
2016-04-01 03:09:45 +02:00
if (NumBytesReadButNotDiscarded == NumBytesIdentical) // Last character is identical
2014-12-01 23:55:08 +01:00
{
Buffer[i] = fgetc (FileSrc); // Read next character
if (feof (FileSrc))
{
if (StrDst != NULL)
*Ptr = '\0';
return -1;
}
2016-04-01 03:09:45 +02:00
NumBytesReadButNotDiscarded++;
2014-12-01 23:55:08 +01:00
}
2016-04-01 03:09:45 +02:00
if (Buffer[i] != (int) BoundaryStr[NumBytesIdentical]) // Next character is different
2014-12-01 23:55:08 +01:00
break;
}
2016-04-01 03:09:45 +02:00
if (NumBytesIdentical == LengthBoundaryStr) // Boundary found
2014-12-01 23:55:08 +01:00
{
if (StrDst != NULL)
*Ptr = '\0';
return 1;
}
}
2016-04-01 03:09:45 +02:00
2014-12-01 23:55:08 +01:00
if (LengthDst == MaxLength)
{
if (StrDst != NULL)
*Ptr = '\0';
return 0;
}
2016-04-01 03:09:45 +02:00
2014-12-01 23:55:08 +01:00
if (StrDst != NULL)
*Ptr++ = (char) Buffer[StartIndex];
2016-04-01 03:09:45 +02:00
StartIndex = (StartIndex + 1) % LengthBoundaryStr;
NumBytesReadButNotDiscarded--;
2014-12-01 23:55:08 +01:00
LengthDst++;
}
return 0; // Not reached
}
/*****************************************************************************/
/****** Convert invalid characters in a file name to valid characters ********/
/*****************************************************************************/
// Return true if the name of the file o folder is valid
2019-03-09 20:12:44 +01:00
// If the name is not valid, an alert will contain feedback text
2014-12-01 23:55:08 +01:00
// File names with heading and trailing spaces are allowed
bool Str_ConvertFilFolLnkNameToValid (char *FileName)
{
extern const char *Txt_UPLOAD_FILE_X_invalid_name_NO_HTML;
extern const char *Txt_UPLOAD_FILE_X_invalid_name;
extern const char *Txt_UPLOAD_FILE_Invalid_name_NO_HTML;
extern const char *Txt_UPLOAD_FILE_Invalid_name;
char *Ptr;
unsigned NumAlfanum = 0;
unsigned NumSpaces = 0;
unsigned NumPoints = 0;
bool FileNameIsOK = false;
Ptr = FileName;
if (*Ptr) // FileName is not empty
{
for (;
*Ptr;
Ptr++)
{
if (*Ptr == ' ')
{
NumPoints = 0;
if (++NumSpaces > 1)
{
*Ptr = '_';
NumAlfanum++;
NumSpaces = NumPoints = 0;
}
}
else if (*Ptr == '.')
{
if (++NumPoints > 1) // Don't allow ".."
{
*Ptr = '_';
NumAlfanum++;
NumSpaces = NumPoints = 0;
}
}
else
{
switch (*Ptr)
{
case '<EFBFBD>': *Ptr = 'a'; break;
case '<EFBFBD>': *Ptr = 'e'; break;
case '<EFBFBD>': *Ptr = 'i'; break;
case '<EFBFBD>': *Ptr = 'o'; break;
case '<EFBFBD>': *Ptr = 'u'; break;
case '<EFBFBD>': *Ptr = 'n'; break;
case '<EFBFBD>': *Ptr = 'a'; break;
case '<EFBFBD>': *Ptr = 'e'; break;
case '<EFBFBD>': *Ptr = 'i'; break;
case '<EFBFBD>': *Ptr = 'o'; break;
case '<EFBFBD>': *Ptr = 'u'; break;
case '<EFBFBD>': *Ptr = 'c'; break;
case '<EFBFBD>': *Ptr = 'A'; break;
case '<EFBFBD>': *Ptr = 'E'; break;
case '<EFBFBD>': *Ptr = 'I'; break;
case '<EFBFBD>': *Ptr = 'O'; break;
case '<EFBFBD>': *Ptr = 'U'; break;
case '<EFBFBD>': *Ptr = 'N'; break;
case '<EFBFBD>': *Ptr = 'A'; break;
case '<EFBFBD>': *Ptr = 'E'; break;
case '<EFBFBD>': *Ptr = 'I'; break;
case '<EFBFBD>': *Ptr = 'O'; break;
case '<EFBFBD>': *Ptr = 'U'; break;
case '<EFBFBD>': *Ptr = 'C'; break;
}
if ((*Ptr >= 'a' && *Ptr <= 'z') ||
(*Ptr >= 'A' && *Ptr <= 'Z') ||
(*Ptr >= '0' && *Ptr <= '9') ||
*Ptr == '_' ||
*Ptr == '-')
{
NumAlfanum++;
NumSpaces = NumPoints = 0;
}
else
{
*Ptr = '_';
NumAlfanum++;
NumSpaces = NumPoints = 0;
}
}
}
if (NumAlfanum)
FileNameIsOK = true;
else
2019-03-09 20:12:44 +01:00
Ale_CreateAlert (Ale_WARNING,NULL,
Gbl.FileBrowser.UploadingWithDropzone ? Txt_UPLOAD_FILE_X_invalid_name_NO_HTML :
Txt_UPLOAD_FILE_X_invalid_name,
FileName);
2014-12-01 23:55:08 +01:00
}
else // FileName is empty
2019-03-09 20:12:44 +01:00
Ale_CreateAlert (Ale_WARNING,NULL,
Gbl.FileBrowser.UploadingWithDropzone ? Txt_UPLOAD_FILE_Invalid_name_NO_HTML :
Txt_UPLOAD_FILE_Invalid_name);
2014-12-01 23:55:08 +01:00
return FileNameIsOK;
}
/*****************************************************************************/
/************ Convert a string to a valid name of file or folder *************/
/*****************************************************************************/
void Str_ConvertToValidFileName (char *Str)
{
char *Ptr;
for (Ptr = Str;
*Ptr;
Ptr++)
{
if ((*Ptr >= 'a' && *Ptr <= 'z') ||
(*Ptr >= 'A' && *Ptr <= 'Z') ||
(*Ptr >= '0' && *Ptr <= '9') ||
*Ptr == '_' ||
*Ptr == '-')
continue;
2015-04-14 19:38:00 +02:00
if (isspace ((int) *Ptr) ||
*Ptr == '\xA0')
*Ptr = '_';
else
switch (*Ptr)
{
case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': *Ptr = 'a'; break;
case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': *Ptr = 'e'; break;
case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': *Ptr = 'i'; break;
case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': *Ptr = 'o'; break;
case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': *Ptr = 'u'; break;
case '<EFBFBD>': *Ptr = 'n'; break;
case '<EFBFBD>': *Ptr = 'c'; break;
case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': *Ptr = 'A'; break;
case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': *Ptr = 'E'; break;
case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': *Ptr = 'I'; break;
case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': *Ptr = 'O'; break;
case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': case '<EFBFBD>': *Ptr = 'U'; break;
case '<EFBFBD>': *Ptr = 'N'; break;
case '<EFBFBD>': *Ptr = 'C'; break;
default: *Ptr = '-'; break;
}
2014-12-01 23:55:08 +01:00
}
}
/*****************************************************************************/
/******************* Create a random alphanumeric string *********************/
/*****************************************************************************/
2017-01-28 15:58:46 +01:00
#define NUM_ALPHANUM_CHARS (10 + 26 + 26)
2014-12-01 23:55:08 +01:00
void Str_CreateRandomAlphanumStr (char *Str,size_t Length)
{
static const char CharTable[NUM_ALPHANUM_CHARS] =
{'0','1','2','3','4','5','6','7','8','9',
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'
};
size_t i;
/***** Set random chars in string *****/
for (i = 0;
i <Length;
i++)
Str[i] = CharTable[(unsigned) (((float) rand () * (float) (NUM_ALPHANUM_CHARS-1)) / (float) RAND_MAX + 0.5)];
Str[Length] = '\0';
}
/*****************************************************************************/
2017-01-17 03:10:43 +01:00
/****************************** Safe string copy *****************************/
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
2018-10-18 02:02:32 +02:00
// DstSize does not include ending byte '\0'
2014-12-01 23:55:08 +01:00
2017-01-17 03:10:43 +01:00
void Str_Copy (char *Dst,const char *Src,size_t DstSize)
2014-12-01 23:55:08 +01:00
{
2019-02-17 01:14:55 +01:00
char ErrorTxt[128];
2017-01-17 03:10:43 +01:00
size_t LengthSrc = strlen (Src);
2014-12-01 23:55:08 +01:00
2017-01-17 10:06:52 +01:00
/***** Check if buffer has enough space for source *****/
if (LengthSrc > DstSize)
2017-01-17 03:10:43 +01:00
{
2019-02-17 01:14:55 +01:00
snprintf (ErrorTxt,sizeof (ErrorTxt),
2018-10-16 21:56:01 +02:00
"Trying to copy %lu chars into a %lu-chars buffer.",
LengthSrc,DstSize);
2019-02-17 01:14:55 +01:00
Lay_ShowErrorAndExit (ErrorTxt);
2017-01-17 03:10:43 +01:00
}
2014-12-01 23:55:08 +01:00
2017-01-17 10:06:52 +01:00
/***** Copy source into destination *****/
2017-01-17 03:10:43 +01:00
strcpy (Dst,Src);
2014-12-01 23:55:08 +01:00
}
2017-01-15 02:05:24 +01:00
2017-01-15 18:02:52 +01:00
/*****************************************************************************/
2017-01-17 03:10:43 +01:00
/************************** Safe string concatenation ************************/
2017-01-15 18:02:52 +01:00
/*****************************************************************************/
2018-10-18 02:02:32 +02:00
// DstSize does not include ending byte '\0'
2017-01-15 18:02:52 +01:00
2017-01-17 03:10:43 +01:00
void Str_Concat (char *Dst,const char *Src,size_t DstSize)
2017-01-15 18:02:52 +01:00
{
2017-01-17 03:10:43 +01:00
size_t LengthDst;
size_t LengthSrc;
size_t FreeSpace;
2019-02-17 01:14:55 +01:00
char ErrorTxt[256];
2017-01-15 18:02:52 +01:00
2017-01-17 10:06:52 +01:00
/***** Check if buffer has already overflowed *****/
2017-01-17 03:10:43 +01:00
LengthDst = strlen (Dst);
if (LengthDst > DstSize)
{
2019-02-17 01:14:55 +01:00
snprintf (ErrorTxt,sizeof (ErrorTxt),
2018-10-16 21:56:01 +02:00
"%lu-chars buffer has %lu chars!",
DstSize,LengthDst);
2019-02-17 01:14:55 +01:00
Lay_ShowErrorAndExit (ErrorTxt);
2017-01-17 03:10:43 +01:00
}
2017-01-15 02:05:24 +01:00
2017-01-17 10:06:52 +01:00
/***** Check if buffer has enough space for source *****/
// DstSize >= LengthDst ==> FreeSpace >= 0
2017-01-17 03:10:43 +01:00
FreeSpace = DstSize - LengthDst;
LengthSrc = strlen (Src);
if (FreeSpace < LengthSrc)
{
2019-02-17 01:14:55 +01:00
snprintf (ErrorTxt,sizeof (ErrorTxt),
"Trying to concatenate %lu chars to a %lu-chars buffer"
" with free space for only %lu chars!",
2018-10-16 21:56:01 +02:00
LengthSrc,DstSize,FreeSpace);
2019-02-17 01:14:55 +01:00
Lay_ShowErrorAndExit (ErrorTxt);
2017-01-17 03:10:43 +01:00
}
2017-01-17 10:06:52 +01:00
/***** Concatenate ******/
2017-01-17 03:10:43 +01:00
strcat (Dst,Src);
2017-01-15 02:05:24 +01:00
}