| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | /*  Part of SWI-Prolog
 | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Author:        Jan Wielemaker | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |     E-mail:        J.Wielemaker@vu.nl | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |     WWW:           http://www.swi-prolog.org
 | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |     Copyright (C): 1985-2013, University of Amsterdam | 
					
						
							|  |  |  | 			      VU University Amsterdam | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     This library is free software; you can redistribute it and/or | 
					
						
							|  |  |  |     modify it under the terms of the GNU Lesser General Public | 
					
						
							|  |  |  |     License as published by the Free Software Foundation; either | 
					
						
							|  |  |  |     version 2.1 of the License, or (at your option) any later version. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This library 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 | 
					
						
							|  |  |  |     Lesser General Public License for more details. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     You should have received a copy of the GNU Lesser General Public | 
					
						
							|  |  |  |     License along with this library; if not, write to the Free Software | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  |     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-15 03:13:50 -05:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2014-09-15 13:10:49 -05:00
										 |  |  |  * @defgroup Format Formatted Output | 
					
						
							|  |  |  |  * @ingroup InputOutput | 
					
						
							| 
									
										
										
										
											2014-09-15 03:13:50 -05:00
										 |  |  |  * @{ | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
					
						
							|  |  |  | Formatted output (Prolog predicates format/[1,2,3]).   One  day,  the  C | 
					
						
							|  |  |  | source should also use format() to produce error messages, etc. | 
					
						
							|  |  |  | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "pl-incl.h"
 | 
					
						
							|  |  |  | #include "pl-ctype.h"
 | 
					
						
							|  |  |  | #include "pl-utf8.h"
 | 
					
						
							|  |  |  | #include <ctype.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | static char *	formatInteger(PL_locale *locale, int div, int radix, | 
					
						
							| 
									
										
										
										
											2011-03-08 00:03:50 +00:00
										 |  |  | 			     bool smll, Number n, Buffer out); | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | static char *	formatFloat(PL_locale *locale, int how, int arg, | 
					
						
							|  |  |  | 			    Number f, Buffer out); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define MAXRUBBER 100
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct rubber | 
					
						
							|  |  |  | { size_t where;				/* where is rubber in output */ | 
					
						
							|  |  |  |   size_t size;				/* how big should it be */ | 
					
						
							|  |  |  |   pl_wchar_t pad;			/* padding character */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct | 
					
						
							|  |  |  | { IOSTREAM *out;			/* our output stream */ | 
					
						
							|  |  |  |   int column;				/* current column */ | 
					
						
							|  |  |  |   tmp_buffer buffer;			/* bin for characters with tabs */ | 
					
						
							|  |  |  |   size_t buffered;			/* characters in buffer */ | 
					
						
							|  |  |  |   int pending_rubber;			/* number of not-filled ~t's */ | 
					
						
							|  |  |  |   struct rubber rub[MAXRUBBER]; | 
					
						
							|  |  |  | } format_state; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | #define BUFSIZE		1024
 | 
					
						
							|  |  |  | #define DEFAULT		(-1)
 | 
					
						
							|  |  |  | #define SHIFT		{ argc--; argv++; }
 | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | #define NEED_ARG	{ if ( argc <= 0 ) \
 | 
					
						
							|  |  |  | 			  { FMT_ERROR("not enough arguments"); \ | 
					
						
							|  |  |  | 			  } \ | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | #define FMT_ERROR(fmt)	return (void)Sunlock(fd), \
 | 
					
						
							|  |  |  | 			  PL_error(NULL, 0, NULL, ERR_FORMAT, fmt) | 
					
						
							|  |  |  | #define FMT_ARG(c, a)	return (void)Sunlock(fd), \
 | 
					
						
							|  |  |  | 			       PL_error(NULL, 0, NULL, \ | 
					
						
							|  |  |  | 					ERR_FORMAT_ARG, c, a) | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | #define FMT_EXEPTION()	return (void)Sunlock(fd), FALSE
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static PL_locale prolog_locale = | 
					
						
							|  |  |  | { 0,0,LOCALE_MAGIC,1, | 
					
						
							|  |  |  |   L".", NULL | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | update_column(int col, int c) | 
					
						
							|  |  |  | { switch(c) | 
					
						
							|  |  |  |   { case '\n':	return 0; | 
					
						
							|  |  |  |     case '\r':  return 0; | 
					
						
							|  |  |  |     case '\t':	return (col + 1) | 0x7; | 
					
						
							|  |  |  |     case '\b':	return (col <= 0 ? 0 : col - 1); | 
					
						
							|  |  |  |     default:	return col + 1; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
					
						
							|  |  |  | Low-level output. If there is pending  rubber   the  output is stored in | 
					
						
							|  |  |  | UTF-8 format in the state's `buffer'.   The  `buffered' field represents | 
					
						
							|  |  |  | the number of UTF-8 characters in the buffer. | 
					
						
							|  |  |  | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | outchr(format_state *state, int chr) | 
					
						
							|  |  |  | { if ( state->pending_rubber ) | 
					
						
							|  |  |  |   { if ( chr > 0x7f ) | 
					
						
							|  |  |  |     { char buf[8]; | 
					
						
							|  |  |  |       char *s, *e; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       e = utf8_put_char(buf, chr); | 
					
						
							|  |  |  |       for(s=buf; s<e; s++) | 
					
						
							|  |  |  | 	addBuffer((Buffer)&state->buffer, *s, char); | 
					
						
							|  |  |  |     } else | 
					
						
							|  |  |  |     { char c = chr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       addBuffer((Buffer)&state->buffer, c, char); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     state->buffered++; | 
					
						
							|  |  |  |   } else | 
					
						
							|  |  |  |   { if ( Sputcode(chr, state->out) < 0 ) | 
					
						
							|  |  |  |       return FALSE; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   state->column = update_column(state->column, chr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return TRUE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
					
						
							|  |  |  | Emit ASCII 0-terminated strings resulting from sprintf() on numeric | 
					
						
							|  |  |  | arguments.  No fuzz with wide characters here. | 
					
						
							|  |  |  | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | outstring(format_state *state, const char *s, size_t len) | 
					
						
							|  |  |  | { const char *q; | 
					
						
							|  |  |  |   const char *e = &s[len]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ( state->pending_rubber ) | 
					
						
							|  |  |  |   { addMultipleBuffer(&state->buffer, s, len, char); | 
					
						
							|  |  |  |     state->buffered += len; | 
					
						
							|  |  |  |   } else | 
					
						
							|  |  |  |   { for(q=s; q < e; q++) | 
					
						
							|  |  |  |     { if ( Sputcode(*q&0xff, state->out) < 0 ) | 
					
						
							|  |  |  | 	return FALSE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for(q=s; q < e; q++) | 
					
						
							|  |  |  |     state->column = update_column(state->column, *q&0xff); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return TRUE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | oututf8(format_state *state, const char *s, size_t len) | 
					
						
							|  |  |  | { const char *e = &s[len]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while(s<e) | 
					
						
							|  |  |  |   { int chr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     s = utf8_get_char(s, &chr); | 
					
						
							|  |  |  |     if ( !outchr(state, chr) ) | 
					
						
							|  |  |  |       return FALSE; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return TRUE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | static int | 
					
						
							|  |  |  | oututf80(format_state *state, const char *s) | 
					
						
							|  |  |  | { return oututf8(state, s, strlen(s)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | static int | 
					
						
							|  |  |  | outtext(format_state *state, PL_chars_t *txt) | 
					
						
							|  |  |  | { switch(txt->encoding) | 
					
						
							|  |  |  |   { case ENC_ISO_LATIN_1: | 
					
						
							|  |  |  |       return outstring(state, txt->text.t, txt->length); | 
					
						
							| 
									
										
										
										
											2014-02-03 23:27:06 +00:00
										 |  |  |     case ENC_UTF8: | 
					
						
							|  |  |  |       return oututf8(state, txt->text.t, txt->length); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |     case ENC_WCHAR: | 
					
						
							|  |  |  |     { const pl_wchar_t *s = txt->text.w; | 
					
						
							|  |  |  |       const pl_wchar_t *e = &s[txt->length]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       while(s<e) | 
					
						
							|  |  |  |       { if ( !outchr(state, *s++) ) | 
					
						
							|  |  |  | 	  return FALSE; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return TRUE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |     { assert(0); | 
					
						
							|  |  |  |       return FALSE; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define format_predicates (GD->format.predicates)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int	update_column(int, Char); | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | static bool	do_format(IOSTREAM *fd, PL_chars_t *fmt, | 
					
						
							|  |  |  | 			  int ac, term_t av, Module m); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | static void	distribute_rubber(struct rubber *, int, int); | 
					
						
							|  |  |  | static int	emit_rubber(format_state *state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/********************************
 | 
					
						
							|  |  |  | 		*       PROLOG CONNECTION	* | 
					
						
							|  |  |  | 		********************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | word | 
					
						
							|  |  |  | pl_format_predicate(term_t chr, term_t descr) | 
					
						
							|  |  |  | { int c; | 
					
						
							| 
									
										
										
										
											2011-03-02 09:18:51 +00:00
										 |  |  |   predicate_t proc = NULL; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |   Symbol s; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:02:05 +00:00
										 |  |  |   int arity; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if ( !PL_get_char_ex(chr, &c, FALSE) ) | 
					
						
							|  |  |  |     fail; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ( !get_procedure(descr, &proc, 0, GP_CREATE) ) | 
					
						
							|  |  |  |     fail; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:02:05 +00:00
										 |  |  |   PL_predicate_info(proc, NULL, &arity, NULL); | 
					
						
							|  |  |  |   if ( arity == 0 ) | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |     return PL_error(NULL, 0, "arity must be > 0", ERR_DOMAIN, | 
					
						
							|  |  |  | 		    PL_new_atom("format_predicate"), | 
					
						
							|  |  |  | 		    descr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ( !format_predicates ) | 
					
						
							|  |  |  |     format_predicates = newHTable(8); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ( (s = lookupHTable(format_predicates, (void *)(intptr_t)c)) ) | 
					
						
							|  |  |  |     s->value = proc; | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     addHTable(format_predicates, (void *)(intptr_t)c, proc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   succeed; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | word | 
					
						
							|  |  |  | pl_current_format_predicate(term_t chr, term_t descr, control_t h) | 
					
						
							|  |  |  | { GET_LD | 
					
						
							|  |  |  |   Symbol s = NULL; | 
					
						
							|  |  |  |   TableEnum e; | 
					
						
							|  |  |  |   fid_t fid; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   switch( ForeignControl(h) ) | 
					
						
							|  |  |  |   { case FRG_FIRST_CALL: | 
					
						
							|  |  |  |       if ( !format_predicates ) | 
					
						
							|  |  |  | 	fail; | 
					
						
							|  |  |  |       e = newTableEnum(format_predicates); | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     case FRG_REDO: | 
					
						
							|  |  |  |       e = ForeignContextPtr(h); | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     case FRG_CUTTED: | 
					
						
							|  |  |  |       e = ForeignContextPtr(h); | 
					
						
							|  |  |  |       freeTableEnum(e); | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |       succeed; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ( !(fid = PL_open_foreign_frame()) ) | 
					
						
							|  |  |  |   { freeTableEnum(e); | 
					
						
							|  |  |  |     return FALSE; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   while( (s=advanceTableEnum(e)) ) | 
					
						
							|  |  |  |   { if ( PL_unify_integer(chr, (intptr_t)s->name) && | 
					
						
							| 
									
										
										
										
											2011-02-10 00:02:05 +00:00
										 |  |  | 	 PL_unify_predicate(descr, (predicate_t)s->value, 0) ) | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |     { PL_close_foreign_frame(fid); | 
					
						
							|  |  |  |       ForeignRedoPtr(e); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     PL_rewind_foreign_frame(fid); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   PL_close_foreign_frame(fid); | 
					
						
							|  |  |  |   freeTableEnum(e); | 
					
						
							|  |  |  |   fail; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static word | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | format_impl(IOSTREAM *out, term_t format, term_t Args, Module m) | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | { GET_LD | 
					
						
							|  |  |  |   term_t argv; | 
					
						
							|  |  |  |   int argc = 0; | 
					
						
							|  |  |  |   term_t args = PL_copy_term_ref(Args); | 
					
						
							|  |  |  |   int rval; | 
					
						
							|  |  |  |   PL_chars_t fmt; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ( !PL_get_text(format, &fmt, CVT_ALL|BUF_RING) ) | 
					
						
							|  |  |  |     return PL_error("format", 3, NULL, ERR_TYPE, ATOM_text, format); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ( (argc = (int)lengthList(args, FALSE)) >= 0 ) | 
					
						
							|  |  |  |   { term_t head = PL_new_term_ref(); | 
					
						
							|  |  |  |     int n = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     argv = PL_new_term_refs(argc); | 
					
						
							|  |  |  |     while( PL_get_list(args, head, args) ) | 
					
						
							|  |  |  |       PL_put_term(argv+n++, head); | 
					
						
							|  |  |  |   } else | 
					
						
							|  |  |  |   { argc = 1; | 
					
						
							|  |  |  |     argv = PL_new_term_refs(argc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     PL_put_term(argv, args); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   startCritical; | 
					
						
							|  |  |  |   switch(fmt.storage)			/* format can do call-back! */ | 
					
						
							|  |  |  |   { case PL_CHARS_RING: | 
					
						
							|  |  |  |     case PL_CHARS_STACK: | 
					
						
							|  |  |  |       PL_save_text(&fmt, BUF_MALLOC); | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  |   rval = do_format(out, &fmt, argc, argv, m); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |   PL_free_text(&fmt); | 
					
						
							|  |  |  |   if ( !endCritical ) | 
					
						
							|  |  |  |     return FALSE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return rval; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-15 03:13:50 -05:00
										 |  |  | /** @pred  format(+ _S_,+ _T_,+ _L_)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Print formatted output to stream  _S_. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | */ | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | word | 
					
						
							|  |  |  | pl_format3(term_t out, term_t format, term_t args) | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | { GET_LD | 
					
						
							|  |  |  |   redir_context ctx; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |   word rc; | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  |   Module m = NULL; | 
					
						
							|  |  |  |   term_t list = PL_new_term_ref(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ( !PL_strip_module(args, &m, list) ) | 
					
						
							|  |  |  |     return FALSE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ( (rc=setupOutputRedirect(out, &ctx, FALSE)) ) | 
					
						
							|  |  |  |   { if ( (rc = format_impl(ctx.stream, format, list, m)) ) | 
					
						
							|  |  |  |       rc = closeOutputRedirect(&ctx); | 
					
						
							|  |  |  |     else | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |       discardOutputRedirect(&ctx); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return rc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-15 03:13:50 -05:00
										 |  |  | /** @pred  format(+ _T_,+ _L_)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Print formatted output to the current output stream. The arguments in | 
					
						
							|  |  |  | list  _L_ are output according to the string or atom  _T_. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | A control sequence is introduced by a `w`. The following control | 
					
						
							|  |  |  | sequences are available in YAP: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~~` | 
					
						
							|  |  |  | Print a single tilde. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~a` | 
					
						
							|  |  |  | The next argument must be an atom, that will be printed as if by `write`. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~Nc` | 
					
						
							|  |  |  | The next argument must be an integer, that will be printed as a | 
					
						
							|  |  |  | character code. The number  _N_ is the number of times to print the | 
					
						
							|  |  |  | character (default 1). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~Ne` | 
					
						
							|  |  |  | + `~NE` | 
					
						
							|  |  |  | + `~Nf` | 
					
						
							|  |  |  | + `~Ng` | 
					
						
							|  |  |  | + `~NG` | 
					
						
							|  |  |  | The next argument must be a floating point number. The float  _F_, the number | 
					
						
							|  |  |  |  _N_ and the control code `c` will be passed to `printf` as: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~{.prolog} | 
					
						
							|  |  |  |     printf("%s.Nc", F) | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | As an example: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~{.prolog} | 
					
						
							|  |  |  | ?- format("~8e, ~8E, ~8f, ~8g, ~8G~w", | 
					
						
							|  |  |  |           [3.14,3.14,3.14,3.14,3.14,3.14]). | 
					
						
							|  |  |  | 3.140000e+00, 3.140000E+00, 3.140000, 3.14, 3.143.14 | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~Nd` | 
					
						
							|  |  |  | The next argument must be an integer, and  _N_ is the number of digits | 
					
						
							|  |  |  | after the decimal point. If  _N_ is `0` no decimal points will be | 
					
						
							|  |  |  | printed. The default is  _N = 0_. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~{.prolog} | 
					
						
							|  |  |  | ?- format("~2d, ~d",[15000, 15000]). | 
					
						
							|  |  |  | 150.00, 15000 | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~ND` | 
					
						
							|  |  |  | Identical to `~Nd`, except that commas are used to separate groups | 
					
						
							|  |  |  | of three digits. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~{.prolog} | 
					
						
							|  |  |  | ?- format("~2D, ~D",[150000, 150000]). | 
					
						
							|  |  |  | 1,500.00, 150,000 | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~i` | 
					
						
							|  |  |  | Ignore the next argument in the list of arguments: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~{.prolog} | 
					
						
							|  |  |  | ?- format('The ~i met the boregrove',[mimsy]). | 
					
						
							|  |  |  | The  met the boregrove | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~k` | 
					
						
							|  |  |  | Print the next argument with `write_canonical`: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~{.prolog} | 
					
						
							|  |  |  | ?- format("Good night ~k",a+[1,2]). | 
					
						
							|  |  |  | Good night +(a,[1,2]) | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~Nn` | 
					
						
							|  |  |  | Print  _N_ newlines (where  _N_ defaults to 1). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~NN` | 
					
						
							|  |  |  | Print  _N_ newlines if at the beginning of the line (where  _N_ | 
					
						
							|  |  |  | defaults to 1). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~Nr` | 
					
						
							|  |  |  | The next argument must be an integer, and  _N_ is interpreted as a | 
					
						
							|  |  |  | radix, such that `2 <= N <= 36` (the default is 8). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~{.prolog} | 
					
						
							|  |  |  | ?- format("~2r, 0x~16r, ~r", | 
					
						
							|  |  |  |           [150000, 150000, 150000]). | 
					
						
							|  |  |  | 100100100111110000, 0x249f0, 444760 | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Note that the letters `a-z` denote digits larger than 9. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~NR` | 
					
						
							|  |  |  | Similar to `~NR`. The next argument must be an integer, and  _N_ is | 
					
						
							|  |  |  | interpreted as a radix, such that `2 <= N <= 36` (the default is 8). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~{.prolog} | 
					
						
							|  |  |  | ?- format("~2r, 0x~16r, ~r", | 
					
						
							|  |  |  |           [150000, 150000, 150000]). | 
					
						
							|  |  |  | 100100100111110000, 0x249F0, 444760 | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-09-15 03:13:50 -05:00
										 |  |  | The only difference is that letters `A-Z` denote digits larger than 9. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~p` | 
					
						
							|  |  |  | Print the next argument with print/1: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~{.prolog} | 
					
						
							|  |  |  | ?- format("Good night ~p",a+[1,2]). | 
					
						
							|  |  |  | Good night a+[1,2] | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~q` | 
					
						
							|  |  |  | Print the next argument with writeq/1: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~{.prolog} | 
					
						
							|  |  |  | ?- format("Good night ~q",'Hello'+[1,2]). | 
					
						
							|  |  |  | Good night 'Hello'+[1,2] | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~Ns` | 
					
						
							|  |  |  | The next argument must be a list of character codes. The system then | 
					
						
							|  |  |  | outputs their representation as a string, where  _N_ is the maximum | 
					
						
							|  |  |  | number of characters for the string ( _N_ defaults to the length of the | 
					
						
							|  |  |  | string). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~{.prolog} | 
					
						
							|  |  |  | ?- format("The ~s are ~4s",["woods","lovely"]). | 
					
						
							|  |  |  | The woods are love | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~w` | 
					
						
							|  |  |  | Print the next argument with write/1: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | ?- format("Good night ~w",'Hello'+[1,2]). | 
					
						
							|  |  |  | Good night Hello+[1,2] | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The number of arguments, `N`, may be given as an integer, or it | 
					
						
							|  |  |  | may be given as an extra argument. The next example shows a small | 
					
						
							|  |  |  | procedure to write a variable number of `a` characters: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | write_many_as(N) :- | 
					
						
							|  |  |  |         format("~*c",[N,0'a]). | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The format/2 built-in also allows for formatted output.  One can | 
					
						
							|  |  |  | specify column boundaries and fill the intermediate space by a padding | 
					
						
							|  |  |  | character: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~N|` | 
					
						
							|  |  |  | Set a column boundary at position  _N_, where  _N_ defaults to the | 
					
						
							|  |  |  | current position. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~N+` | 
					
						
							|  |  |  | Set a column boundary at  _N_ characters past the current position, where | 
					
						
							|  |  |  |  _N_ defaults to `8`. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + `~Nt` | 
					
						
							|  |  |  | Set padding for a column, where  _N_ is the fill code (default is | 
					
						
							|  |  |  | `SPC`). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The next example shows how to align columns and padding. We first show | 
					
						
							|  |  |  | left-alignment: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  |    ?- format("~n*Hello~16+*~n",[]). | 
					
						
							|  |  |  | *Hello          * | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Note that we reserve 16 characters for the column. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The following example shows how to do right-alignment: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  |    ?- format("*~tHello~16+*~n",[]). | 
					
						
							|  |  |  | *          Hello* | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The `~t` escape sequence forces filling before `Hello`. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | We next show how to do centering: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  |    ?- format("*~tHello~t~16+*~n",[]). | 
					
						
							|  |  |  | *     Hello     * | 
					
						
							|  |  |  | ~~~~~ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The two `~t` escape sequence force filling both before and after | 
					
						
							|  |  |  | `Hello`. Space is then evenly divided between the right and the | 
					
						
							|  |  |  | left sides. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | */ | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | word | 
					
						
							|  |  |  | pl_format(term_t fmt, term_t args) | 
					
						
							|  |  |  | { return pl_format3(0, fmt, args); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline int | 
					
						
							|  |  |  | get_chr_from_text(const PL_chars_t *t, int index) | 
					
						
							|  |  |  | { switch(t->encoding) | 
					
						
							|  |  |  |   { case ENC_ISO_LATIN_1: | 
					
						
							|  |  |  |       return t->text.t[index]&0xff; | 
					
						
							|  |  |  |     case ENC_WCHAR: | 
					
						
							|  |  |  |       return t->text.w[index]; | 
					
						
							| 
									
										
										
										
											2014-09-09 23:53:10 -05:00
										 |  |  |     case ENC_UTF8: | 
					
						
							|  |  |  |       return t->text.w[index]; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |     default: | 
					
						
							|  |  |  |       assert(0); | 
					
						
							|  |  |  |       return 0;				/* not reached */ | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/********************************
 | 
					
						
							|  |  |  | 		*       ACTUAL FORMATTING	* | 
					
						
							|  |  |  | 		********************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | do_format(IOSTREAM *fd, PL_chars_t *fmt, int argc, term_t argv, Module m) | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | { GET_LD | 
					
						
							|  |  |  |   format_state state;			/* complete state */ | 
					
						
							|  |  |  |   int tab_stop = 0;			/* padded tab stop */ | 
					
						
							|  |  |  |   Symbol s; | 
					
						
							|  |  |  |   unsigned int here = 0; | 
					
						
							|  |  |  |   int rc = TRUE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Slock(fd);				/* buffer locally */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   state.out = fd; | 
					
						
							|  |  |  |   state.pending_rubber = 0; | 
					
						
							|  |  |  |   initBuffer(&state.buffer); | 
					
						
							|  |  |  |   state.buffered = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ( fd->position ) | 
					
						
							|  |  |  |     state.column = fd->position->linepos; | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     state.column = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while(here < fmt->length) | 
					
						
							|  |  |  |   { int c = get_chr_from_text(fmt, here); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch(c) | 
					
						
							|  |  |  |     { case '~': | 
					
						
							|  |  |  | 	{ int arg = DEFAULT;		/* Numeric argument */ | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 	  int mod_colon = FALSE;	/* Used colon modifier */ | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 					/* Get the numeric argument */ | 
					
						
							|  |  |  | 	  c = get_chr_from_text(fmt, ++here); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	  if ( isDigitW(c) ) | 
					
						
							|  |  |  | 	  { arg = c - '0'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	    here++; | 
					
						
							|  |  |  | 	    while(here < fmt->length) | 
					
						
							|  |  |  | 	    { c = get_chr_from_text(fmt, here); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	      if ( isDigitW(c) ) | 
					
						
							|  |  |  | 	      { int dw = c - '0'; | 
					
						
							|  |  |  | 		int arg2 = arg*10 + dw; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if ( (arg2 - dw)/10 != arg )	/* see mul64() in pl-arith.c */ | 
					
						
							|  |  |  | 		{ FMT_ERROR("argument overflow"); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		arg = arg2; | 
					
						
							|  |  |  | 		here++; | 
					
						
							|  |  |  | 	      } else | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	    } | 
					
						
							|  |  |  | 	  } else if ( c == '*' ) | 
					
						
							|  |  |  | 	  { NEED_ARG; | 
					
						
							|  |  |  | 	    if ( PL_get_integer(argv, &arg) ) | 
					
						
							|  |  |  | 	    { SHIFT; | 
					
						
							|  |  |  | 	    } else | 
					
						
							|  |  |  | 	      FMT_ERROR("no or negative integer for `*' argument"); | 
					
						
							|  |  |  | 	    c = get_chr_from_text(fmt, ++here); | 
					
						
							|  |  |  | 	  } else if ( c == '`' && here < fmt->length ) | 
					
						
							|  |  |  | 	  { arg = get_chr_from_text(fmt, ++here); | 
					
						
							|  |  |  | 	    c = get_chr_from_text(fmt, ++here); | 
					
						
							|  |  |  | 	  } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 	  if ( c == ':' ) | 
					
						
							|  |  |  | 	  { mod_colon = TRUE; | 
					
						
							|  |  |  | 	    c = get_chr_from_text(fmt, ++here); | 
					
						
							|  |  |  | 	  } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 					/* Check for user defined format */ | 
					
						
							|  |  |  | 	  if ( format_predicates && | 
					
						
							|  |  |  | 	       (s = lookupHTable(format_predicates, (void*)((intptr_t)c))) ) | 
					
						
							| 
									
										
										
										
											2011-02-10 00:02:05 +00:00
										 |  |  | 	  { predicate_t proc = (predicate_t) s->value; | 
					
						
							|  |  |  | 	    int arity; | 
					
						
							|  |  |  | 	    term_t av; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 	    char buf[BUFSIZE]; | 
					
						
							|  |  |  | 	    char *str = buf; | 
					
						
							|  |  |  | 	    size_t bufsize = BUFSIZE; | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | 	    int i; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-10 00:02:05 +00:00
										 |  |  | 	    PL_predicate_info(proc, NULL, &arity, NULL); | 
					
						
							|  |  |  | 	    av = PL_new_term_refs(arity); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 	    if ( arg == DEFAULT ) | 
					
						
							|  |  |  | 	      PL_put_atom(av+0, ATOM_default); | 
					
						
							|  |  |  | 	    else | 
					
						
							|  |  |  | 	      PL_put_integer(av+0, arg); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-10 00:02:05 +00:00
										 |  |  | 	    for(i=1; i < arity; i++) | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 	    { NEED_ARG; | 
					
						
							|  |  |  | 	      PL_put_term(av+i, argv); | 
					
						
							|  |  |  | 	      SHIFT; | 
					
						
							|  |  |  | 	    } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	    tellString(&str, &bufsize, ENC_UTF8); | 
					
						
							|  |  |  | 	    rc = PL_call_predicate(NULL, PL_Q_PASS_EXCEPTION, proc, av); | 
					
						
							|  |  |  | 	    toldString(); | 
					
						
							|  |  |  | 	    if ( !rc ) | 
					
						
							|  |  |  | 	    { if ( str != buf ) | 
					
						
							|  |  |  | 		free(str); | 
					
						
							|  |  |  | 	      goto out; | 
					
						
							|  |  |  | 	    } | 
					
						
							|  |  |  | 	    oututf8(&state, str, bufsize); | 
					
						
							|  |  |  | 	    if ( str != buf ) | 
					
						
							|  |  |  | 	      free(str); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	    here++; | 
					
						
							|  |  |  | 	  } else | 
					
						
							|  |  |  | 	  { switch(c)			/* Build in formatting */ | 
					
						
							|  |  |  | 	    { case 'a':			/* atomic */ | 
					
						
							|  |  |  | 		{ PL_chars_t txt; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		  NEED_ARG; | 
					
						
							|  |  |  | 		  if ( !PL_get_text(argv, &txt, CVT_ATOMIC) ) | 
					
						
							|  |  |  | 		    FMT_ARG("a", argv); | 
					
						
							|  |  |  | 		  SHIFT; | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | 		  rc = outtext(&state, &txt); | 
					
						
							|  |  |  |                   if ( !rc ) | 
					
						
							|  |  |  | 		    goto out; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		  here++; | 
					
						
							|  |  |  | 		  break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	      case 'c':			/* ~c: character code */ | 
					
						
							|  |  |  | 		{ int chr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		  NEED_ARG; | 
					
						
							|  |  |  | 		  if ( PL_get_integer(argv, &chr) && chr >= 0 ) | 
					
						
							|  |  |  | 		  { int times = (arg == DEFAULT ? 1 : arg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		    SHIFT; | 
					
						
							|  |  |  | 		    while(times-- > 0) | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | 		    { rc = outchr(&state, chr); | 
					
						
							|  |  |  | 		      if ( !rc ) | 
					
						
							|  |  |  | 		        goto out; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		    } | 
					
						
							|  |  |  | 		  } else | 
					
						
							|  |  |  | 		    FMT_ARG("c", argv); | 
					
						
							|  |  |  | 		  here++; | 
					
						
							|  |  |  | 		  break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	      case 'e':			/* exponential float */ | 
					
						
							|  |  |  | 	      case 'E':			/* Exponential float */ | 
					
						
							|  |  |  | 	      case 'f':			/* float */ | 
					
						
							|  |  |  | 	      case 'g':			/* shortest of 'f' and 'e' */ | 
					
						
							|  |  |  | 	      case 'G':			/* shortest of 'f' and 'E' */ | 
					
						
							|  |  |  | 		{ number n; | 
					
						
							| 
									
										
										
										
											2011-03-02 09:18:51 +00:00
										 |  |  | 		  union { | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | 		  tmp_buffer b; | 
					
						
							| 
									
										
										
										
											2011-03-02 09:18:51 +00:00
										 |  |  | 		    buffer b1; | 
					
						
							|  |  |  | 		  } u; | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 		  PL_locale *l; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		  NEED_ARG; | 
					
						
							|  |  |  | 		  if ( !valueExpression(argv, &n PASS_LD) ) | 
					
						
							|  |  |  | 		  { char f[2]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		    f[0] = c; | 
					
						
							|  |  |  | 		    f[1] = EOS; | 
					
						
							|  |  |  | 		    FMT_ARG(f, argv); | 
					
						
							|  |  |  | 		  } | 
					
						
							|  |  |  | 		  SHIFT; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 		  if ( c == 'f' && mod_colon ) | 
					
						
							|  |  |  | 		    l = fd->locale; | 
					
						
							|  |  |  | 		  else | 
					
						
							|  |  |  | 		    l = &prolog_locale; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-02 09:18:51 +00:00
										 |  |  | 		  initBuffer(&u.b); | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 		  rc = formatFloat(l, c, arg, &n, &u.b1) != NULL; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		  clearNumber(&n); | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 		  if ( rc ) | 
					
						
							|  |  |  | 		    rc = oututf80(&state, baseBuffer(&u.b, char)); | 
					
						
							| 
									
										
										
										
											2011-03-02 09:18:51 +00:00
										 |  |  | 		  discardBuffer(&u.b); | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  |                   if ( !rc ) | 
					
						
							|  |  |  | 		    goto out; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		  here++; | 
					
						
							|  |  |  | 		  break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	      case 'd':			/* integer */ | 
					
						
							|  |  |  | 	      case 'D':			/* grouped integer */ | 
					
						
							|  |  |  | 	      case 'r':			/* radix number */ | 
					
						
							|  |  |  | 	      case 'R':			/* Radix number */ | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 	      case 'I':			/* Prolog 1_000_000 */ | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		{ number i; | 
					
						
							|  |  |  | 		  tmp_buffer b; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		  NEED_ARG; | 
					
						
							|  |  |  | 		  if ( !valueExpression(argv, &i PASS_LD) || | 
					
						
							|  |  |  | 		       !toIntegerNumber(&i, 0) ) | 
					
						
							|  |  |  | 		  { char f[2]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		    f[0] = c; | 
					
						
							|  |  |  | 		    f[1] = EOS; | 
					
						
							|  |  |  | 		    FMT_ARG(f, argv); | 
					
						
							|  |  |  | 		  } | 
					
						
							|  |  |  | 		  SHIFT; | 
					
						
							|  |  |  | 		  initBuffer(&b); | 
					
						
							|  |  |  | 		  if ( c == 'd' || c == 'D' ) | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 		  { PL_locale ltmp; | 
					
						
							|  |  |  | 		    PL_locale *l; | 
					
						
							|  |  |  | 		    static char grouping[] = {3,0}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		    if ( c == 'D' ) | 
					
						
							|  |  |  | 		    { ltmp.thousands_sep = L","; | 
					
						
							|  |  |  | 		      ltmp.decimal_point = L"."; | 
					
						
							|  |  |  | 		      ltmp.grouping = grouping; | 
					
						
							|  |  |  | 		      l = <mp; | 
					
						
							|  |  |  | 		    } else if ( mod_colon ) | 
					
						
							|  |  |  | 		    { l = fd->locale; | 
					
						
							|  |  |  | 		    } else | 
					
						
							|  |  |  | 		    { l = NULL; | 
					
						
							|  |  |  | 		    } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		    if ( arg == DEFAULT ) | 
					
						
							|  |  |  | 		      arg = 0; | 
					
						
							|  |  |  | 		    if ( !formatInteger(l, arg, 10, TRUE, &i, (Buffer)&b) ) | 
					
						
							|  |  |  | 		      FMT_EXEPTION(); | 
					
						
							|  |  |  | 		  } else if ( c == 'I' ) | 
					
						
							|  |  |  | 		  { PL_locale ltmp; | 
					
						
							|  |  |  | 		    char grouping[2]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		    grouping[0] = (arg == DEFAULT ? 3 : arg); | 
					
						
							|  |  |  | 		    grouping[1] = '\0'; | 
					
						
							|  |  |  | 		    ltmp.thousands_sep = L"_"; | 
					
						
							|  |  |  | 		    ltmp.grouping = grouping; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		    if ( !formatInteger(<mp, 0, 10, TRUE, &i, (Buffer)&b) ) | 
					
						
							|  |  |  | 		      FMT_EXEPTION(); | 
					
						
							|  |  |  | 		  } else			/* r,R */ | 
					
						
							|  |  |  | 		  { if ( arg == DEFAULT ) | 
					
						
							|  |  |  | 		      FMT_ERROR("r,R requires radix specifier"); | 
					
						
							|  |  |  | 		    if ( arg < 1 || arg > 36 ) | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		    { term_t r = PL_new_term_ref(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		      PL_put_integer(r, arg); | 
					
						
							|  |  |  | 		      Sunlock(fd); | 
					
						
							|  |  |  | 		      return PL_error(NULL, 0, NULL, ERR_DOMAIN, | 
					
						
							|  |  |  | 				      ATOM_radix, r); | 
					
						
							|  |  |  | 		    } | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 		    if ( !formatInteger(NULL, 0, arg, c == 'r', &i, (Buffer)&b) ) | 
					
						
							|  |  |  | 		      FMT_EXEPTION(); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		  } | 
					
						
							|  |  |  | 		  clearNumber(&i); | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 		  rc = oututf80(&state, baseBuffer(&b, char)); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		  discardBuffer(&b); | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | 		  if ( !rc ) | 
					
						
							|  |  |  | 		    goto out; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		  here++; | 
					
						
							|  |  |  | 		  break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	      case 's':			/* string */ | 
					
						
							|  |  |  | 		{ PL_chars_t txt; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		  NEED_ARG; | 
					
						
							|  |  |  | 		  if ( !PL_get_text(argv, &txt, CVT_LIST|CVT_STRING) && | 
					
						
							|  |  |  | 		       !PL_get_text(argv, &txt, CVT_ATOM) ) /* SICStus compat */ | 
					
						
							|  |  |  | 		    FMT_ARG("s", argv); | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | 		  rc = outtext(&state, &txt); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		  SHIFT; | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | 		  if ( !rc ) | 
					
						
							|  |  |  | 		    goto out; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		  here++; | 
					
						
							|  |  |  | 		  break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	      case 'i':			/* ignore */ | 
					
						
							|  |  |  | 		{ NEED_ARG; | 
					
						
							|  |  |  | 		  SHIFT; | 
					
						
							|  |  |  | 		  here++; | 
					
						
							|  |  |  | 		  break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		{ Func f; | 
					
						
							|  |  |  | 		  char buf[BUFSIZE]; | 
					
						
							|  |  |  | 		  char *str; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	      case 'k':			/* write_canonical */ | 
					
						
							|  |  |  | 		  f = pl_write_canonical; | 
					
						
							|  |  |  | 	          goto pl_common; | 
					
						
							|  |  |  | 	      case 'p':			/* print */ | 
					
						
							|  |  |  | 		  f = pl_print; | 
					
						
							|  |  |  | 	          goto pl_common; | 
					
						
							|  |  |  | 	      case 'q':			/* writeq */ | 
					
						
							|  |  |  | 		  f = pl_writeq; | 
					
						
							|  |  |  | 	          goto pl_common; | 
					
						
							|  |  |  | 	      case 'w':			/* write */ | 
					
						
							|  |  |  | 		  f = pl_write; | 
					
						
							|  |  |  | 		  pl_common: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		  NEED_ARG; | 
					
						
							|  |  |  | 		  if ( state.pending_rubber ) | 
					
						
							|  |  |  | 		  { size_t bufsize = BUFSIZE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		    str = buf; | 
					
						
							|  |  |  | 		    tellString(&str, &bufsize, ENC_UTF8); | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | 		    rc = (*f)(argv); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		    toldString(); | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | 		    if ( !rc ) | 
					
						
							|  |  |  | 		      goto out; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		    oututf8(&state, str, bufsize); | 
					
						
							|  |  |  | 		    if ( str != buf ) | 
					
						
							|  |  |  | 		      free(str); | 
					
						
							|  |  |  | 		  } else | 
					
						
							|  |  |  | 		  { if ( fd->position && | 
					
						
							|  |  |  | 			 fd->position->linepos == state.column ) | 
					
						
							|  |  |  | 		    { IOSTREAM *old = Scurout; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		      Scurout = fd; | 
					
						
							|  |  |  | 		      rc = (int)(*f)(argv); | 
					
						
							|  |  |  | 		      Scurout = old; | 
					
						
							|  |  |  | 		      if ( !rc ) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		      state.column = fd->position->linepos; | 
					
						
							|  |  |  | 		    } else | 
					
						
							|  |  |  | 		    { size_t bufsize = BUFSIZE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		      str = buf; | 
					
						
							|  |  |  | 		      tellString(&str, &bufsize, ENC_UTF8); | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | 		      rc = (*f)(argv); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		      toldString(); | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | 		      if ( !rc ) | 
					
						
							|  |  |  | 		        goto out; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		      oututf8(&state, str, bufsize); | 
					
						
							|  |  |  | 		      if ( str != buf ) | 
					
						
							|  |  |  | 			free(str); | 
					
						
							|  |  |  | 		    } | 
					
						
							|  |  |  | 		  } | 
					
						
							|  |  |  | 		  SHIFT; | 
					
						
							|  |  |  | 		  here++; | 
					
						
							|  |  |  | 		  break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	      case 'W':			/* write_term(Value, Options) */ | 
					
						
							|  |  |  | 	       { char buf[BUFSIZE]; | 
					
						
							|  |  |  | 		 char *str; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		 if ( argc < 2 ) | 
					
						
							|  |  |  | 		 { FMT_ERROR("not enough arguments"); | 
					
						
							|  |  |  | 		 } | 
					
						
							|  |  |  | 		 if ( state.pending_rubber ) | 
					
						
							|  |  |  | 		  { size_t bufsize = BUFSIZE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		    str = buf; | 
					
						
							|  |  |  | 		    tellString(&str, &bufsize, ENC_UTF8); | 
					
						
							|  |  |  | 		    rc = (int)pl_write_term(argv, argv+1); | 
					
						
							|  |  |  | 		    toldString(); | 
					
						
							|  |  |  | 		    if ( !rc ) | 
					
						
							|  |  |  | 		      goto out; | 
					
						
							|  |  |  | 		    oututf8(&state, str, bufsize); | 
					
						
							|  |  |  | 		    if ( str != buf ) | 
					
						
							|  |  |  | 		      free(str); | 
					
						
							|  |  |  | 		  } else | 
					
						
							|  |  |  | 		  { if ( fd->position && | 
					
						
							|  |  |  | 			 fd->position->linepos == state.column ) | 
					
						
							|  |  |  | 		    { IOSTREAM *old = Scurout; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		      Scurout = fd; | 
					
						
							|  |  |  | 		      rc = (int)pl_write_term(argv, argv+1); | 
					
						
							|  |  |  | 		      Scurout = old; | 
					
						
							|  |  |  | 		      if ( !rc ) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		      state.column = fd->position->linepos; | 
					
						
							|  |  |  | 		    } else | 
					
						
							|  |  |  | 		    { size_t bufsize = BUFSIZE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		      str = buf; | 
					
						
							|  |  |  | 		      tellString(&str, &bufsize, ENC_UTF8); | 
					
						
							|  |  |  | 		      rc = (int)pl_write_term(argv, argv+1); | 
					
						
							|  |  |  | 		      if ( !rc ) | 
					
						
							|  |  |  | 			goto out; | 
					
						
							|  |  |  | 		      toldString(); | 
					
						
							|  |  |  | 		      oututf8(&state, str, bufsize); | 
					
						
							|  |  |  | 		      if ( str != buf ) | 
					
						
							|  |  |  | 			free(str); | 
					
						
							|  |  |  | 		    } | 
					
						
							|  |  |  | 		  } | 
					
						
							|  |  |  | 		  SHIFT; | 
					
						
							|  |  |  | 		  SHIFT; | 
					
						
							|  |  |  | 		  here++; | 
					
						
							|  |  |  | 		  break; | 
					
						
							|  |  |  | 	       } | 
					
						
							|  |  |  | 	      case '@': | 
					
						
							|  |  |  | 	        { char buf[BUFSIZE]; | 
					
						
							|  |  |  | 		  char *str = buf; | 
					
						
							|  |  |  | 		  size_t bufsize = BUFSIZE; | 
					
						
							|  |  |  | 		  term_t ex = 0; | 
					
						
							|  |  |  | 		  int rval; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		  if ( argc < 1 ) | 
					
						
							|  |  |  | 		  { FMT_ERROR("not enough arguments"); | 
					
						
							|  |  |  | 		  } | 
					
						
							|  |  |  | 		  tellString(&str, &bufsize, ENC_UTF8); | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | 		  rval = callProlog(m, argv, PL_Q_CATCH_EXCEPTION, &ex); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		  toldString(); | 
					
						
							|  |  |  | 		  oututf8(&state, str, bufsize); | 
					
						
							|  |  |  | 		  if ( str != buf ) | 
					
						
							|  |  |  | 		    free(str); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		  if ( !rval ) | 
					
						
							|  |  |  | 		  { Sunlock(fd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		    if ( ex ) | 
					
						
							|  |  |  | 		      return PL_raise_exception(ex); | 
					
						
							|  |  |  | 		    else | 
					
						
							|  |  |  | 		      fail; | 
					
						
							|  |  |  | 		  } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		  SHIFT; | 
					
						
							|  |  |  | 		  here++; | 
					
						
							|  |  |  | 		  break; | 
					
						
							|  |  |  | 	        } | 
					
						
							|  |  |  | 	      case '~':			/* ~ */ | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | 		{ rc = outchr(&state, '~'); | 
					
						
							|  |  |  | 		  if ( !rc ) | 
					
						
							|  |  |  | 		    goto out; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		  here++; | 
					
						
							|  |  |  | 		  break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	      case 'n':			/* \n */ | 
					
						
							|  |  |  | 	      case 'N':			/* \n if not on newline */ | 
					
						
							|  |  |  | 		{ if ( arg == DEFAULT ) | 
					
						
							|  |  |  | 		    arg = 1; | 
					
						
							|  |  |  | 		  if ( c == 'N' && state.column == 0 ) | 
					
						
							|  |  |  | 		    arg--; | 
					
						
							|  |  |  | 		  while( arg-- > 0 ) | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  |                   { rc = outchr(&state, '\n'); | 
					
						
							|  |  |  | 		    if ( !rc ) | 
					
						
							|  |  |  | 		      goto out; | 
					
						
							|  |  |  |                   } | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 		  here++; | 
					
						
							|  |  |  | 		  break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	      case 't':			/* insert tab */ | 
					
						
							|  |  |  | 		{ if ( state.pending_rubber >= MAXRUBBER ) | 
					
						
							|  |  |  | 		    FMT_ERROR("Too many tab stops"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		  state.rub[state.pending_rubber].where = state.buffered; | 
					
						
							|  |  |  | 		  state.rub[state.pending_rubber].pad   = | 
					
						
							|  |  |  | 					(arg == DEFAULT ? (pl_wchar_t)' ' | 
					
						
							|  |  |  | 							: (pl_wchar_t)arg); | 
					
						
							|  |  |  | 		  state.rub[state.pending_rubber].size = 0; | 
					
						
							|  |  |  | 		  state.pending_rubber++; | 
					
						
							|  |  |  | 		  here++; | 
					
						
							|  |  |  | 		  break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	      case '|':			/* set tab */ | 
					
						
							|  |  |  | 		{ int stop; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		  if ( arg == DEFAULT ) | 
					
						
							|  |  |  | 		    arg = state.column; | 
					
						
							|  |  |  | 	      case '+':			/* tab relative */ | 
					
						
							|  |  |  | 		  if ( arg == DEFAULT ) | 
					
						
							|  |  |  | 		    arg = 8; | 
					
						
							|  |  |  | 		  stop = (c == '+' ? tab_stop + arg : arg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		  if ( state.pending_rubber == 0 ) /* nothing to distribute */ | 
					
						
							|  |  |  | 		  { state.rub[0].where = state.buffered; | 
					
						
							|  |  |  | 		    state.rub[0].pad = ' '; | 
					
						
							|  |  |  | 		    state.pending_rubber++; | 
					
						
							|  |  |  | 		  } | 
					
						
							|  |  |  | 		  distribute_rubber(state.rub, | 
					
						
							|  |  |  | 				    state.pending_rubber, | 
					
						
							|  |  |  | 				    stop - state.column); | 
					
						
							|  |  |  | 		  emit_rubber(&state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		  state.column = tab_stop = stop; | 
					
						
							|  |  |  | 		  here++; | 
					
						
							|  |  |  | 		  break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	      default: | 
					
						
							|  |  |  | 	      { term_t ex = PL_new_term_ref(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Sunlock(fd); | 
					
						
							|  |  |  | 		PL_put_atom(ex, codeToAtom(c)); | 
					
						
							|  |  |  | 		return PL_error("format", 2, NULL, ERR_EXISTENCE, | 
					
						
							|  |  |  | 				PL_new_atom("format_character"), | 
					
						
							|  |  |  | 				ex); | 
					
						
							|  |  |  | 	      } | 
					
						
							|  |  |  | 	    } | 
					
						
							|  |  |  | 	  } | 
					
						
							|  |  |  | 	  break;			/* the '~' switch */ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  |       default: | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | 	{ rc = outchr(&state, c); | 
					
						
							|  |  |  | 	  if ( !rc ) | 
					
						
							|  |  |  | 	    goto out; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 	  here++; | 
					
						
							|  |  |  | 	  break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ( state.pending_rubber )		/* not closed ~t: flush out */ | 
					
						
							|  |  |  |     emit_rubber(&state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | out: | 
					
						
							|  |  |  |   Sunlock(fd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return rc; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | distribute_rubber(struct rubber *r, int rn, int space) | 
					
						
							|  |  |  | { if ( space > 0 ) | 
					
						
							|  |  |  |   { int s = space / rn; | 
					
						
							|  |  |  |     int n, m; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(n=0; n < rn; n++)		/* give them equal size */ | 
					
						
							|  |  |  |       r[n].size = s; | 
					
						
							|  |  |  | 					/* distribute from the center */ | 
					
						
							|  |  |  |     space -= s*rn; | 
					
						
							|  |  |  |     for(m = rn / 2, n = 0; space; n++, space--) | 
					
						
							|  |  |  |     { r[m + (n % 2 ? n : -n)].size++; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } else | 
					
						
							|  |  |  |   { int n; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for(n=0; n < rn; n++)		/* set all rubber to 0 */ | 
					
						
							|  |  |  |       r[n].size = 0; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | emit_rubber(format_state *state) | 
					
						
							|  |  |  | { const char *s = baseBuffer(&state->buffer, char); | 
					
						
							|  |  |  |   const char *e = &s[entriesBuffer(&state->buffer, char)]; | 
					
						
							|  |  |  |   struct rubber *r = state->rub; | 
					
						
							|  |  |  |   int rn = state->pending_rubber; | 
					
						
							|  |  |  |   size_t j; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for(j = 0; s <= e; j++) | 
					
						
							|  |  |  |   { int chr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( rn && r->where == j ) | 
					
						
							|  |  |  |     { size_t n; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       for(n=0; n<r->size; n++) | 
					
						
							|  |  |  |       { if ( Sputcode(r->pad, state->out) < 0 ) | 
					
						
							|  |  |  | 	  return FALSE; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       r++; | 
					
						
							|  |  |  |       rn--; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( s < e ) | 
					
						
							|  |  |  |     { s = utf8_get_char(s, &chr); | 
					
						
							|  |  |  |       if ( Sputcode(chr, state->out) < 0 ) | 
					
						
							|  |  |  | 	return FALSE; | 
					
						
							|  |  |  |     } else | 
					
						
							|  |  |  |       break; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   discardBuffer(&state->buffer); | 
					
						
							|  |  |  |   initBuffer(&state->buffer); | 
					
						
							|  |  |  |   state->buffered = 0; | 
					
						
							|  |  |  |   state->pending_rubber = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return TRUE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*  format an integer according to  a  number  of  modifiers  at various
 | 
					
						
							|  |  |  |     radius.   `split'  is a boolean asking to put ',' between each group | 
					
						
							|  |  |  |     of three digits (e.g. 67,567,288).  `div' askes to divide the number | 
					
						
							|  |  |  |     by radix^`div' before printing.   `radix'  is  the  radix  used  for | 
					
						
							|  |  |  |     conversion.  `n' is the number to be converted. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  ** Fri Aug 19 22:26:41 1988  jan@swivax.UUCP (Jan Wielemaker)  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | static void | 
					
						
							|  |  |  | lappend(const wchar_t *l, int def, Buffer out) | 
					
						
							|  |  |  | { if ( l ) | 
					
						
							|  |  |  |   { const wchar_t *e = l+wcslen(l); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (--e >= l) | 
					
						
							|  |  |  |     { int c = *e; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if ( c < 128 ) | 
					
						
							|  |  |  |       { addBuffer(out, c, char); | 
					
						
							|  |  |  |       } else | 
					
						
							|  |  |  |       { char buf[6]; | 
					
						
							|  |  |  | 	char *e8, *s; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	e8=utf8_put_char(buf, c); | 
					
						
							|  |  |  | 	for(s=e8; --s>=buf; )		/* must be reversed as we reverse */ | 
					
						
							|  |  |  | 	{ addBuffer(out, *s, char);	/* in the end */ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } else | 
					
						
							|  |  |  |   { addBuffer(out, def, char); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | revert_string(char *s, size_t len) | 
					
						
							|  |  |  | { char *e = &s[len-1]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for(; e>s; s++,e--) | 
					
						
							|  |  |  |   { int c = *e; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     *e = *s; | 
					
						
							|  |  |  |     *s = c; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | static char * | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | formatInteger(PL_locale *locale, int div, int radix, bool smll, Number i, | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 	     Buffer out) | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | { const char *grouping = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ( !locale ) | 
					
						
							|  |  |  |   { locale = &prolog_locale; | 
					
						
							|  |  |  |   } else | 
					
						
							|  |  |  |   { if ( locale->grouping && locale->grouping[0] && | 
					
						
							|  |  |  | 	 locale->thousands_sep && locale->thousands_sep[0] ) | 
					
						
							|  |  |  |       grouping = locale->grouping; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   switch(i->type) | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |   { case V_INTEGER: | 
					
						
							|  |  |  |     { int64_t n = i->value.i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if ( n == 0 && div == 0 ) | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |       { addBuffer(out, '0', char); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |       } else | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |       { int before = FALSE;			/* before decimal point */ | 
					
						
							|  |  |  | 	int negative = FALSE; | 
					
						
							|  |  |  | 	int gsize = 0; | 
					
						
							|  |  |  | 	int dweight; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	negative = (n < 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while( n != 0 || div >= 0 ) | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 	{ if ( div-- == 0 && !before ) | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 	  { if ( !isEmptyBuffer(out) ) | 
					
						
							|  |  |  | 	      lappend(locale->decimal_point, '.', out); | 
					
						
							|  |  |  | 	    before = TRUE; | 
					
						
							|  |  |  | 	    if ( grouping ) | 
					
						
							|  |  |  | 	      gsize = grouping[0]; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 	  } | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	  if ( !negative ) | 
					
						
							|  |  |  | 	    dweight = (int)(n % radix); | 
					
						
							|  |  |  | 	  else | 
					
						
							|  |  |  | 	    dweight = -(int)(n % -radix); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	  addBuffer(out, digitName(dweight, smll), char); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 	  n /= radix; | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	  if ( --gsize == 0 && n != 0 ) | 
					
						
							|  |  |  | 	  { lappend(locale->thousands_sep, ',', out); | 
					
						
							|  |  |  | 	    if ( grouping[1] == 0 ) | 
					
						
							|  |  |  | 	      gsize = grouping[0]; | 
					
						
							|  |  |  | 	    else if ( grouping[1] == CHAR_MAX ) | 
					
						
							|  |  |  | 	      gsize = 0; | 
					
						
							|  |  |  | 	    else | 
					
						
							|  |  |  | 	      gsize = *++grouping; | 
					
						
							|  |  |  | 	  } | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if ( negative ) | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 	  addBuffer(out, '-', char); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |       revert_string(baseBuffer(out, char), entriesBuffer(out, char)); | 
					
						
							|  |  |  |       addBuffer(out, EOS, char); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       return baseBuffer(out, char); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #ifdef O_GMP
 | 
					
						
							|  |  |  |     case V_MPZ: | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |     { GET_LD | 
					
						
							|  |  |  |       size_t len = mpz_sizeinbase(i->value.mpz, radix); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |       char tmp[256]; | 
					
						
							|  |  |  |       char *buf; | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |       int rc = TRUE; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if ( len+2 > sizeof(tmp) ) | 
					
						
							|  |  |  | 	buf = PL_malloc(len+2); | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  | 	buf = tmp; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |       EXCEPTION_GUARDED({ LD->gmp.persistent++; | 
					
						
							|  |  |  | 			  mpz_get_str(buf, radix, i->value.mpz); | 
					
						
							|  |  |  | 			  LD->gmp.persistent--; | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			{ LD->gmp.persistent--; | 
					
						
							|  |  |  | 			  rc = PL_rethrow(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  |       if ( !rc ) | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-03-08 00:03:50 +00:00
										 |  |  |       if ( !smll && radix > 10 ) | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |       { char *s; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for(s=buf; *s; s++) | 
					
						
							|  |  |  | 	  *s = toupper(*s); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if ( grouping || div > 0 ) | 
					
						
							|  |  |  |       { int before = FALSE;			/* before decimal point */ | 
					
						
							|  |  |  | 	int gsize = 0; | 
					
						
							|  |  |  | 	char *e = buf+strlen(buf)-1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while(e >= buf || div >= 0) | 
					
						
							|  |  |  | 	{ if ( div-- == 0 && !before ) | 
					
						
							|  |  |  | 	  { if ( !isEmptyBuffer(out) ) | 
					
						
							|  |  |  | 	      lappend(locale->decimal_point, '.', out); | 
					
						
							|  |  |  | 	    before = TRUE; | 
					
						
							|  |  |  | 	    if ( grouping ) | 
					
						
							|  |  |  | 	      gsize = grouping[0]; | 
					
						
							|  |  |  | 	  } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	  addBuffer(out, *e, char); | 
					
						
							|  |  |  | 	  e--; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	  if ( --gsize == 0 && e >= buf && *e != '-' ) | 
					
						
							|  |  |  | 	  { lappend(locale->thousands_sep, ',', out); | 
					
						
							|  |  |  | 	    if ( grouping[1] == 0 ) | 
					
						
							|  |  |  | 	      gsize = grouping[0]; | 
					
						
							|  |  |  | 	    else if ( grouping[1] == CHAR_MAX ) | 
					
						
							|  |  |  | 	      gsize = 0; | 
					
						
							|  |  |  | 	    else | 
					
						
							|  |  |  | 	      gsize = *++grouping; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 	  } | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 	revert_string(baseBuffer(out, char), entriesBuffer(out, char)); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |       } else | 
					
						
							|  |  |  |       { addMultipleBuffer(out, buf, strlen(buf), char); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if ( buf != tmp ) | 
					
						
							|  |  |  | 	PL_free(buf); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |       addBuffer(out, EOS, char); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |       return baseBuffer(out, char); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #endif /*O_GMP*/
 | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |       assert(0); | 
					
						
							|  |  |  |       return NULL; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-15 22:29:04 +00:00
										 |  |  | #if O_LOCALE
 | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | static int | 
					
						
							|  |  |  | countGroups(const char *grouping, int len) | 
					
						
							|  |  |  | { int groups = 0; | 
					
						
							|  |  |  |   int gsize = grouping[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while(len>0) | 
					
						
							|  |  |  |   { len -= gsize; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( len > 0 ) | 
					
						
							|  |  |  |       groups++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( grouping[1] == 0 ) | 
					
						
							|  |  |  |     { if ( len > 1 ) | 
					
						
							|  |  |  | 	groups += (len-1)/grouping[0]; | 
					
						
							|  |  |  |       return groups; | 
					
						
							|  |  |  |     } else if ( grouping[1] == CHAR_MAX ) | 
					
						
							|  |  |  |     { return groups; | 
					
						
							|  |  |  |     } else | 
					
						
							|  |  |  |     { gsize = *++grouping; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return groups; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | ths_to_utf8(char *u8, const wchar_t *s, size_t len) | 
					
						
							|  |  |  | { char *e = u8+len-7; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for( ; u8<e && *s; s++) | 
					
						
							|  |  |  |     u8 = utf8_put_char(u8,*s); | 
					
						
							|  |  |  |   *u8 = EOS; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return *s == 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | same_decimal_point(PL_locale *l1, PL_locale *l2) | 
					
						
							|  |  |  | { if ( l1->decimal_point && l2->decimal_point && | 
					
						
							|  |  |  |        wcscmp(l1->decimal_point, l2->decimal_point) == 0 ) | 
					
						
							|  |  |  |     return TRUE; | 
					
						
							|  |  |  |   if ( !l1->decimal_point && !l2->decimal_point ) | 
					
						
							|  |  |  |     return TRUE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return FALSE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | utf8_dp(PL_locale *l, char *s, int *len) | 
					
						
							|  |  |  | { if ( l->decimal_point ) | 
					
						
							|  |  |  |   { if ( !ths_to_utf8(s, l->decimal_point, 20) ) | 
					
						
							|  |  |  |       return FALSE; | 
					
						
							|  |  |  |     *len = strlen(s); | 
					
						
							|  |  |  |   } else | 
					
						
							|  |  |  |   { *s++ = '.'; | 
					
						
							|  |  |  |     *s = EOS; | 
					
						
							|  |  |  |     *len = 1; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return TRUE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* localizeDecimalPoint() replaces the decimal point as entered by the
 | 
					
						
							|  |  |  |    local sensitive print functions by the one in the specified locale. | 
					
						
							|  |  |  |    This is overly complicated. Needs more testing, in particular for | 
					
						
							|  |  |  |    locales with (in UTF-8) multibyte decimal points. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | localizeDecimalPoint(PL_locale *locale, Buffer b) | 
					
						
							|  |  |  | { if ( locale == GD->locale.default_locale || | 
					
						
							|  |  |  |        same_decimal_point(GD->locale.default_locale, locale) ) | 
					
						
							|  |  |  |     return TRUE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if ( locale->decimal_point && locale->decimal_point[0] ) | 
					
						
							|  |  |  |   { char *s = baseBuffer(b, char); | 
					
						
							|  |  |  |     char *e; | 
					
						
							|  |  |  |     char dp[20];  int dplen; | 
					
						
							|  |  |  |     char ddp[20]; int ddplen; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( !utf8_dp(locale, dp, &dplen) || | 
					
						
							|  |  |  | 	 !utf8_dp(GD->locale.default_locale, ddp, &ddplen) ) | 
					
						
							|  |  |  |       return FALSE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( *s == '-' ) | 
					
						
							|  |  |  |       s++; | 
					
						
							|  |  |  |     for(e=s; *e && isDigit(*e); e++) | 
					
						
							|  |  |  |       ; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( strncmp(e, ddp, ddplen) == 0 ) | 
					
						
							|  |  |  |     { if ( dplen == ddplen ) | 
					
						
							|  |  |  |       { memcpy(e, dp, dplen); | 
					
						
							|  |  |  |       } else | 
					
						
							|  |  |  |       { char *ob = baseBuffer(b, char); | 
					
						
							|  |  |  | 	if ( dplen > ddplen && !growBuffer(b, dplen-ddplen) ) | 
					
						
							|  |  |  | 	  return PL_no_memory(); | 
					
						
							|  |  |  | 	e += baseBuffer(b, char) - ob; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	memmove(&e[dplen-ddplen], e, strlen(e)+1); | 
					
						
							|  |  |  | 	memcpy(e, dp, dplen); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return TRUE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | groupDigits(PL_locale *locale, Buffer b) | 
					
						
							|  |  |  | { if ( locale->thousands_sep && locale->thousands_sep[0] && | 
					
						
							|  |  |  |        locale->grouping && locale->grouping[0] ) | 
					
						
							|  |  |  |   { char *s = baseBuffer(b, char); | 
					
						
							|  |  |  |     char *e; | 
					
						
							|  |  |  |     int groups; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( *s == '-' ) | 
					
						
							|  |  |  |       s++; | 
					
						
							|  |  |  |     for(e=s; *e && isDigit(*e); e++) | 
					
						
							|  |  |  |       ; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     groups = countGroups(locale->grouping, (int)(e-s)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ( groups > 0 ) | 
					
						
							|  |  |  |     { char *o; | 
					
						
							|  |  |  |       char *grouping = locale->grouping; | 
					
						
							|  |  |  |       int gsize = grouping[0]; | 
					
						
							|  |  |  |       char ths[20]; | 
					
						
							|  |  |  |       int thslen; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if ( !ths_to_utf8(ths, locale->thousands_sep, sizeof(ths)) ) | 
					
						
							|  |  |  | 	return FALSE; | 
					
						
							|  |  |  |       thslen = strlen(ths); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if ( !growBuffer(b, thslen*groups) ) | 
					
						
							|  |  |  | 	return PL_no_memory(); | 
					
						
							|  |  |  |       memmove(&e[groups*thslen], e, strlen(e)+1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       e--; | 
					
						
							|  |  |  |       for(o=e+groups*thslen; e>=s; ) | 
					
						
							|  |  |  |       { *o-- = *e--; | 
					
						
							|  |  |  | 	if ( --gsize == 0 && e>=s ) | 
					
						
							|  |  |  | 	{ o -= thslen-1; | 
					
						
							|  |  |  | 	  memcpy(o, ths, thslen); | 
					
						
							|  |  |  | 	  o--; | 
					
						
							|  |  |  | 	  if ( grouping[1] == 0 ) | 
					
						
							|  |  |  | 	    gsize = grouping[0]; | 
					
						
							|  |  |  | 	  else if ( grouping[1] == CHAR_MAX ) | 
					
						
							|  |  |  | 	    gsize = 0; | 
					
						
							|  |  |  | 	  else | 
					
						
							|  |  |  | 	    gsize = *++grouping; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return TRUE; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2014-03-15 22:29:04 +00:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
					
						
							|  |  |  | formatFloat(PL_locale *locale, int how, int arg, Number f, Buffer out) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | formats a floating point  number  to  a   buffer.  `How'  is  the format | 
					
						
							|  |  |  | specifier ([eEfgG]), `arg' the argument. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MPZ/MPQ numbers printed using the format specifier `f' are written using | 
					
						
							|  |  |  | the following algorithm, courtesy of Jan Burse: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Given: A rational n/m | 
					
						
							|  |  |  |   Seeked: The ration rounded to d fractional digits. | 
					
						
							|  |  |  |   Algorithm: Compute (n*10^d+m//2)//m, and place period at d.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | static char * | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | formatFloat(PL_locale *locale, int how, int arg, Number f, Buffer out) | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | { if ( arg == DEFAULT ) | 
					
						
							|  |  |  |     arg = 6; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   switch(f->type) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  | #ifdef O_GMP
 | 
					
						
							|  |  |  |     mpf_t mpf; | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |     mpz_t t1, t2; | 
					
						
							|  |  |  |     int neg; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |     case V_MPZ: | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |     { switch(how) | 
					
						
							|  |  |  |       { case 'f': | 
					
						
							|  |  |  |         { mpz_init(t1); | 
					
						
							|  |  |  |           mpz_init(t2); | 
					
						
							|  |  |  |           mpz_ui_pow_ui(t1, 10, arg); | 
					
						
							|  |  |  |           mpz_mul(t1, f->value.mpz, t1); | 
					
						
							|  |  |  |           neg = (mpz_cmp_ui(t1, 0) < 0) ? 1 : 0; | 
					
						
							|  |  |  |           mpz_abs(t1, t1); | 
					
						
							|  |  |  |           goto print_mpz; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         case 'e': | 
					
						
							|  |  |  | 	case 'E': | 
					
						
							|  |  |  |         case 'g': | 
					
						
							|  |  |  | 	case 'G': | 
					
						
							|  |  |  |         { mpf_init2(mpf, arg*4); | 
					
						
							|  |  |  |           mpf_set_z(mpf, f->value.mpz); | 
					
						
							|  |  |  |           goto print_mpf; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |     case V_MPQ: | 
					
						
							|  |  |  |     { char tmp[12]; | 
					
						
							|  |  |  |       int size; | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |       int written = 0; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |       int fbits; | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |       int digits = 0; | 
					
						
							| 
									
										
										
										
											2014-06-14 10:27:39 +01:00
										 |  |  |       int padding; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |       switch(how) | 
					
						
							|  |  |  |       { case 'f': | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |         { mpz_init(t1); | 
					
						
							|  |  |  |           mpz_init(t2); | 
					
						
							|  |  |  |           mpz_ui_pow_ui(t1, 10, arg); | 
					
						
							|  |  |  |           mpz_mul(t1, mpq_numref(f->value.mpq), t1); | 
					
						
							|  |  |  |           mpz_tdiv_q_ui(t2, mpq_denref(f->value.mpq), 2); | 
					
						
							|  |  |  |           if (mpq_cmp_ui(f->value.mpq, 0, 1) < 0) | 
					
						
							|  |  |  |           { mpz_sub(t1, t1, t2); | 
					
						
							|  |  |  |             neg=1; | 
					
						
							|  |  |  |           } else | 
					
						
							|  |  |  |           { mpz_add(t1, t1, t2); | 
					
						
							|  |  |  |             neg=0; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           mpz_tdiv_q(t1, t1, mpq_denref(f->value.mpq)); | 
					
						
							|  |  |  |           mpz_abs(t1, t1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         print_mpz: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (mpz_cmp_ui(t1, 0) != 0) | 
					
						
							|  |  |  |           { size = mpz_sizeinbase(t1, 10) + 1; /* reserve for <null> */ | 
					
						
							|  |  |  |             if ( !growBuffer(out, size) ) | 
					
						
							|  |  |  |             { PL_no_memory(); | 
					
						
							|  |  |  |               return NULL; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             digits = written = gmp_snprintf(baseBuffer(out, char), size, "%Zd", t1); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           size = digits; | 
					
						
							|  |  |  |           if (neg) size++;               /* leading - */ | 
					
						
							|  |  |  |           if (arg) size++;               /* decimal point */ | 
					
						
							|  |  |  |           if (digits <= arg)             /* leading '0's */ | 
					
						
							|  |  |  |           { padding = (arg-digits+1); | 
					
						
							|  |  |  |             size += padding; | 
					
						
							| 
									
										
										
										
											2014-06-14 10:27:39 +01:00
										 |  |  |           } else | 
					
						
							|  |  |  |           { padding = 0; | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |           } | 
					
						
							|  |  |  |           size++;                        /* NULL terminator */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if ( !growBuffer(out, size) ) | 
					
						
							|  |  |  |           { PL_no_memory(); | 
					
						
							|  |  |  |             return NULL; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (!digits) | 
					
						
							|  |  |  |           { memset(out->base, '\0', 1); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (neg) | 
					
						
							|  |  |  |           { memmove(out->base+1, out->base, digits+1); | 
					
						
							|  |  |  |             memset(out->base, '-', 1); | 
					
						
							|  |  |  |             written++; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (padding) | 
					
						
							|  |  |  |           { memmove(out->base+neg+padding, out->base+neg, written-neg+1); | 
					
						
							|  |  |  |             memset(out->base+neg, '0', padding); | 
					
						
							|  |  |  |             written += padding; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (arg) | 
					
						
							|  |  |  |           { memmove(out->base+written-(arg-1), out->base+written-arg, arg+1); | 
					
						
							|  |  |  |             if ( locale->decimal_point && locale->decimal_point[0] ) | 
					
						
							|  |  |  |               *(out->base+written-arg) = locale->decimal_point[0]; | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |               *(out->base+written-arg) = '.'; | 
					
						
							|  |  |  |             written++; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           out->top = out->base + written; | 
					
						
							|  |  |  |           mpz_clear(t1); | 
					
						
							|  |  |  |           mpz_clear(t2); | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         case 'e': | 
					
						
							|  |  |  | 	case 'E': | 
					
						
							|  |  |  |         case 'g': | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 	case 'G': | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |         { switch(how) | 
					
						
							|  |  |  |           { case 'g': | 
					
						
							|  |  |  |             case 'G': | 
					
						
							|  |  |  |             { mpz_t iv; | 
					
						
							|  |  |  |               mpz_init(iv); | 
					
						
							|  |  |  |               mpz_set_q(iv, f->value.mpq); | 
					
						
							|  |  |  |               fbits = (int)mpz_sizeinbase(iv, 2) + 4*arg; | 
					
						
							|  |  |  |               mpz_clear(iv); | 
					
						
							|  |  |  |               break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |               fbits = 4*arg; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           mpf_init2(mpf, fbits); | 
					
						
							|  |  |  |           mpf_set_q(mpf, f->value.mpq); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         print_mpf: | 
					
						
							|  |  |  |           Ssprintf(tmp, "%%.%dF%c", arg, how); | 
					
						
							|  |  |  |           size = 0; | 
					
						
							|  |  |  |           written = arg+4; | 
					
						
							|  |  |  |           while(written >= size) | 
					
						
							|  |  |  |           { size = written+1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if ( !growBuffer(out, size) )	/* reserve for -.e<null> */ | 
					
						
							|  |  |  |             { PL_no_memory(); | 
					
						
							|  |  |  |               return NULL; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             written = gmp_snprintf(baseBuffer(out, char), size, tmp, mpf); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           mpf_clear(mpf); | 
					
						
							|  |  |  |           out->top = out->base + written; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |       break; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     case V_INTEGER: | 
					
						
							|  |  |  |       promoteToFloatNumber(f); | 
					
						
							|  |  |  |       /*FALLTHROUGH*/ | 
					
						
							|  |  |  |     case V_FLOAT: | 
					
						
							|  |  |  |     { char tmp[12]; | 
					
						
							|  |  |  |       int written = arg+20; | 
					
						
							|  |  |  |       int size = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       Ssprintf(tmp, "%%.%d%c", arg, how); | 
					
						
							|  |  |  |       while(written >= size) | 
					
						
							|  |  |  |       { size = written+1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-16 11:28:58 +00:00
										 |  |  | 	if ( !growBuffer(out, size) ) | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  | 	{ PL_no_memory(); | 
					
						
							|  |  |  | 	  return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 	written = snprintf(baseBuffer(out, char), size, tmp, f->value.f); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       out->top = out->base + written; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |       break; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |     default: | 
					
						
							|  |  |  |       assert(0); | 
					
						
							|  |  |  |       return NULL; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-15 22:29:04 +00:00
										 |  |  | #if O_LOCALE
 | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |   if ( locale ) | 
					
						
							|  |  |  |   { if ( !localizeDecimalPoint(locale, out) || | 
					
						
							|  |  |  | 	 !groupDigits(locale, out) ) | 
					
						
							|  |  |  |       return NULL; | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2014-03-15 22:29:04 +00:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-15 01:10:25 +00:00
										 |  |  |   return baseBuffer(out, char); | 
					
						
							| 
									
										
										
										
											2011-02-10 00:01:19 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2014-09-15 03:13:50 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | //! @}
 |