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 .
2024-02-07 00:40:28 +01:00
Copyright ( C ) 1999 - 2024 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 ***********************************/
/*****************************************************************************/
2019-10-10 21:21:24 +02:00
# define _GNU_SOURCE // For asprintf
2014-12-01 23:55:08 +01:00
# 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...
2019-10-10 21:21:24 +02:00
# include <stdio.h> // For asprintf
2021-12-01 19:30:49 +01:00
# include <stdlib.h> // For free
2014-12-01 23:55:08 +01:00
# include <string.h> // For string functions
2022-10-19 18:07:49 +02:00
# include "swad_alert.h"
2021-04-26 15:27:27 +02:00
# include "swad_error.h"
2014-12-01 23:55:08 +01:00
# include "swad_global.h"
2021-10-01 09:11:58 +02:00
# include "swad_notification_database.h"
2022-10-19 18:07:49 +02:00
# include "swad_parameter.h"
2014-12-01 23:55:08 +01:00
# include "swad_string.h"
/*****************************************************************************/
/******************** Global variables from other modules ********************/
/*****************************************************************************/
2021-11-28 00:49:23 +01:00
extern struct Globals Gbl ;
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
2019-11-21 16:47:07 +01:00
/*************************** Private prototypes ******************************/
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
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 ) ;
/*****************************************************************************/
2019-11-21 16:47:07 +01:00
/**************************** Private constants ******************************/
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
/***** 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 } ;
2021-11-24 18:12:23 +01:00
/*****************************************************************************/
2021-11-24 20:54:54 +01:00
/*********** Check if a character is in set { a-z, A-Z, 0-9, _ } *************/
2021-11-24 18:12:23 +01:00
/*****************************************************************************/
2021-11-24 20:54:54 +01:00
bool Str_ChIsAlphaNum ( char Ch )
2021-11-24 18:12:23 +01:00
{
2021-11-24 20:54:54 +01:00
return ( ( Ch > = ' a ' & & Ch < = ' z ' ) | |
( Ch > = ' A ' & & Ch < = ' Z ' ) | |
( Ch > = ' 0 ' & & Ch < = ' 9 ' ) | |
( Ch = = ' _ ' ) ) ;
2021-11-24 18:12:23 +01:00
}
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
/** Get next ASCII character from a string converting &#number; to character */
/*****************************************************************************/
// Returns number of char analyzed
2021-11-28 00:49:23 +01:00
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 ñ ==> 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 ñ ==> 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 "ñ..." or "面...", 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
}
2021-02-08 19:04:47 +01:00
/*****************************************************************************/
/*** Create a notification about mention for any nickname in a publication ***/
/*****************************************************************************/
/*
Example : " The user @rms says... "
^ ^
PtrStart ___ | | ___ PtrEnd
Length = 3
*/
void Str_AnalyzeTxtAndStoreNotifyEventToMentionedUsrs ( long PubCod , const char * Txt )
{
const char * Ptr ;
bool IsNickname ;
struct
{
const char * PtrStart ;
const char * PtrEnd ;
size_t Length ; // Length of the nickname
} Nickname ;
2022-09-24 18:30:26 +02:00
struct Usr_Data UsrDat ;
2021-02-08 19:04:47 +01:00
bool CreateNotif ;
bool NotifyByEmail ;
/***** Initialize structure with user's data *****/
Usr_UsrDataConstructor ( & UsrDat ) ;
/***** Find nicknames and create notifications *****/
for ( Ptr = Txt ;
* Ptr ; )
/* Check if the next char is the start of a nickname */
if ( ( int ) * Ptr = = ( int ) ' @ ' )
{
/* Find nickname end */
Ptr + + ; // Points to first character after @
Nickname . PtrStart = Ptr ;
/* A nick can have digits, letters and '_' */
for ( ;
* Ptr ;
Ptr + + )
if ( ! ( ( * Ptr > = ' a ' & & * Ptr < = ' z ' ) | |
( * Ptr > = ' A ' & & * Ptr < = ' Z ' ) | |
( * Ptr > = ' 0 ' & & * Ptr < = ' 9 ' ) | |
( * Ptr = = ' _ ' ) ) )
break ;
/* Calculate length of this nickname */
Nickname . PtrEnd = Ptr - 1 ;
Nickname . Length = ( size_t ) ( Ptr - Nickname . PtrStart ) ;
/* A nick (without arroba) must have a number of characters
2021-10-21 20:33:55 +02:00
Nck_MIN_CHARS_NICK_WITHOUT_ARROBA < = Length < = Nck_MAX_CHARS_NICK_WITHOUT_ARROBA */
IsNickname = ( Nickname . Length > = Nck_MIN_CHARS_NICK_WITHOUT_ARROBA & &
Nickname . Length < = Nck_MAX_CHARS_NICK_WITHOUT_ARROBA ) ;
2021-02-08 19:04:47 +01:00
if ( IsNickname )
{
/* Copy nickname */
strncpy ( UsrDat . Nickname , Nickname . PtrStart , Nickname . Length ) ;
UsrDat . Nickname [ Nickname . Length ] = ' \0 ' ;
if ( ( UsrDat . UsrCod = Nck_GetUsrCodFromNickname ( UsrDat . Nickname ) ) > 0 )
2023-04-04 13:46:51 +02:00
if ( Usr_ItsMe ( UsrDat . UsrCod ) = = Usr_OTHER ) // Not me
2021-02-08 19:04:47 +01:00
{
/* Get user's data */
2021-04-24 15:10:07 +02:00
Usr_GetAllUsrDataFromUsrCod ( & UsrDat ,
Usr_DONT_GET_PREFS ,
2023-10-27 13:54:14 +02:00
Usr_DONT_GET_ROLE_IN_CRS ) ;
2021-02-08 19:04:47 +01:00
/* Create notification for the mentioned user *****/
2021-11-12 01:12:15 +01:00
CreateNotif = ( UsrDat . NtfEvents . CreateNotif & ( 1 < < Ntf_EVENT_TML_MENTION ) ) ;
2021-02-08 19:04:47 +01:00
if ( CreateNotif )
{
2021-11-12 01:12:15 +01:00
NotifyByEmail = ( UsrDat . NtfEvents . SendEmail & ( 1 < < Ntf_EVENT_TML_MENTION ) ) ;
Ntf_DB_StoreNotifyEventToUsr ( Ntf_EVENT_TML_MENTION , UsrDat . UsrCod , PubCod ,
2021-11-09 09:20:54 +01:00
( Ntf_Status_t ) ( NotifyByEmail ? Ntf_STATUS_BIT_EMAIL :
0 ) ,
2023-09-22 14:47:56 +02:00
Gbl . Hierarchy . Node [ Hie_INS ] . HieCod ,
Gbl . Hierarchy . Node [ Hie_CTR ] . HieCod ,
Gbl . Hierarchy . Node [ Hie_DEG ] . HieCod ,
Gbl . Hierarchy . Node [ Hie_CRS ] . HieCod ) ;
2021-02-08 19:04:47 +01:00
}
}
}
}
/* The next char is not the start of a nickname */
else // Character != '@'
Ptr + + ;
/***** Free memory used for user's data *****/
Usr_UsrDataDestructor ( & UsrDat ) ;
}
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
/***** 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
2017-11-13 16:34:33 +01:00
Ch = = ' - ' | |
Ch = = ' ( ' | |
Ch = = ' ) ' | |
Ch = = ' , ' | |
// Ch == ';' || // Do not start a new word on ';'.
// Example: aktoğan should be converted to Aktoğ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
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 ) ;
}
}
2019-10-10 21:21:24 +02:00
/*****************************************************************************/
2020-01-11 15:22:02 +01:00
/****************** Write a number in floating point *************************/
2019-10-10 21:21:24 +02:00
/*****************************************************************************/
// Str should be freed after calling this function
2019-11-11 00:15:44 +01:00
void Str_DoubleNumToStr ( char * * Str , double Number )
2020-01-11 15:22:02 +01:00
{
if ( asprintf ( Str , " %.15lg " ,
Number ) < 0 )
2021-04-26 15:27:27 +02:00
Err_NotEnoughMemoryExit ( ) ;
2020-01-11 15:22:02 +01:00
}
/*****************************************************************************/
/******* Write a number in floating point with few digits to a string ********/
/*****************************************************************************/
// Str should be freed after calling this function
void Str_DoubleNumToStrFewDigits ( char * * Str , double Number )
2015-03-11 14:35:33 +01:00
{
double IntegerPart ;
double FractionaryPart ;
char * Format ;
2019-11-11 00:15:44 +01:00
FractionaryPart = fabs ( modf ( Number , & IntegerPart ) ) ;
2015-03-11 14:35:33 +01:00
if ( FractionaryPart = = 0.0 )
2019-10-10 21:21:24 +02:00
{
if ( asprintf ( Str , " %.0f " , IntegerPart ) < 0 )
2021-04-26 15:27:27 +02:00
Err_NotEnoughMemoryExit ( ) ;
2019-10-10 21:21:24 +02:00
}
2015-03-11 14:35:33 +01:00
else
{
2019-03-05 14:13:27 +01:00
if ( IntegerPart ! = 0.0 )
2019-11-11 00:15:44 +01:00
Format = " %.1lf " ;
2019-03-06 10:13:39 +01:00
else if ( FractionaryPart > = 0.095 )
2019-11-11 00:15:44 +01:00
Format = " %.2lf " ;
2019-03-23 13:10:31 +01:00
else if ( FractionaryPart > = 0.0095 )
2019-11-11 00:15:44 +01:00
Format = " %.3lf " ;
2019-03-23 13:10:31 +01:00
else if ( FractionaryPart > = 0.00095 )
2019-11-11 00:15:44 +01:00
Format = " %.4lf " ;
2019-03-23 13:10:31 +01:00
else if ( FractionaryPart > = 0.000095 )
2019-11-11 00:15:44 +01:00
Format = " %.5lf " ;
2019-03-23 13:10:31 +01:00
else if ( FractionaryPart > = 0.0000095 )
2019-11-11 00:15:44 +01:00
Format = " %.6lf " ;
2015-03-11 14:35:33 +01:00
else
2019-11-11 00:15:44 +01:00
Format = " %le " ;
2019-10-10 21:21:24 +02:00
if ( asprintf ( Str , Format , Number ) < 0 )
2021-04-26 15:27:27 +02:00
Err_NotEnoughMemoryExit ( ) ;
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 = ' . ' ;
}
/*****************************************************************************/
2019-11-27 09:01:45 +01:00
/************************ Get a double from a string *************************/
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
2019-11-27 09:01:45 +01:00
// This function may change Str on wrong double
2014-12-01 23:55:08 +01:00
2020-03-23 17:14:41 +01:00
double Str_GetDoubleFromStr ( const char * Str )
2014-12-01 23:55:08 +01:00
{
2020-03-23 17:14:41 +01:00
char * StrPoint ;
2019-11-27 09:01:45 +01:00
double DoubleNum ;
/***** Trivial check *****/
if ( Str = = NULL ) // If no string...
return 0.0 ; // ...the number is reset to 0
2020-03-23 17:14:41 +01:00
/***** Copy source string to temporary string to convert to point *****/
if ( asprintf ( & StrPoint , " %s " , Str ) < 0 )
2021-04-26 15:27:27 +02:00
Err_NotEnoughMemoryExit ( ) ;
2020-03-23 17:14:41 +01:00
2019-11-27 09:01:45 +01:00
/***** The string is "scanned" in floating point
( it must have a point , not a comma as decimal separator ) * * * * */
2020-03-23 17:14:41 +01:00
Str_ConvertStrFloatCommaToStrFloatPoint ( StrPoint ) ;
2019-11-27 09:01:45 +01:00
Str_SetDecimalPointToUS ( ) ; // To get the decimal point as a dot
2020-03-23 17:14:41 +01:00
if ( sscanf ( StrPoint , " %lf " , & DoubleNum ) ! = 1 ) // If the string does not hold a valid number...
DoubleNum = 0.0 ; // ...the number is reset to 0
2019-11-27 09:01:45 +01:00
Str_SetDecimalPointToLocal ( ) ; // Return to local system
2014-12-01 23:55:08 +01:00
2020-03-23 17:14:41 +01:00
/***** Free temporary string *****/
free ( StrPoint ) ;
2019-11-27 09:01:45 +01:00
return DoubleNum ;
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
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 ' ***************/
/*****************************************************************************/
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 '
}
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 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 ,
2023-05-30 23:18:59 +02:00
char * Str , size_t MaxLengthStr ,
Str_RemoveSpaces_t RemoveLeadingAndTrailingSpaces )
2014-12-01 23:55:08 +01:00
{
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
if ( ChangeTo ! = Str_DONT_CHANGE )
{
/***** Allocate memory for a destination string where to do the changes *****/
2021-02-15 16:25:55 +01:00
if ( ( StrDst = malloc ( MaxLengthStr + 1 ) ) = = NULL )
2021-04-26 15:27:27 +02:00
Err_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 :
2022-10-18 08:41:54 +02:00
switch ( Par_GetContentReceivedByCGI ( ) )
{
2024-04-23 23:54:23 +02:00
case Act_NORM :
2022-10-18 08:41:54 +02:00
// The form contained text and was sent with content type application/x-www-form-urlencoded
switch ( Ch )
{
case ' + ' : /* Change every '+' to a space */
IsSpecialChar = true ;
LengthSpecStrSrc = 1 ;
SpecialChar = 0x20 ;
break ;
case ' % ' : /* Change "%XX" --> "&#decimal_number;" (from 0 to 255) */
/* Change "%uXXXX" --> "&#decimal number; (from 0 to 65535) */
IsSpecialChar = true ;
if ( * ( PtrSrc + 1 ) = = ' u ' )
{
sscanf ( PtrSrc + 2 , " %4X " , & SpecialChar ) ;
LengthSpecStrSrc = 6 ;
}
else
{
sscanf ( PtrSrc + 1 , " %2X " , & SpecialChar ) ;
LengthSpecStrSrc = 3 ;
}
/* Some special characters, like a chinese character,
can be received from a form in a format like this :
% 26 % 2335753 % 3 B - - > % 26 % 23 3 5 7 5 3 % 3 B - - > & # 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 ;
*/
break ;
case 0x27 : /* Change single comilla --> "'" to avoid SQL code injection */
case 0x5C : /* '\\' */
2016-04-07 23:35:47 +02:00
IsSpecialChar = true ;
LengthSpecStrSrc = 1 ;
2016-04-08 00:50:30 +02:00
SpecialChar = ( unsigned int ) Ch ;
2022-10-18 08:41:54 +02:00
break ;
default :
2016-04-07 23:35:47 +02:00
IsSpecialChar = false ;
NumPrintableCharsFromReturn + + ;
ThereIsSpaceChar = false ;
2022-10-18 08:41:54 +02:00
break ;
}
break ;
2024-04-23 23:54:23 +02:00
case Act_DATA :
2022-10-18 08:41:54 +02:00
// The form contained data and was sent with content type multipart/form-data
switch ( Ch )
{
case 0x20 : /* Space */
case 0x22 : /* Change double comilla --> """ */
case 0x23 : /* '#' */
case 0x26 : /* Change '&' --> "&" */
case 0x27 : /* Change single comilla --> "'" to avoid SQL code injection */
case 0x2C : /* ',' */
case 0x2F : /* '/' */
case 0x3A : /* ':' */
case 0x3B : /* ';' */
case 0x3C : /* '<' --> "<" */
case 0x3E : /* '>' --> ">" */
case 0x3F : /* '?' */
case 0x40 : /* '@' */
case 0x5C : /* '\\' */
IsSpecialChar = true ;
LengthSpecStrSrc = 1 ;
SpecialChar = ( unsigned int ) Ch ;
break ;
default :
if ( Ch < 0x20 | | Ch > 0x7F )
{
IsSpecialChar = true ;
LengthSpecStrSrc = 1 ;
SpecialChar = ( unsigned int ) Ch ;
}
else
{
IsSpecialChar = false ;
NumPrintableCharsFromReturn + + ;
ThereIsSpaceChar = false ;
}
break ;
}
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 --> "'" 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
// 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 ? " " :
" " , // The first space
2021-02-15 16:25:55 +01:00
sizeof ( StrSpecialChar ) - 1 ) ;
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 , " " , // Add a space
2021-02-15 16:25:55 +01:00
sizeof ( StrSpecialChar ) - 1 ) ;
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 /> " ,
2021-02-15 16:25:55 +01:00
sizeof ( StrSpecialChar ) - 1 ) ;
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 , " " ,
2021-02-15 16:25:55 +01:00
sizeof ( StrSpecialChar ) - 1 ) ;
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" --> """ (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 , " " " , // Double comilla is stored as HTML code to avoid problems when displaying it
2021-02-15 16:25:55 +01:00
sizeof ( StrSpecialChar ) - 1 ) ;
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 分
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 分
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" --> "'" (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 , " ' " , // Single comilla is stored as HTML entity to avoid problem when querying database (SQL code injection)
2021-02-15 16:25:55 +01:00
sizeof ( StrSpecialChar ) - 1 ) ;
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 分
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" --> "<" (<) */
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 , " < " , // "<" is stored as HTML code to avoid problems when displaying it
2021-02-15 16:25:55 +01:00
sizeof ( StrSpecialChar ) - 1 ) ;
2014-12-01 23:55:08 +01:00
NumPrintableCharsFromReturn + + ;
ThereIsSpaceChar = false ;
break ;
case 0x3E : /* "%3E" --> ">" (>) */
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 , " > " , // ">" is stored as HTML code to avoid problems when displaying it
2021-02-15 16:25:55 +01:00
sizeof ( StrSpecialChar ) - 1 ) ;
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" --> "\" (\) */
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 , " \ " , // "\" is stored as HTML code to avoid problems when displaying it
2021-02-15 16:25:55 +01:00
sizeof ( StrSpecialChar ) - 1 ) ;
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 ) ,
2020-05-21 04:02:29 +02:00
( SpecialChar < 256 & &
( ChangeTo = = Str_TO_TEXT | |
ChangeTo = = Str_TO_MARKDOWN ) ) ? " %c " :
" &#%u; " ,
2018-10-18 02:02:32 +02:00
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 )
2021-04-26 15:27:27 +02:00
Err_ShowErrorAndExit ( " Space allocated to string is full. " ) ;
2014-12-01 23:55:08 +01:00
/* Copy to appropiate place the special character string */
2021-01-20 00:42:59 +01:00
// strncpy (PtrDst,StrSpecialChar,LengthSpecStrDst);
strcpy ( PtrDst , StrSpecialChar ) ;
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 )
2021-04-26 15:27:27 +02:00
Err_ShowErrorAndExit ( " Space allocated to string is full. " ) ;
2014-12-01 23:55:08 +01:00
/* 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 *****/
2019-11-06 19:45:20 +01:00
free ( StrDst ) ;
2014-12-01 23:55:08 +01:00
}
2023-05-30 23:18:59 +02:00
if ( RemoveLeadingAndTrailingSpaces = = Str_REMOVE_SPACES )
2014-12-01 23:55:08 +01:00
{
/***** 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
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 */
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 */
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
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 */
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 */
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 )
{
2021-09-30 20:11:00 +02:00
size_t NumLeadingArr ;
2014-12-01 23:55:08 +01:00
2015-10-07 19:55:13 +02:00
if ( Str )
if ( Str [ 0 ] )
{
2021-09-30 20:11:00 +02:00
NumLeadingArr = strspn ( Str , " @ " ) ;
if ( NumLeadingArr )
2015-10-07 19:55:13 +02:00
// Do not use strcpy / memcpy because the strings overlap
2021-09-30 20:11:00 +02:00
memmove ( Str , & Str [ NumLeadingArr ] ,
strlen ( Str ) - NumLeadingArr + 1 ) ;
2015-10-07 19:55:13 +02:00
}
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 *****/
SpaceFound = false ;
if ( Ch = = ( int ) ' & ' )
{
/* Check for (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  
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
Str_FindStrInFileBack ( FileSrc , " & " , Str_NO_SKIP_HTML_COMMENTS ) ; // Back until &
Str_FindStrInFile ( FileSrc , " & " , Str_NO_SKIP_HTML_COMMENTS ) ; // Skip &
}
else // It's
SpaceFound = true ;
}
2014-12-01 23:55:08 +01:00
2016-06-13 20:15:31 +02:00
/***** Skip spaces *****/
if ( isspace ( Ch ) | |
2020-01-23 17:08:57 +01:00
Ch = = 0xC2 | | // Used in Unicode // TODO: Skip '\xA0' or the sequence "\xC2\xA0"
Ch = = 0xA0 ) // Used in Unicode
2016-06-13 20:15:31 +02:00
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
{
2024-06-07 10:44:31 +02:00
if ( ( Ch = ( int ) * * StrSrc ) )
2014-12-01 23:55:08 +01:00
( * StrSrc ) + + ;
}
2015-03-20 14:30:28 +01:00
while ( isspace ( Ch ) | |
2020-01-23 17:08:57 +01:00
Ch = = 0xC2 | | // Used in Unicode // TODO: Skip '\xA0' or the sequence "\xC2\xA0"
Ch = = 0xA0 ) ; // Used in Unicode
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 ) | |
2020-01-23 17:08:57 +01:00
Ch = = 0xC2 | | // Used in Unicode // TODO: Skip '\xA0' or the sequence "\xC2\xA0"
Ch = = 0xA0 ) ) // Used in Unicode
2014-12-01 23:55:08 +01:00
{
if ( i < MaxLength )
StrDst [ i + + ] = ( char ) Ch ;
2024-06-07 10:44:31 +02:00
if ( ( Ch = ( int ) * * StrSrc ) )
2014-12-01 23:55:08 +01:00
( * 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
{
2024-06-07 10:44:31 +02:00
if ( ( Ch = ( int ) * * StrSrc ) )
2014-12-01 23:55:08 +01:00
( * StrSrc ) + + ;
}
2015-03-20 14:30:28 +01:00
while ( isspace ( Ch ) | |
2020-01-23 17:08:57 +01:00
Ch = = 0xC2 | | // Used in Unicode // TODO: Skip '\xA0' or the sequence "\xC2\xA0"
Ch = = 0xA0 | | // Used in Unicode
2015-03-20 14:30:28 +01:00
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 ) | |
2020-01-23 17:08:57 +01:00
Ch = = 0xC2 | | // Used in Unicode // TODO: Skip '\xA0' or the sequence "\xC2\xA0"
Ch = = 0xA0 | | // Used in Unicode
2015-03-20 14:30:28 +01:00
Ch = = ( int ) ' , ' | |
Ch = = ( int ) ' ; ' ) )
2014-12-01 23:55:08 +01:00
{
if ( i < MaxLength )
StrDst [ i + + ] = ( char ) Ch ;
2024-06-07 10:44:31 +02:00
if ( ( Ch = ( int ) * * StrSrc ) )
2014-12-01 23:55:08 +01:00
( * 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 ) | |
2020-01-23 17:08:57 +01:00
Ch = = 0xC2 | | // Used in Unicode // TODO: Skip '\xA0' or the sequence "\xC2\xA0"
Ch = = 0xA0 | | // Used in Unicode
2015-03-20 14:30:28 +01:00
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
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
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 )
2021-04-26 15:27:27 +02:00
Err_ShowErrorAndExit ( " Path is too long. " ) ;
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
/* 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 ;
}
2020-02-27 00:19:55 +01:00
/*****************************************************************************/
/******************* Get parameter with index of question ********************/
/*****************************************************************************/
unsigned Str_ConvertStrToUnsigned ( const char * UnsignedStr )
{
long LongNum = Str_ConvertStrCodToLongCod ( UnsignedStr ) ;
return ( LongNum > = 0 & &
LongNum < = UINT_MAX ) ? ( unsigned ) LongNum :
0 ;
}
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
/**** 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
{
2021-02-15 16:25:55 +01:00
Str_Copy ( PathWithoutFileName , FullPath , PATH_MAX ) ;
2017-01-17 03:10:43 +01:00
PathWithoutFileName [ LengthUntilFileName - 1 ] = ' \0 ' ; // Do not copy ending slash
}
2014-12-01 23:55:08 +01:00
else
PathWithoutFileName [ 0 ] = ' \0 ' ;
/***** Get FileName *****/
2021-02-15 16:25:55 +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 '/' */
2021-11-12 01:12:15 +01:00
return * Path1 = = ' \0 ' | | * Path1 = = ' / ' ;
2014-12-01 23:55:08 +01:00
}
/*****************************************************************************/
/** 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 ) | |
2020-01-23 17:08:57 +01:00
Ch = = 0xC2 | | // Used in Unicode // TODO: Skip '\xA0' or the sequence "\xC2\xA0"
Ch = = 0xA0 ) ) // Used in Unicode
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 for space *************************/
/*****************************************************************************/
void Str_FilePrintStrChangingBRToRetAndNBSPToSpace ( FILE * FileTgt , const char * Str )
{
while ( * Str )
{
/* Is ? */
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
2019-11-22 08:43:05 +01:00
unsigned long long DstLength ;
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 )
2021-04-26 15:27:27 +02:00
Err_ShowErrorAndExit ( " Delimiter string too large. " ) ;
2014-12-01 23:55:08 +01:00
Ptr = StrDst ;
2016-04-01 03:09:45 +02:00
StartIndex = 0 ;
NumBytesReadButNotDiscarded = 0 ;
2019-11-22 08:43:05 +01:00
DstLength = 0 ;
2016-04-01 03:09:45 +02:00
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
2019-11-22 08:43:05 +01:00
if ( DstLength = = MaxLength )
2014-12-01 23:55:08 +01:00
{
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 - - ;
2019-11-22 08:43:05 +01:00
DstLength + + ;
2014-12-01 23:55:08 +01:00
}
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 + + )
2019-11-11 00:15:44 +01:00
Str [ i ] = CharTable [ ( unsigned ) ( ( ( double ) rand ( ) * ( double ) ( NUM_ALPHANUM_CHARS - 1 ) ) /
( double ) RAND_MAX + 0.5 ) ] ;
2014-12-01 23:55:08 +01:00
Str [ Length ] = ' \0 ' ;
}
/*****************************************************************************/
2017-01-17 03:10:43 +01:00
/****************************** Safe string copy *****************************/
2014-12-01 23:55:08 +01:00
/*****************************************************************************/
2021-04-13 23:34:07 +02:00
// DstSize does not include the 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 ] ;
2019-11-22 08:43:05 +01:00
size_t SrcLength = 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 *****/
2019-11-22 08:43:05 +01:00
if ( SrcLength > DstSize )
2017-01-17 03:10:43 +01:00
{
2019-02-17 01:14:55 +01:00
snprintf ( ErrorTxt , sizeof ( ErrorTxt ) ,
2019-11-22 08:43:05 +01:00
" Trying to copy %zu chars into a %zu-chars buffer. " ,
SrcLength , DstSize ) ;
2021-04-26 15:27:27 +02:00
Err_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
{
2019-11-22 08:43:05 +01:00
size_t DstLength ;
size_t SrcLength ;
2017-01-17 03:10:43 +01:00
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 *****/
2019-11-22 08:43:05 +01:00
DstLength = strlen ( Dst ) ;
if ( DstLength > DstSize )
2017-01-17 03:10:43 +01:00
{
2021-02-15 16:25:55 +01:00
snprintf ( ErrorTxt , sizeof ( ErrorTxt ) , " %lu-chars buffer has %lu chars! " ,
2019-11-22 08:43:05 +01:00
DstSize , DstLength ) ;
2021-04-26 15:27:27 +02:00
Err_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 *****/
2019-11-22 08:43:05 +01:00
// DstSize >= DstLength ==> FreeSpace >= 0
FreeSpace = DstSize - DstLength ;
SrcLength = strlen ( Src ) ;
if ( FreeSpace < SrcLength )
2017-01-17 03:10:43 +01:00
{
2019-02-17 01:14:55 +01:00
snprintf ( ErrorTxt , sizeof ( ErrorTxt ) ,
2019-11-22 08:43:05 +01:00
" Trying to concatenate %zu chars to a %zu-chars buffer "
" with free space for only %zu chars! " ,
SrcLength , DstSize , FreeSpace ) ;
2021-04-26 15:27:27 +02:00
Err_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
}
2019-12-30 14:55:25 +01:00
2021-12-15 00:47:29 +01:00
/*****************************************************************************/
2021-12-30 11:50:29 +01:00
/********************** Build a "Go to <where>" title ************************/
2021-12-15 00:47:29 +01:00
/*****************************************************************************/
// Where is a hierarchy member (country, institution, center, degree or course
2021-12-30 11:50:29 +01:00
// Str_FreeGoToTitle() must be called after calling this function
2021-12-15 00:47:29 +01:00
2021-12-30 11:50:29 +01:00
static char * Str_Title = NULL ;
2019-12-30 18:49:52 +01:00
2021-12-30 11:50:29 +01:00
char * Str_BuildGoToTitle ( const char * Where )
2021-12-15 00:47:29 +01:00
{
2021-12-30 11:50:29 +01:00
extern const char * Txt_Go_to_X ;
2019-12-30 20:41:40 +01:00
2021-12-30 11:50:29 +01:00
if ( Str_Title ! = NULL )
Err_ShowErrorAndExit ( " Can not build go to title. " ) ;
2021-12-15 00:47:29 +01:00
2021-12-30 11:50:29 +01:00
if ( asprintf ( & Str_Title , Txt_Go_to_X , Where ) < 0 )
Err_NotEnoughMemoryExit ( ) ;
2019-12-30 20:41:40 +01:00
2021-12-30 11:50:29 +01:00
return Str_Title ;
2019-12-30 20:41:40 +01:00
}
2021-12-30 11:50:29 +01:00
void Str_FreeGoToTitle ( void )
2019-12-30 14:55:25 +01:00
{
2021-12-30 11:50:29 +01:00
free ( Str_Title ) ;
Str_Title = NULL ;
2019-12-30 14:55:25 +01:00
}