2021-08-12 03:28:16 +02:00
/*
* Copyright © 2011 Google , Inc .
*
* This is part of HarfBuzz , a text shaping library .
*
* Permission is hereby granted , without written agreement and without
* license or royalty fees , to use , copy , modify , and distribute this
* software and its documentation for any purpose , provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software .
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT , INDIRECT , SPECIAL , INCIDENTAL , OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION , EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE .
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES , INCLUDING ,
* BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE . THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN " AS IS " BASIS , AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE , SUPPORT , UPDATES , ENHANCEMENTS , OR MODIFICATIONS .
*
* Google Author ( s ) : Behdad Esfahbod
*/
# ifndef TEXT_OPTIONS_HH
# define TEXT_OPTIONS_HH
# include "options.hh"
2021-08-07 21:38:19 +02:00
struct text_options_t
{
2021-08-12 03:06:57 +02:00
text_options_t ( )
: gs ( g_string_new ( nullptr ) )
{ }
2021-08-07 21:38:19 +02:00
~ text_options_t ( )
{
g_free ( text ) ;
g_free ( text_file ) ;
if ( gs )
g_string_free ( gs , true ) ;
2021-08-12 03:48:28 +02:00
if ( in_fp & & in_fp ! = stdin )
fclose ( in_fp ) ;
2021-08-07 21:38:19 +02:00
}
void add_options ( option_parser_t * parser ) ;
void post_parse ( GError * * error G_GNUC_UNUSED )
{
2021-08-12 03:06:57 +02:00
if ( ! text & & ! text_file )
text_file = g_strdup ( " - " ) ;
2021-08-12 02:30:08 +02:00
2021-08-07 21:38:19 +02:00
if ( text & & text_file )
2021-08-12 03:06:57 +02:00
{
2021-08-07 21:38:19 +02:00
g_set_error ( error ,
G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Only one of text and text-file can be set " ) ;
2021-08-12 03:06:57 +02:00
return ;
}
if ( text_file )
{
if ( 0 ! = strcmp ( text_file , " - " ) )
2021-08-12 03:48:28 +02:00
in_fp = fopen ( text_file , " r " ) ;
2021-08-12 03:06:57 +02:00
else
2021-08-12 03:48:28 +02:00
in_fp = stdin ;
2021-08-12 03:06:57 +02:00
2021-08-12 03:48:28 +02:00
if ( ! in_fp )
2021-08-12 03:06:57 +02:00
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_FAILED ,
" Failed opening text file `%s': %s " ,
text_file , strerror ( errno ) ) ;
}
2021-08-07 21:38:19 +02:00
}
2021-08-10 03:08:34 +02:00
const char * get_line ( unsigned int * len ) ;
2021-08-07 21:38:19 +02:00
int text_len = - 1 ;
char * text = nullptr ;
char * text_file = nullptr ;
private :
2021-08-12 03:48:28 +02:00
FILE * in_fp = nullptr ;
2021-08-07 21:38:19 +02:00
GString * gs = nullptr ;
2021-11-25 19:34:24 +01:00
char * line = nullptr ;
unsigned line_len = UINT_MAX ;
2021-11-25 19:49:16 +01:00
hb_bool_t single_par = false ;
2021-08-07 21:38:19 +02:00
} ;
2021-08-12 03:28:16 +02:00
struct shape_text_options_t : text_options_t
{
~ shape_text_options_t ( )
{
g_free ( text_before ) ;
g_free ( text_after ) ;
}
void add_options ( option_parser_t * parser ) ;
char * text_before = nullptr ;
char * text_after = nullptr ;
} ;
2021-08-07 21:38:19 +02:00
2017-09-02 04:09:54 +02:00
static gboolean
parse_text ( const char * name G_GNUC_UNUSED ,
const char * arg ,
gpointer data ,
GError * * error G_GNUC_UNUSED )
{
text_options_t * text_opts = ( text_options_t * ) data ;
if ( text_opts - > text )
{
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Either --text or --unicodes can be provided but not both " ) ;
return false ;
}
2018-11-06 16:49:19 +01:00
text_opts - > text_len = - 1 ;
2017-09-02 04:09:54 +02:00
text_opts - > text = g_strdup ( arg ) ;
return true ;
}
2021-08-16 14:52:13 +02:00
static bool
encode_unicodes ( const char * unicodes ,
GString * gs ,
GError * * error )
{
2022-05-31 12:09:06 +02:00
# define DELIMITERS "<+-|>{},;&#\\xXuUnNiI\n\t\v\f\r "
2021-08-16 14:52:13 +02:00
char * s = ( char * ) unicodes ;
char * p ;
while ( s & & * s )
{
while ( * s & & strchr ( DELIMITERS , * s ) )
s + + ;
if ( ! * s )
break ;
errno = 0 ;
hb_codepoint_t u = strtoul ( s , & p , 16 ) ;
if ( errno | | s = = p )
{
g_string_free ( gs , TRUE ) ;
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Failed parsing Unicode value at: '%s' " , s ) ;
return false ;
}
g_string_append_unichar ( gs , u ) ;
s = p ;
}
# undef DELIMITERS
return true ;
}
2017-09-02 04:09:54 +02:00
static gboolean
parse_unicodes ( const char * name G_GNUC_UNUSED ,
2019-08-24 15:27:14 +02:00
const char * arg ,
gpointer data ,
GError * * error G_GNUC_UNUSED )
2017-09-02 04:09:54 +02:00
{
text_options_t * text_opts = ( text_options_t * ) data ;
if ( text_opts - > text )
{
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Either --text or --unicodes can be provided but not both " ) ;
return false ;
}
2017-10-15 12:11:08 +02:00
GString * gs = g_string_new ( nullptr ) ;
2019-08-24 15:27:14 +02:00
if ( 0 = = strcmp ( arg , " * " ) )
2019-06-26 22:23:24 +02:00
g_string_append_c ( gs , ' * ' ) ;
else
2021-08-16 14:52:13 +02:00
if ( ! encode_unicodes ( arg , gs , error ) )
return false ;
text_opts - > text_len = gs - > len ;
text_opts - > text = g_string_free ( gs , FALSE ) ;
return true ;
}
static gboolean
parse_text_before ( const char * name G_GNUC_UNUSED ,
const char * arg ,
gpointer data ,
GError * * error )
{
auto * opts = ( shape_text_options_t * ) data ;
if ( opts - > text_before )
2019-06-26 22:23:24 +02:00
{
2021-08-16 14:52:13 +02:00
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Either --text-before or --unicodes-before can be provided but not both " ) ;
return false ;
}
2019-06-26 22:23:24 +02:00
2021-08-16 14:52:13 +02:00
opts - > text_before = g_strdup ( arg ) ;
fprintf ( stderr , " %s \n " , opts - > text_before ) ;
return true ;
}
2019-08-24 15:27:14 +02:00
2021-08-16 14:52:13 +02:00
static gboolean
parse_unicodes_before ( const char * name G_GNUC_UNUSED ,
const char * arg ,
gpointer data ,
GError * * error )
{
auto * opts = ( shape_text_options_t * ) data ;
if ( opts - > text_before )
{
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Either --text-before or --unicodes-before can be provided but not both " ) ;
return false ;
2017-09-02 04:09:54 +02:00
}
2021-08-16 14:52:13 +02:00
GString * gs = g_string_new ( nullptr ) ;
if ( ! encode_unicodes ( arg , gs , error ) )
return false ;
opts - > text_before = g_string_free ( gs , FALSE ) ;
return true ;
}
static gboolean
parse_text_after ( const char * name G_GNUC_UNUSED ,
const char * arg ,
gpointer data ,
GError * * error )
{
auto * opts = ( shape_text_options_t * ) data ;
if ( opts - > text_after )
{
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Either --text-after or --unicodes-after can be provided but not both " ) ;
return false ;
}
opts - > text_after = g_strdup ( arg ) ;
return true ;
}
static gboolean
parse_unicodes_after ( const char * name G_GNUC_UNUSED ,
const char * arg ,
gpointer data ,
GError * * error )
{
auto * opts = ( shape_text_options_t * ) data ;
if ( opts - > text_after )
{
g_set_error ( error , G_OPTION_ERROR , G_OPTION_ERROR_BAD_VALUE ,
" Either --text-after or --unicodes-after can be provided but not both " ) ;
return false ;
}
GString * gs = g_string_new ( nullptr ) ;
if ( ! encode_unicodes ( arg , gs , error ) )
return false ;
opts - > text_after = g_string_free ( gs , FALSE ) ;
2017-09-02 04:09:54 +02:00
return true ;
}
2011-09-13 19:30:39 +02:00
const char *
2021-08-10 03:08:34 +02:00
text_options_t : : get_line ( unsigned int * len )
2011-09-13 19:30:39 +02:00
{
2021-08-07 21:38:19 +02:00
if ( text )
{
2021-11-25 19:34:24 +01:00
if ( ! line )
{
line = text ;
line_len = text_len ;
}
if ( line_len = = UINT_MAX )
line_len = strlen ( line ) ;
if ( ! line_len )
2018-11-06 17:03:34 +01:00
{
2011-09-16 08:08:36 +02:00
* len = 0 ;
2017-10-15 12:11:08 +02:00
return nullptr ;
2011-09-16 08:08:36 +02:00
}
2021-11-25 19:34:24 +01:00
const char * ret = line ;
2021-11-25 19:49:16 +01:00
const char * p = single_par ? nullptr : ( const char * ) memchr ( line , ' \n ' , line_len ) ;
2021-11-25 19:34:24 +01:00
unsigned int ret_len ;
if ( ! p )
{
ret_len = line_len ;
line + = ret_len ;
line_len = 0 ;
}
else
{
ret_len = p - ret ;
line + = ret_len + 1 ;
line_len - = ret_len + 1 ;
}
2011-09-16 08:08:36 +02:00
2021-11-25 19:34:24 +01:00
* len = ret_len ;
return ret ;
2011-09-16 08:08:36 +02:00
}
g_string_set_size ( gs , 0 ) ;
char buf [ BUFSIZ ] ;
2021-08-12 03:48:28 +02:00
while ( fgets ( buf , sizeof ( buf ) , in_fp ) )
2021-08-01 15:59:25 +02:00
{
unsigned bytes = strlen ( buf ) ;
2021-11-25 19:49:16 +01:00
if ( ! single_par & & bytes & & buf [ bytes - 1 ] = = ' \n ' )
2021-08-01 15:59:25 +02:00
{
2011-09-16 08:08:36 +02:00
bytes - - ;
g_string_append_len ( gs , buf , bytes ) ;
break ;
}
2021-08-01 15:59:25 +02:00
g_string_append_len ( gs , buf , bytes ) ;
2011-08-11 11:54:31 +02:00
}
2021-08-12 03:48:28 +02:00
if ( ferror ( in_fp ) )
2021-08-01 15:59:25 +02:00
fail ( false , " Failed reading text: %s " , strerror ( errno ) ) ;
2011-09-16 08:08:36 +02:00
* len = gs - > len ;
2021-08-12 03:48:28 +02:00
return ! * len & & feof ( in_fp ) ? nullptr : gs - > str ;
2011-08-11 11:54:31 +02:00
}
2021-08-07 21:38:19 +02:00
void
text_options_t : : add_options ( option_parser_t * parser )
{
GOptionEntry entries [ ] =
{
{ " text " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_text , " Set input text " , " string " } ,
2021-08-10 21:59:46 +02:00
{ " text-file " , 0 , 0 , G_OPTION_ARG_STRING , & this - > text_file , " Set input text file-name " , " filename " } ,
2021-11-25 19:49:16 +01:00
{ " unicodes " , ' u ' , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_unicodes , " Set input Unicode codepoints " , " list of hex numbers " } ,
{ " single-par " , 0 , 0 , G_OPTION_ARG_NONE , & this - > single_par , " Treat text as single paragraph " , nullptr } ,
2021-08-07 21:38:19 +02:00
{ nullptr }
} ;
parser - > add_group ( entries ,
" text " ,
2021-11-25 19:49:16 +01:00
" Text options: \n \n If no text is provided, standard input is used for input. \n " ,
2021-08-07 21:38:19 +02:00
" Options for the input text " ,
this ) ;
}
2021-08-12 03:28:16 +02:00
void
shape_text_options_t : : add_options ( option_parser_t * parser )
{
text_options_t : : add_options ( parser ) ;
GOptionEntry entries [ ] =
{
2021-08-16 14:52:13 +02:00
{ " text-before " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_text_before , " Set text context before each line " , " string " } ,
{ " text-after " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_text_after , " Set text context after each line " , " string " } ,
{ " unicodes-before " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_unicodes_before , " Set Unicode codepoints context before each line " , " list of hex numbers " } ,
{ " unicodes-after " , 0 , 0 , G_OPTION_ARG_CALLBACK , ( gpointer ) & parse_unicodes_after , " Set Unicode codepoints context after each line " , " list of hex numbers " } ,
2021-08-12 03:28:16 +02:00
{ nullptr }
} ;
parser - > add_group ( entries ,
" text-context " ,
" Textual context options: " ,
" Options for the input context text " ,
this ) ;
}
# endif