io_builtins_xsb.c

00001 /* File:      io_builtins_xsb.c
00002 ** Author(s): David S. Warren, kifer
00003 ** Contact:   xsb-contact@cs.sunysb.edu
00004 ** 
00005 ** Copyright (C) The Research Foundation of SUNY, 1993-1998
00006 ** 
00007 ** XSB is free software; you can redistribute it and/or modify it under the
00008 ** terms of the GNU Library General Public License as published by the Free
00009 ** Software Foundation; either version 2 of the License, or (at your option)
00010 ** any later version.
00011 ** 
00012 ** XSB is distributed in the hope that it will be useful, but WITHOUT ANY
00013 ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00014 ** FOR A PARTICULAR PURPOSE.  See the GNU Library General Public License for
00015 ** more details.
00016 ** 
00017 ** You should have received a copy of the GNU Library General Public License
00018 ** along with XSB; if not, write to the Free Software Foundation,
00019 ** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020 **
00021 ** $Id: io_builtins_xsb.c,v 1.66 2006/01/14 16:25:32 dwarren Exp $
00022 ** 
00023 */
00024 
00025 #include "xsb_config.h"
00026 #include "xsb_debug.h"
00027 
00028 #include <stdio.h>
00029 #include <signal.h>
00030 #include <errno.h>
00031 #include <string.h>
00032 #include <stdlib.h>
00033 #ifndef WIN_NT
00034 #include <unistd.h> 
00035 #endif
00036 #include <sys/stat.h>
00037 
00038 #include "setjmp_xsb.h"
00039 #include "auxlry.h"
00040 #include "cell_xsb.h"
00041 #include "context.h"
00042 #include "error_xsb.h"
00043 #include "cinterf.h"
00044 #include "memory_xsb.h"
00045 #include "psc_xsb.h"
00046 #include "heap_xsb.h"
00047 #include "register.h"
00048 #include "flags_xsb.h"
00049 #include "inst_xsb.h"
00050 #include "loader_xsb.h" /* for ZOOM_FACTOR */
00051 #include "subp.h"
00052 #include "tries.h"
00053 #include "choice.h"
00054 #include "macro_xsb.h"
00055 #include "io_builtins_xsb.h"
00056 #include "wind2unix.h"
00057 #include "binding.h"
00058 #include "deref.h"
00059 #include "findall.h"
00060 #include "heap_defs_xsb.h"
00061 
00062 stream_record open_files[MAX_OPEN_FILES]; /* open file table, protected by MUTEX IO */
00063 
00064 // static FILE *fptr;                   /* working variable */
00065     
00066 struct fmt_spec {
00067   char type;                     /* i(nteger), f(loat), s(tring) */
00068   /* size: in case of a write op a the * format specifier (e.g., %*.*d), size
00069      is 1, 2, or 3, depending the number of *'s in that particular format.
00070      If there are no *'s, then this format correspnds to exactly one argument,
00071      so size=1. If there is one "*", then this format corresponds to 2
00072      arguments, etc.
00073      This tells how manu arguments to expect.
00074      In case of a read operation, size can be 0, since here '*' means
00075      assignment suppression. */
00076   char size;
00077   char *fmt;
00078 };
00079 
00080 struct next_fmt_state {
00081   int _current_substr_start;
00082   VarString *_workspace;
00083   char _saved_char;
00084 };
00085 #define current_substr_start (fmt_state->_current_substr_start)
00086 #define workspace (*(fmt_state->_workspace))
00087 #define saved_char (fmt_state->_saved_char)
00088 
00089 void next_format_substr(CTXTdeclc char*, struct next_fmt_state*, struct fmt_spec *, int, int);
00090 char *p_charlist_to_c_string(CTXTdeclc prolog_term, VarString*, char*, char*);
00091 
00092 /* type is a char: 's', 'i', 'f' */
00093 #define TYPE_ERROR_CHK(ch_type, Label) \
00094         if (current_fmt_spec->type != ch_type) { \
00095             xsb_abort("[%s] in argument value %d. Expected %c, got %c.", Label, i,current_fmt_spec->type,ch_type); \
00096         }
00097 
00098 #define PRINT_ARG(arg) switch (current_fmt_spec->size) { \
00099         case 1: fprintf(fptr, current_fmt_spec->fmt, arg); \
00100                 break; \
00101         case 2: fprintf(fptr, current_fmt_spec->fmt, width, arg); \
00102                 break; \
00103         case 3: fprintf(fptr, current_fmt_spec->fmt, width, precision, arg); \
00104                 break; \
00105         }
00106 #define CHECK_ARITY(i, Arity, Label) if (i > Arity) { \
00107               xsb_abort("[%s] Not enough arguments for given format", Label); \
00108         }
00109 
00110 #ifdef HAVE_SNPRINTF
00111 /* like PRINT_ARG, but uses snprintf */
00112 #define SPRINT_ARG(arg) \
00113         XSB_StrEnsureSize(&OutString, OutString.length+SAFE_OUT_SIZE); \
00114         switch (current_fmt_spec->size) { \
00115         case 1: bytes_formatted=snprintf(OutString.string+OutString.length, \
00116                                          SAFE_OUT_SIZE, \
00117                                          current_fmt_spec->fmt, arg); \
00118                 break; \
00119         case 2: bytes_formatted=snprintf(OutString.string+OutString.length, \
00120                                          SAFE_OUT_SIZE, \
00121                                          current_fmt_spec->fmt, width, arg); \
00122                 break; \
00123         case 3: bytes_formatted=snprintf(OutString.string+OutString.length, \
00124                                          SAFE_OUT_SIZE, \
00125                                          current_fmt_spec->fmt, \
00126                                          width, precision, arg); \
00127                 break; \
00128         } \
00129         OutString.length += bytes_formatted; \
00130         XSB_StrNullTerminate(&OutString);
00131 
00132 #else
00133 /* like PRINT_ARG, but uses sprintf -- used with old compilers that don't have
00134    snprintf.  This is error-prone: don't use broken compilers!!!
00135    In some systems sprintf returns it's first argument, so have to use
00136    strlen to count bytes formatted, for portability.
00137    */
00138 #define SPRINT_ARG(arg) \
00139         XSB_StrEnsureSize(&OutString, OutString.length+SAFE_OUT_SIZE); \
00140         switch (current_fmt_spec->size) { \
00141         case 1: sprintf(OutString.string+OutString.length, \
00142                         current_fmt_spec->fmt, arg); \
00143                 bytes_formatted = strlen(OutString.string+OutString.length); \
00144                 break; \
00145         case 2: sprintf(OutString.string+OutString.length, \
00146                         current_fmt_spec->fmt, width, arg); \
00147                 bytes_formatted = strlen(OutString.string+OutString.length); \
00148                 break; \
00149         case 3: sprintf(OutString.string+OutString.length, \
00150                         current_fmt_spec->fmt, \
00151                         width, precision, arg); \
00152                 bytes_formatted = strlen(OutString.string+OutString.length); \
00153                 break; \
00154         } \
00155         OutString.length += bytes_formatted; \
00156         XSB_StrNullTerminate(&OutString);
00157 #endif
00158 
00159 
00160 xsbBool fmt_write(CTXTdecl);
00161 xsbBool fmt_write_string(CTXTdecl);
00162 xsbBool fmt_read(CTXTdecl);
00163 
00164 
00165 #include "ptoc_tag_xsb_i.h"
00166             
00167 
00168 xsbBool formatted_io (CTXTdecl)
00169 {
00170   switch (ptoc_int(CTXTc 1)) {
00171   case FMT_WRITE: return fmt_write(CTXT);
00172   case FMT_WRITE_STRING: return fmt_write_string(CTXT);
00173   case FMT_READ: return fmt_read(CTXT);
00174   default:
00175     xsb_abort("[FORMATTED_IO] Invalid operation number: %d", ptoc_int(CTXTc 1));
00176   }
00177   return TRUE; /* just to get rid of compiler warning */
00178 }
00179 
00180 /*----------------------------------------------------------------------
00181     like fprintf
00182      C invocation: formatted_io(FMT_WRITE, IOport, Format, ValTerm)
00183      Prolog invocation: fmt_write(+IOport, +Format, +ValTerm)
00184        IOport: XSB I/O port
00185        Format: format as atom or string;
00186        ValTerm: term whose args are vars to receive values returned.
00187 ----------------------------------------------------------------------*/
00188 /* The following definitions are to use the threadsafe char buffers,
00189    but use more reasonable names.  These buffers will just grow
00190    (unless they are explicitly shrunk.)  They provide global buffers
00191    without having to malloc them every time.  The names are undef'ed
00192    at the end. */
00193 #define FmtBuf (*tsgSBuff1)
00194 #define StrArgBuf (*tsgSBuff2)
00195 
00196 xsbBool fmt_write(CTXTdecl)
00197 {
00198   FILE *fptr;                   /* working variable */
00199   struct next_fmt_state fmt_state;
00200   char *Fmt=NULL, *str_arg;
00201   char aux_msg[50];
00202   prolog_term ValTerm, Arg, Fmt_term;
00203   int i, Arity=0;
00204   long int_arg;                               /* holder for int args         */
00205   double float_arg;                           /* holder for float args       */
00206   struct fmt_spec *current_fmt_spec = (struct fmt_spec *)mem_alloc(sizeof(struct fmt_spec),LEAK_SPACE);
00207   int width=0, precision=0;                   /* these are used in conjunction
00208                                                  with the *.* format         */
00209   XSB_StrSet(&FmtBuf,"");
00210   XSB_StrSet(&StrArgBuf,"");
00211   fmt_state._workspace = tsgLBuff2;
00212   XSB_StrSet(fmt_state._workspace,"");
00213 
00214   SET_FILEPTR(fptr, ptoc_int(CTXTc 2));
00215   Fmt_term = reg_term(CTXTc 3);
00216   if (is_list(Fmt_term))
00217     Fmt = p_charlist_to_c_string(CTXTc Fmt_term,&FmtBuf,"FMT_WRITE","format string");
00218   else if (isstring(Fmt_term))
00219     Fmt = string_val(Fmt_term);
00220   else
00221     xsb_abort("[FMT_WRITE] Format must be an atom or a character string");
00222   ValTerm = reg_term(CTXTc 4);
00223   if (isconstr(ValTerm) && !isboxed(ValTerm))
00224     Arity = get_arity(get_str_psc(ValTerm));
00225   else if (isref(ValTerm))
00226     /* Var in the argument position means, no arguments */
00227     Arity = 0;
00228   else {
00229     /* assume single argument; convert ValTerm into arg(val) */
00230     prolog_term TmpValTerm=p2p_new(CTXT);
00231 
00232     c2p_functor(CTXTc "arg", 1, TmpValTerm);
00233     if (isstring(ValTerm))
00234       c2p_string(CTXTc string_val(ValTerm), p2p_arg(TmpValTerm,1));
00235     else if (isinteger(ValTerm)|isboxedinteger(ValTerm))
00236       c2p_int(CTXTc oint_val(ValTerm), p2p_arg(TmpValTerm,1));
00237      else if (isofloat(ValTerm))
00238       c2p_float(CTXTc ofloat_val(ValTerm), p2p_arg(TmpValTerm,1));
00239     else
00240       xsb_abort("Usage: fmt_write([+IOport,] +FmtStr, +args(A1,A2,...))");
00241     ValTerm = TmpValTerm;
00242     Arity = 1;
00243   }
00244 
00245   next_format_substr(CTXTc Fmt, &fmt_state,current_fmt_spec,
00246                      1,   /* initialize                      */
00247                      0);  /* write                   */
00248   xsb_segfault_message =
00249     "++FMT_WRITE: Argument type doesn't match format specifier\n";
00250   signal(SIGSEGV, &xsb_segfault_catcher);
00251   
00252   i=0;
00253   while (i <= Arity) {
00254     /* last format substring (and has no conversion spec) */
00255     if (current_fmt_spec->type == '.') {
00256       PRINT_ARG("");
00257       if (i < Arity)
00258         xsb_warn("[FMT_WRITE] More arguments than format specifiers");
00259       goto EXIT_WRITE;
00260     }
00261 
00262     i++; /* increment after checking the last format segment */
00263 
00264     if (current_fmt_spec->size >  1) {
00265       Arg = p2p_arg(ValTerm,i++);
00266       width = (int) int_val(Arg);
00267     } 
00268     CHECK_ARITY(i, Arity, "FMT_WRITE");
00269 
00270     if (current_fmt_spec->size == 3) {
00271       Arg = p2p_arg(ValTerm,i++);
00272       precision = (int) int_val(Arg);
00273     }
00274     CHECK_ARITY(i, Arity, "FMT_WRITE");
00275 
00276     Arg = p2p_arg(ValTerm,i);
00277 
00278     if (current_fmt_spec->type == '!') { /* ignore field */
00279     } else if (current_fmt_spec->type == 'S') {
00280       /* Any type: print as a string */
00281       XSB_StrSet(&StrArgBuf,"");
00282       print_pterm(CTXTc Arg, TRUE, &StrArgBuf);
00283       PRINT_ARG(StrArgBuf.string);
00284     } else if (isstring(Arg) && !isnil(Arg)) {
00285       TYPE_ERROR_CHK('s', "FMT_WRITE");
00286       str_arg = string_val(Arg);
00287       PRINT_ARG(str_arg);
00288     } else if (islist(Arg) || isnil(Arg)) {
00289       TYPE_ERROR_CHK('s', "FMT_WRITE");
00290       sprintf(aux_msg, "argument %d", i);
00291       str_arg = p_charlist_to_c_string(CTXTc Arg, &StrArgBuf, "FMT_WRITE", aux_msg);
00292       PRINT_ARG(str_arg);
00293     } else if (isinteger(Arg)|isboxedinteger(Arg)) {
00294       TYPE_ERROR_CHK('i', "FMT_WRITE");
00295       int_arg = oint_val(Arg);
00296       PRINT_ARG(int_arg);
00297     } else if (isofloat(Arg)) {
00298       TYPE_ERROR_CHK('f', "FMT_WRITE")
00299       float_arg = ofloat_val(Arg);
00300       PRINT_ARG(float_arg);
00301     } else {
00302       xsb_abort("[FMT_WRITE] Argument %d has illegal type", i);
00303     }
00304     next_format_substr(CTXTc Fmt, &fmt_state,current_fmt_spec,
00305                        0 /* don't initialize */,
00306                        0 /* write */ );
00307   }
00308 
00309   /* print the remainder of the format string, if it exists */
00310   if (current_fmt_spec->type == '.')
00311       PRINT_ARG("");
00312 
00313  EXIT_WRITE:
00314   xsb_segfault_message = xsb_default_segfault_msg;
00315   signal(SIGSEGV, xsb_default_segfault_handler);
00316   
00317   mem_dealloc(current_fmt_spec,sizeof(struct fmt_spec),LEAK_SPACE);
00318   return TRUE;
00319 }
00320 
00321 #undef FmtBuf
00322 #undef StrArgBuf
00323 
00324 /*----------------------------------------------------------------------
00325    like sprintf:
00326     C invocation: formatted_io(FMT_WRITE_STRING, String, Format, ValTerm)
00327     Prolog invocation: fmt_write_string(-String, +Format, +ValTerm)
00328       String: string buffer
00329       Format: format as atom or string;
00330       ValTerm: Term whose args are vars to receive values returned.
00331 ----------------------------------------------------------------------*/
00332 
00333 #define MAX_SPRINTF_STRING_SIZE MAX_IO_BUFSIZE
00334 
00335 /* If no snprintf, we fill only half of OutString, to be on the safe side */
00336 #ifdef HAVE_SNPRINTF
00337 #define SAFE_OUT_SIZE MAX_SPRINTF_STRING_SIZE
00338 int sprintf(char *s, const char *format, /* args */ ...);
00339 #else
00340 #define SAFE_OUT_SIZE MAX_SPRINTF_STRING_SIZE/2
00341 #endif
00342 
00343 #define OutString (*tsgLBuff1)
00344 #define FmtBuf (*tsgSBuff1)
00345 #define StrArgBuf (*tsgSBuff2)
00346 
00347 xsbBool fmt_write_string(CTXTdecl)
00348 {
00349   char *Fmt=NULL, *str_arg;
00350   struct next_fmt_state fmt_state;
00351   char aux_msg[50];
00352   prolog_term ValTerm, Arg, Fmt_term;
00353   int i, Arity;
00354   long int_arg;                             /* holder for int args          */
00355   double float_arg;                         /* holder for float args        */
00356   struct fmt_spec *current_fmt_spec = (struct fmt_spec *)mem_alloc(sizeof(struct fmt_spec),LEAK_SPACE);
00357   int width=0, precision=0;                 /* these are used in conjunction
00358                                                with the *.* format          */
00359   int bytes_formatted=0;                    /* the number of bytes formatted as
00360                                                returned by sprintf/snprintf */
00361   XSB_StrSet(&OutString,"");
00362   XSB_StrSet(&FmtBuf,"");
00363   XSB_StrSet(&StrArgBuf,"");
00364   fmt_state._workspace = tsgLBuff2;
00365   XSB_StrSet(fmt_state._workspace,"");
00366 
00367   if (isnonvar(reg_term(CTXTc 2)))
00368     xsb_abort("[FMT_WRITE_STRING] Arg 1 must be an unbound variable");
00369   
00370   Fmt_term = reg_term(CTXTc 3);
00371   if (islist(Fmt_term))
00372     Fmt = p_charlist_to_c_string(CTXTc Fmt_term, &FmtBuf,
00373                                  "FMT_WRITE_STRING", "format string");
00374   else if (isstring(Fmt_term))
00375     Fmt = string_val(Fmt_term);
00376   else
00377     xsb_abort("[FMT_WRITE_STRING] Format must be an atom or a character string");
00378 
00379   ValTerm = reg_term(CTXTc 4);
00380   if (isconstr(ValTerm) && ! isboxed(ValTerm))
00381     Arity = get_arity(get_str_psc(ValTerm));
00382   else if (isref(ValTerm))
00383     /* Var in the argument position means, no arguments */
00384     Arity = 0;
00385   else {
00386     /* assume single argument; convert ValTerm into arg(val) */
00387     prolog_term TmpValTerm=p2p_new(CTXT);
00388 
00389     c2p_functor(CTXTc "arg", 1, TmpValTerm);
00390     if (isstring(ValTerm))
00391       c2p_string(CTXTc string_val(ValTerm), p2p_arg(TmpValTerm,1));
00392     else if (isinteger(ValTerm)|isboxedinteger(ValTerm))
00393       c2p_int(CTXTc oint_val(ValTerm), p2p_arg(TmpValTerm,1));
00394     else if (isofloat(ValTerm))
00395       c2p_float(CTXTc ofloat_val(ValTerm), p2p_arg(TmpValTerm,1));
00396     else
00397       xsb_abort("Usage: fmt_write_string(-OutStr, +FmtStr, +args(A1,A2,...))");
00398 
00399     ValTerm = TmpValTerm;
00400     Arity = 1;
00401   }
00402 
00403   next_format_substr(CTXTc Fmt, &fmt_state,current_fmt_spec,
00404                      1,  /* initialize                       */
00405                      0); /* write                    */
00406   xsb_segfault_message =
00407     "++FMT_WRITE_STRING: Argument type doesn't match format specifier\n";
00408   signal(SIGSEGV, &xsb_segfault_catcher);
00409   
00410   i=0;
00411   while (i <= Arity) {
00412     /* last string (and has no conversion spec) */
00413     if (current_fmt_spec->type == '.') {
00414       SPRINT_ARG("");
00415       if (i < Arity)
00416         xsb_warn("[FMT_WRITE_STRING] More arguments than format specifiers");
00417       goto EXIT_WRITE_STRING;
00418     }
00419 
00420     i++; /* increment after checking the last format segment */
00421 
00422     if (current_fmt_spec->size >  1) {
00423       Arg = p2p_arg(ValTerm,i++);
00424       width = (int) int_val(Arg);
00425     } 
00426     CHECK_ARITY(i, Arity, "FMT_WRITE_STRING");
00427 
00428     if (current_fmt_spec->size == 3) {
00429       Arg = p2p_arg(ValTerm,i++);
00430       precision = (int) int_val(Arg);
00431     }
00432     CHECK_ARITY(i, Arity, "FMT_WRITE_STRING");
00433 
00434     Arg = p2p_arg(ValTerm,i);
00435 
00436     if (current_fmt_spec->type == '!') { /* ignore field */
00437     } else if (current_fmt_spec->type == 'S') {
00438       /* Any type: print as a string */
00439       XSB_StrSet(&StrArgBuf,"");
00440       print_pterm(CTXTc Arg, TRUE, &StrArgBuf);
00441       SPRINT_ARG(StrArgBuf.string);
00442     } else if (isstring(Arg)) {
00443       TYPE_ERROR_CHK('s', "FMT_WRITE_STRING");
00444       str_arg = string_val(Arg);
00445       SPRINT_ARG(str_arg);
00446     } else if (islist(Arg)) {
00447       TYPE_ERROR_CHK('s', "FMT_WRITE_STRING");
00448       sprintf(aux_msg, "argument %d", i);
00449       str_arg = p_charlist_to_c_string(CTXTc Arg, &StrArgBuf,
00450                                        "FMT_WRITE_STRING", aux_msg);
00451       SPRINT_ARG(str_arg);
00452     } else if (isinteger(Arg)|isboxedinteger(Arg)) {
00453       TYPE_ERROR_CHK('i', "FMT_WRITE_STRING");
00454       int_arg = oint_val(Arg);
00455       SPRINT_ARG(int_arg);
00456     } else if (isofloat(Arg)) {
00457       TYPE_ERROR_CHK('f', "FMT_WRITE_STRING");
00458       float_arg = ofloat_val(Arg);
00459       SPRINT_ARG(float_arg);
00460     } else {
00461       xsb_abort("[FMT_WRITE_STRING] Argument %d has illegal type", i);
00462     }
00463     next_format_substr(CTXTc Fmt, &fmt_state,current_fmt_spec,
00464                        0 /* don't initialize */,
00465                        0 /* write */ );
00466   }
00467 
00468   /* print the remainder of the format string, if it exists */
00469   if (current_fmt_spec->type == '.') {
00470       SPRINT_ARG("");
00471   }
00472 
00473  EXIT_WRITE_STRING:
00474   xsb_segfault_message = xsb_default_segfault_msg;
00475   signal(SIGSEGV, xsb_default_segfault_handler);
00476 
00477   mem_dealloc(current_fmt_spec,sizeof(struct fmt_spec),LEAK_SPACE);
00478   /* fmt_write_string is used in places where interning of the string is needed
00479      (such as constructing library search paths)
00480      Therefore, must use string_find(..., 1). */
00481   ctop_string(CTXTc 2, string_find(OutString.string,1));
00482   
00483   return TRUE;
00484 }
00485 #undef OutString
00486 #undef FmtBuf
00487 #undef StrArgBuf
00488 
00489 
00490 /*----------------------------------------------------------------------
00491    like fscanf
00492      C invocation: formatted_io(FMT_READ, IOport, Format, ArgTerm, Status)
00493      Prolog invocation: fmt_read(+IOport, +Format, -ArgTerm, -Status)
00494       IOport: XSB I/O port
00495       Format: format as atom or string;
00496       ArgTerm: Term whose args are vars to receive values returned.
00497       Status: 0 OK, -1 eof 
00498 ----------------------------------------------------------------------*/
00499 #define FmtBuf (*tsgSBuff1)
00500 #define StrArgBuf (*tsgSBuff2)
00501 #define aux_fmt (*tsgLBuff1)                  /* auxiliary fmt holder        */
00502 
00503 xsbBool fmt_read(CTXTdecl)
00504 {
00505   FILE *fptr;                   /* working variable */
00506   char *Fmt=NULL;
00507   struct next_fmt_state fmt_state;
00508   prolog_term AnsTerm, Arg, Fmt_term;
00509   Integer i ;
00510   long int_arg;                               /* holder for int args         */
00511   float float_arg;                            /* holder for float args       */
00512   struct fmt_spec *current_fmt_spec = (struct fmt_spec *)mem_alloc(sizeof(struct fmt_spec),LEAK_SPACE);
00513   int Arity=0;
00514   int number_of_successes=0, curr_assignment=0;
00515   int cont; /* continuation indicator */
00516   int chars_accumulator=0, curr_chars_consumed=0;
00517 
00518   XSB_StrSet(&FmtBuf,"");
00519   XSB_StrSet(&StrArgBuf,"");
00520   XSB_StrSet(&aux_fmt,"");
00521   fmt_state._workspace = tsgLBuff2;
00522   XSB_StrSet(fmt_state._workspace,"");
00523 
00524   SET_FILEPTR(fptr, ptoc_int(CTXTc 2));
00525   Fmt_term = reg_term(CTXTc 3);
00526   if (islist(Fmt_term))
00527     Fmt = p_charlist_to_c_string(CTXTc Fmt_term,&FmtBuf,"FMT_READ","format string");
00528   else if (isstring(Fmt_term))
00529     Fmt = string_val(Fmt_term);
00530   else
00531     xsb_abort("[FMT_READ] Format must be an atom or a character string");
00532 
00533   AnsTerm = reg_term(CTXTc 4);
00534   if (isconstr(AnsTerm))
00535     Arity = get_arity(get_str_psc(AnsTerm));
00536   else if (isref(AnsTerm)) {
00537     /* assume that only one input val is reuired */
00538     prolog_term TmpAnsTerm=p2p_new(CTXT), TmpArg;
00539 
00540     Arity = 1;
00541     c2p_functor(CTXTc "arg", 1, TmpAnsTerm);
00542     /* The following is a bit tricky: Suppose AnsTerm was X.
00543        We unify AnsTerm (which is avriable) with
00544        TmpArg, the argument of the new term TmpAnsTerm.
00545        Then the variable AnsTerm is reset to TmpAnsTerm so that the rest of the
00546        code would think that AnsTerm was arg(X).
00547        Eventually, X will get bound to the result */
00548     TmpArg = p2p_arg(TmpAnsTerm,1);
00549     p2p_unify(CTXTc TmpArg, AnsTerm);
00550     AnsTerm = TmpAnsTerm;
00551   } else
00552     xsb_abort("Usage: fmt_read([IOport,] FmtStr, args(A1,A2,...), Feedback)");
00553 
00554   /* status variable */
00555   if (isnonvar(reg_term(CTXTc 5)))
00556     xsb_abort("[FMT_READ] Arg 4 must be an unbound variable");
00557 
00558   next_format_substr(CTXTc Fmt, &fmt_state,current_fmt_spec,
00559                      1,   /* initialize                      */
00560                      1);  /* read                    */
00561   XSB_StrSet(&aux_fmt, current_fmt_spec->fmt);
00562   XSB_StrAppend(&aux_fmt,"%n");
00563 
00564   for (i = 1; (i <= Arity); i++) {
00565     Arg = p2p_arg(AnsTerm,i);
00566     cont = 0;
00567     curr_chars_consumed=0;
00568 
00569     /* if there was an assignment suppression spec, '*' */
00570     if (current_fmt_spec->size == 0)
00571       current_fmt_spec->type = '-';
00572 
00573     switch (current_fmt_spec->type) {
00574     case '-':
00575       /* we had an assignment suppression character: just count how 
00576          many chars were scanned, don't skip to the next scan variable */
00577       fscanf(fptr, aux_fmt.string, &curr_chars_consumed);
00578       curr_assignment = 0;
00579       i--; /* don't skip scan variable */
00580       cont = 1; /* don't leave the loop */
00581       break;
00582     case '.': /* last format substring (and has no conversion spec) */
00583       curr_assignment = fscanf(fptr, current_fmt_spec->fmt);
00584       if (isref(Arg))
00585         xsb_warn("[FMT_READ] More arguments than format specifiers");
00586       goto EXIT_READ;
00587     case 's':
00588       XSB_StrEnsureSize(&StrArgBuf, MAX_IO_BUFSIZE);
00589       curr_assignment = fscanf(fptr, aux_fmt.string,
00590                                StrArgBuf.string,
00591                                &curr_chars_consumed);
00592       /* if no match, leave prolog variable uninstantiated;
00593          if it is a prolog constant, then return FALSE (no unification) */
00594       if (curr_assignment <= 0) {
00595         if (isref(Arg)) break;
00596         else goto EXIT_READ_FALSE;
00597       }
00598       if (isref(Arg))
00599         c2p_string(CTXTc StrArgBuf.string,Arg);
00600       else if (strcmp(StrArgBuf.string, string_val(Arg)))
00601         goto EXIT_READ_FALSE;
00602       break;
00603     case 'n':
00604       int_arg = -1;
00605       curr_assignment = fscanf(fptr, current_fmt_spec->fmt, &int_arg);
00606       if (int_arg < 0) break; /* scanf failed before reaching %n */
00607       cont = 1; /* don't leave the loop */
00608       curr_chars_consumed = int_arg;
00609       int_arg += chars_accumulator;
00610       if (isref(Arg))
00611         c2p_int(CTXTc int_arg,Arg);
00612       else xsb_abort("[FMT_READ] Argument %i must be a variable", i);
00613       break;
00614     case 'i':
00615       curr_assignment = fscanf(fptr, aux_fmt.string,
00616                                &int_arg, &curr_chars_consumed);
00617       /* if no match, leave prolog variable uninstantiated;
00618          if it is a prolog constant, then return FALSE (no unification) */
00619       if (curr_assignment <= 0) {
00620         if (isref(Arg)) break;
00621         else goto EXIT_READ_FALSE;
00622       }
00623       if (isref(Arg))
00624         c2p_int(CTXTc int_arg,Arg);
00625       else if (int_arg != (Integer)oint_val(Arg)) goto EXIT_READ_FALSE;
00626       break;
00627     case 'f':
00628       curr_assignment = fscanf(fptr, aux_fmt.string,
00629                                &float_arg, &curr_chars_consumed);
00630       /* floats never unify with anything */
00631       if (!isref(Arg)) goto EXIT_READ_FALSE;
00632       /* if no match, leave prolog variable uninstantiated */
00633       if (curr_assignment <= 0) break;
00634       c2p_float(CTXTc float_arg, Arg);
00635       break;
00636     default:
00637       xsb_abort("[FMT_READ] Unsupported format specifier for argument %d", i);
00638     }
00639 
00640     chars_accumulator +=curr_chars_consumed;
00641 
00642     /* format %n shouldn't cause us to leave the loop */
00643     if (curr_assignment > 0 || cont)
00644       number_of_successes =
00645         (curr_assignment ? number_of_successes+1 : number_of_successes);
00646     else
00647       break;
00648 
00649     next_format_substr(CTXTc Fmt, &fmt_state,current_fmt_spec,
00650                        0 /* don't initialize */,
00651                        1 /* read */ );
00652     XSB_StrSet(&aux_fmt, current_fmt_spec->fmt);
00653     XSB_StrAppend(&aux_fmt,"%n");
00654   }
00655 
00656   /* if there are format specifiers beyond what corresponds to the last
00657      variable then we make use of %* (suppression) and of non-format
00658      strings. The leftover format specifiers are ignored. */
00659   /* last format substr without conversion spec */
00660   if (current_fmt_spec->type == '.')
00661     curr_assignment = fscanf(fptr, current_fmt_spec->fmt);
00662   /* last format substr with assignment suppression (spec size=0) */
00663   if (current_fmt_spec->size == 0)
00664     fscanf(fptr, aux_fmt.string, &curr_chars_consumed);
00665 
00666   /* check for end of file */
00667   if ((number_of_successes == 0) && (curr_assignment < 0))
00668     number_of_successes = -1;
00669 
00670  EXIT_READ:
00671   mem_dealloc(current_fmt_spec,sizeof(struct fmt_spec),LEAK_SPACE);
00672   ctop_int(CTXTc 5, number_of_successes);
00673   return TRUE;
00674 
00675  EXIT_READ_FALSE:
00676   mem_dealloc(current_fmt_spec,sizeof(struct fmt_spec),LEAK_SPACE);
00677   return FALSE;
00678 }
00679 #undef FmtBuf
00680 #undef StrArgBuf
00681 #undef aux_fmt
00682 
00683 
00684 /**********
00685 In scanning a canonical term, we maintain a functor stack, and an
00686 operand stack. The functor stack has the name of the functor and a
00687 pointer to its first operand on the operand stack. The operand stack
00688 is just a stack of operands. They are Prolog terms. (How to handle
00689 variables remains to be seen.)
00690 ***/
00691 
00692 /* prevpsc is kept as an optimization to save a lookup in prolog.  If
00693    it has changed, it is reset to 0.  So it doesn't need to be
00694    protected for multithreading, since it is self-correcting. */
00695 static Psc prevpsc = 0;
00696 
00697 
00698 /* ----- handle read_cannonical errors: print msg and scan to end ----- */
00699 /***
00700 reallocate op stack.
00701 add clear findall stack at toploop
00702 ***/
00703 
00704 CPtr init_term_buffer(CTXTdeclc int *findall_chunk_index) {
00705   *findall_chunk_index = findall_init_c(CTXT);
00706   current_findall = findall_solutions + *findall_chunk_index;
00707   return current_findall->top_of_chunk ;
00708 }
00709 
00710 #define ensure_term_space(ptr,size) \
00711   if ((ptr+size) > (current_findall->current_chunk + FINDALL_CHUNCK_SIZE -1)) {\
00712         if (!get_more_chunk(CTXT)) xsb_abort("Cannot allocate space for term buffer") ;\
00713         ptr = current_findall->top_of_chunk ;\
00714   }
00715 
00716 #define free_term_buffer() findall_free(CTXTc findall_chunk_index)
00717 
00718 static int read_can_error(CTXTdeclc FILE *filep, STRFILE *instr, int prevchar, Cell prologvar, int findall_chunk_index)
00719 {
00720   char *ptr;
00721 
00722   xsb_error("READ_CAN_ERROR: illegal format. Next tokens:");
00723   while ((token->type != TK_EOC) && (token->type != TK_EOF)) {
00724     ptr = token->value;
00725     switch (token->type) {
00726     case TK_PUNC        : fprintf(stderr,"%c ", *ptr); break;
00727     case TK_VARFUNC     : fprintf(stderr,"%s ", ptr); break;
00728     case TK_VAR         : fprintf(stderr,"%s ", ptr); break;
00729     case TK_FUNC        : fprintf(stderr,"%s ", ptr); break;
00730     case TK_INT         : fprintf(stderr,"%d ", *(int *)ptr); break;
00731     case TK_ATOM        : fprintf(stderr,"%s ", ptr); break;
00732     case TK_VVAR        : fprintf(stderr,"%s ", ptr); break;
00733     case TK_VVARFUNC    : fprintf(stderr,"%s ", ptr); break;
00734     case TK_REAL        : fprintf(stderr,"%f ", *(double *)ptr); break;
00735     case TK_STR         : fprintf(stderr,"%s ", ptr); break;
00736     case TK_LIST        : fprintf(stderr,"%s ", ptr); break;
00737     case TK_HPUNC       : fprintf(stderr,"%c ", *ptr); break;
00738     case TK_INTFUNC     : fprintf(stderr,"%d ", *(int *)ptr); break;
00739     case TK_REALFUNC    : fprintf(stderr,"%f ", *(double *)ptr); break;
00740     }
00741     token = GetToken(CTXTc filep,instr,prevchar);
00742     prevchar = token-> nextch;
00743   }
00744   if (token->type == TK_EOC)
00745     fprintf(stderr,".\n");
00746   else
00747     fprintf(stderr,"\n");
00748   free_term_buffer();
00749   unify(CTXTc prologvar,makestring(string_find("read_canonical_error",1)));
00750   return 0;
00751 }
00752 
00753 
00754 /* Read a canonical term from XSB I/O port in r1 and put answer in variable in
00755    r2; r3 set to 0 if ground fact (non zero-ary), to 1 if variable or :-.
00756    Fail on EOF */
00757 
00758 #define INIT_STK_SIZE 32
00759 #define MAX_INIT_STK_SIZE 1000
00760 
00761 #define FUNFUN 0
00762 #define FUNLIST 1
00763 #define FUNDTLIST 2
00764 #define FUNCOMMALIST 3
00765 
00766 #ifndef MULTI_THREAD
00767 int opstk_size = 0;
00768 int funstk_size = 0;
00769 struct funstktype *funstk;
00770 struct opstktype *opstk;
00771 struct vartype rc_vars[MAXVAR];
00772 #endif
00773 
00774 #define setvar(loc,op1) \
00775     if (rc_vars[opstk[op1].op].varval) \
00776        cell(loc) = rc_vars[opstk[op1].op].varval; \
00777     else { \
00778              cell(loc) = (Cell) loc; \
00779              rc_vars[opstk[op1].op].varval = (Cell) loc; \
00780          }
00781 
00782 #define expand_opstk {\
00783     opstk_size = opstk_size+opstk_size;\
00784     opstk = (struct opstktype *)mem_realloc(opstk,(opstk_size/2)*sizeof(struct opstktype),\
00785                                             opstk_size*sizeof(struct opstktype),READ_CAN_SPACE);\
00786     if (!opstk) xsb_abort("[READ_CANONICAL] Out of space for operand stack");\
00787     /*printf("RC opstk expanded to %d\n",opstk_size);*/ \
00788   }
00789 #define expand_funstk {\
00790     funstk_size = funstk_size+funstk_size;\
00791     funstk = (struct funstktype *)mem_realloc(funstk,(funstk_size/2)*sizeof(struct funstktype),\
00792                                           funstk_size*sizeof(struct funstktype),READ_CAN_SPACE);\
00793     if (!funstk) xsb_abort("[READ CANONICAL] Out of space for function stack");\
00794     /*printf("RC funstk expanded to %d\n",funstk_size);*/ \
00795   }
00796 
00797 int read_canonical(CTXTdecl)
00798 {
00799   FILE *filep;
00800   STRFILE *instr;
00801   long tempfp;
00802   
00803   tempfp = ptoc_int(CTXTc 1);
00804   if (tempfp == -1000) {
00805     prevpsc = 0;
00806     return TRUE;
00807   }
00808 
00809   if ((tempfp < 0) && (tempfp >= -MAXIOSTRS)) {
00810     instr = strfileptr(tempfp);
00811     filep = NULL;
00812   } else {
00813     instr = NULL;
00814     SET_FILEPTR(filep, tempfp);
00815   }
00816   ctop_int(CTXTc 3,read_canonical_term(CTXTc filep, instr, 1));
00817   return TRUE;
00818 }
00819 
00820 Cell read_canonical_return_var(CTXTdeclc int code) {
00821   if (code == 1) { /* from read_canonical */
00822     return (Cell)ptoc_tag(CTXTc 2);
00823   } else if (code == 2) { /* from odbc */
00824     Cell op1, op;
00825     op = ptoc_tag(CTXTc 4);
00826     op1 = cell(clref_val(op)+1);
00827     XSB_Deref(op1);
00828     return op1;
00829   } else return (Cell)NULL;
00830 }
00831 
00832 /* copied from emuloop.c and added param */
00833 #ifndef FAST_FLOATS
00834 inline void bld_boxedfloat_here(CTXTdeclc CPtr *h, CPtr addr, Float value)
00835 {
00836     Float tempFloat = value;
00837     new_heap_functor((*h),box_psc);
00838     bld_int(*h,((ID_BOXED_FLOAT << BOX_ID_OFFSET ) | FLOAT_HIGH_16_BITS(tempFloat) ));
00839     (*h)++;
00840     bld_int(*h,FLOAT_MIDDLE_24_BITS(tempFloat)); (*h)++;
00841     bld_int(*h,FLOAT_LOW_24_BITS(tempFloat)); (*h)++;
00842     cell(addr) = makecs((*h)-4);
00843 }
00844 #else
00845 inline void bld_boxedfloat_here(CTXTdeclc CPtr *h, CPtr addr, Float value) {
00846   bld_float(addr,value);
00847 }
00848 #endif
00849 
00850 /* read canonical term, and return prev psc pointer, if valid */
00851 int read_canonical_term(CTXTdeclc FILE *filep, STRFILE *instr, int return_location_code)
00852 {
00853   int findall_chunk_index;
00854   int funtop = 0;
00855   int optop = 0;
00856   int cvarbot = MAXVAR-1;
00857   int prevchar, arity, i, size;
00858   CPtr h;
00859   int j, op1, retpscptr;
00860   Pair sym;
00861   Float float_temp;
00862   Psc headpsc, termpsc;
00863   char *cvar;
00864   int postopreq = FALSE, varfound = FALSE;
00865   prolog_term term;
00866   Cell prologvar = read_canonical_return_var(CTXTc return_location_code);
00867   
00868   if (opstk_size == 0) {
00869     opstk = 
00870       (struct opstktype *)mem_alloc(INIT_STK_SIZE*sizeof(struct opstktype),READ_CAN_SPACE);
00871     opstk_size = INIT_STK_SIZE;
00872     funstk = 
00873       (struct funstktype *)mem_alloc(INIT_STK_SIZE*sizeof(struct funstktype),READ_CAN_SPACE);
00874     funstk_size = INIT_STK_SIZE;
00875   }
00876 
00877   /* get findall buffer to read term into */
00878   h = init_term_buffer(CTXTc &findall_chunk_index);
00879   size = 0;
00880 
00881   prevchar = 10;
00882   while (1) {
00883         token = GetToken(CTXTc filep,instr,prevchar);
00884 /*      print_token((int)(token-f>type),(char *)(token->value)); */
00885         prevchar = token->nextch;
00886         if (postopreq) {  /* must be an operand follower: , or ) or | or ] */
00887             if (token->type == TK_PUNC) {
00888                 if (*token->value == ')') {
00889                   CPtr this_term;
00890                   funtop--;
00891                   if (funstk[funtop].funtyp == FUNFUN) {
00892                     arity = optop - funstk[funtop].funop;
00893                     ensure_term_space(h,arity+1);
00894                     this_term = h;
00895                     op1 = funstk[funtop].funop;
00896                     if ((arity == 2) && !(strcmp(funstk[funtop].fun,"."))) {
00897                       if (opstk[op1].typ == TK_VAR) { setvar(h,op1) }
00898                       else cell(h) = opstk[op1].op;
00899                       h++;
00900                       if (opstk[op1+1].typ == TK_VAR) { setvar(h,op1+1) }
00901                       else cell(h) = opstk[op1+1].op;
00902                       h++;
00903                       opstk[op1].op = makelist(this_term);
00904                       opstk[op1].typ = TK_FUNC;
00905                       size += 2;
00906                     } else {
00907                       size += arity+1;
00908                       sym = (Pair)insert(funstk[funtop].fun,(char)arity,
00909                                          (Psc)flags[CURRENT_MODULE],&i);
00910                       new_heap_functor(h, sym->psc_ptr);
00911                       for (j=op1; j<optop; h++,j++) {
00912                         if (opstk[j].typ == TK_VAR) { setvar(h,j) }
00913                         else cell(h) = opstk[j].op;
00914                       }
00915                       opstk[op1].op = makecs(this_term);
00916                       opstk[op1].typ = TK_FUNC;
00917                     }
00918                     optop = op1+1;
00919                   } else if (funstk[funtop].funtyp == FUNCOMMALIST) {
00920                     op1 = funstk[funtop].funop;
00921                     if ((op1+1) == optop) { /* no comma-list, just parens */
00922                     } else {    /* handle comma list... */
00923                       CPtr prev_tail;
00924                       ensure_term_space(h,3);
00925                       this_term = h;
00926 
00927                       new_heap_functor(h, comma_psc);
00928 
00929                       if (opstk[op1].typ == TK_VAR) { setvar(h,op1) }
00930                       else cell(h) = opstk[op1].op;
00931                       h++;
00932                       prev_tail = h;
00933                       h++;
00934                       size += 3;
00935                       for (j=op1+1; j<optop-1; j++) {
00936                         ensure_term_space(h,3);
00937                         cell(prev_tail) = makecs(h);
00938                         new_heap_functor(h, comma_psc);
00939                         if (opstk[j].typ == TK_VAR) { setvar(h,j) }
00940                         else cell(h) = opstk[j].op;
00941                         h++;
00942                         prev_tail = h;
00943                         h++;
00944                         size += 3;
00945                       }
00946                       j = optop-1;
00947                       if (opstk[j].typ == TK_VAR) { setvar(prev_tail,j) }
00948                       else cell(prev_tail) = opstk[j].op;
00949                       opstk[op1].op = makecs(this_term);
00950                       opstk[op1].typ = TK_FUNC;
00951                       optop = op1+1;
00952                     }
00953                   } else {
00954                     return read_can_error(CTXTc filep,instr,prevchar,prologvar,findall_chunk_index); /* ')' ends a list? */
00955                   }
00956                 } else if (*token->value == ']') {      /* end of list */
00957                   CPtr this_term, prev_tail;
00958                   funtop--;
00959                   if (funstk[funtop].funtyp == FUNFUN || funstk[funtop].funtyp == FUNCOMMALIST)
00960                         return read_can_error(CTXTc filep,instr,prevchar,prologvar,findall_chunk_index);
00961                   ensure_term_space(h,2);
00962                   this_term = h;
00963                   op1 = funstk[funtop].funop;
00964 
00965                   if (opstk[op1].typ == TK_VAR) { setvar(h,op1) }
00966                   else cell(h) = opstk[op1].op;
00967                   h++;
00968                   size += 2;
00969                   if ((op1+1) == optop) {
00970                         cell(h) = makenil;
00971                         h++;
00972                   } else {
00973                         prev_tail = h;
00974                         h++;
00975                         for (j=op1+1; j<optop-1; j++) {
00976                           ensure_term_space(h,2);
00977                           cell(prev_tail) = makelist(h);
00978                           if (opstk[j].typ == TK_VAR) { setvar(h,j) }
00979                           else cell(h) = opstk[j].op;
00980                           h++;
00981                           prev_tail = h;
00982                           h++;
00983                           size += 2;
00984                         }
00985                         j = optop-1;
00986                         if (funstk[funtop].funtyp == FUNLIST) {
00987                           ensure_term_space(h,2);
00988                           cell(prev_tail) = makelist(h);
00989                           if (opstk[j].typ == TK_VAR) { setvar(h,j) }
00990                           else cell(h) = opstk[j].op;
00991                           h++;
00992                           prev_tail = h;
00993                           h++;
00994                           cell(prev_tail) = makenil;
00995                           size += 2;
00996                         } else {
00997                           if (opstk[j].typ == TK_VAR) { setvar(prev_tail,j) }
00998                           else cell(prev_tail) = opstk[j].op;
00999                         }
01000                   }
01001                   opstk[op1].op = makelist(this_term);
01002                   opstk[op1].typ = TK_FUNC;
01003                   optop = op1+1;
01004                 } else if (*token->value == ',') {
01005                   postopreq = FALSE;
01006                 } else if (*token->value == '|') {
01007                   postopreq = FALSE;
01008                   if (funstk[funtop-1].funtyp != FUNLIST) 
01009                         return read_can_error(CTXTc filep,instr,prevchar,prologvar,findall_chunk_index);
01010                   funstk[funtop-1].funtyp = FUNDTLIST;
01011                 } else return read_can_error(CTXTc filep,instr,prevchar,prologvar,findall_chunk_index);
01012       } else {  /* check for neg numbers and backpatch if so */
01013                 if (opstk[optop-1].typ == TK_ATOM && 
01014                                 !strcmp("-",string_val(opstk[optop-1].op))) {
01015                   if (token->type == TK_INT) {
01016                         opstk[optop-1].typ = TK_INT;
01017                         opstk[optop-1].op = makeint(-(*(int *)token->value));
01018                   } else if (token->type == TK_REAL) {
01019                         float_temp = (Float) *(double *)(token->value);
01020 #ifdef FAST_FLOATS
01021                         opstk[optop-1].typ = TK_REAL;
01022                         opstk[optop-1].op = makefloat((float)-float_temp); // lose precision FIX!!
01023 #else
01024                         ensure_term_space(h,4); // size of boxfloat
01025                         opstk[optop-1].typ = TK_FUNC;
01026                         bld_boxedfloat_here(CTXTc &h, &opstk[optop-1].op, -float_temp);
01027 #endif
01028                   } else return read_can_error(CTXTc filep,instr,prevchar,prologvar,findall_chunk_index);
01029                 } else return read_can_error(CTXTc filep,instr,prevchar,prologvar,findall_chunk_index);
01030       }
01031     } else {  /* must be an operand */
01032       switch (token->type) {
01033       case TK_PUNC:
01034                 if (*token->value == '[') {
01035                   if(token->nextch == ']') {
01036                         if (optop >= opstk_size) expand_opstk;
01037                         token = GetToken(CTXTc filep,instr,prevchar);
01038                         /* print_token(token->type,token->value); */
01039                         prevchar = token->nextch;
01040                         opstk[optop].typ = TK_ATOM;
01041                         opstk[optop].op = makenil;
01042                         optop++;
01043                         postopreq = TRUE;
01044                   } else {      /* beginning of a list */
01045                         if (funtop >= funstk_size) expand_funstk;
01046                         funstk[funtop].funop = optop;
01047                         funstk[funtop].funtyp = FUNLIST; /* assume regular list */
01048                         funtop++;
01049                   }
01050                   break;
01051                 } else if (*token->value == '(') { /* beginning of comma list */
01052                   if (funtop >= funstk_size) expand_funstk;
01053                   funstk[funtop].funop = optop;
01054                   funstk[funtop].funtyp = FUNCOMMALIST;
01055                   funtop++;
01056                   break;
01057                 }
01058           /* let a punctuation mark be a functor symbol */
01059       case TK_FUNC:
01060                 if (funtop >= funstk_size) expand_funstk;
01061                 funstk[funtop].fun = (char *)string_find(token->value,1);
01062                 funstk[funtop].funop = optop;
01063                 funstk[funtop].funtyp = FUNFUN; /* functor */
01064                 funtop++;
01065 
01066                 if (token->nextch != '(')
01067                         return read_can_error(CTXTc filep,instr,prevchar,prologvar,findall_chunk_index);
01068                 token = GetToken(CTXTc filep,instr,prevchar);
01069                 /* print_token(token->type,token->value); */
01070                 prevchar = token->nextch;
01071                 break;
01072       case TK_VVAR:
01073                 if ((token->value)[1] == 0) { /* anonymous var */
01074                   if (cvarbot < 0)
01075                     xsb_abort("[READ_CANONICAL] too many variables in term");
01076                   i = cvarbot;
01077                   rc_vars[cvarbot].varid = (Cell) "_";
01078                   rc_vars[cvarbot].varval = 0;
01079                   cvarbot--;
01080                   if (optop >= opstk_size) expand_opstk;
01081                   opstk[optop].typ = TK_VAR;
01082                   opstk[optop].op = (prolog_term) i;
01083                   optop++;
01084                   postopreq = TRUE;
01085                   break;
01086                 }  /* else fall through and treat as regular var*/
01087       case TK_VAR:
01088                 varfound = TRUE;
01089                 cvar = (char *)string_find(token->value,1);
01090                 i = MAXVAR-1;
01091                 while (i>cvarbot) {
01092                   if (cvar == (char *)rc_vars[i].varid) break;
01093                   i--;
01094                 }
01095                 if (i == cvarbot) {
01096                   if (cvarbot < 0)
01097                     xsb_abort("[READ_CANONICAL] too many variables in term");
01098                   rc_vars[cvarbot].varid = (Cell) cvar;
01099                   rc_vars[cvarbot].varval = 0;
01100                   cvarbot--;
01101                 }
01102                 if (optop >= opstk_size) expand_opstk;
01103                 opstk[optop].typ = TK_VAR;
01104                 opstk[optop].op = (prolog_term) i;
01105                 optop++;
01106                 postopreq = TRUE;
01107                 break;
01108       case TK_REAL:
01109                 if (optop >= opstk_size) expand_opstk;
01110                 float_temp = (Float)* (double *)(token->value);
01111 #ifdef FAST_FLOATS              
01112                 opstk[optop].typ = TK_REAL;
01113                 opstk[optop].op = makefloat((float)float_temp); // lose precision  FIX!!
01114 #else
01115                 ensure_term_space(h,4); // size of boxfloat
01116                 opstk[optop].typ = TK_FUNC;
01117                 bld_boxedfloat_here(CTXTc &h, &opstk[optop].op, float_temp);
01118 #endif
01119                 optop++;
01120                 postopreq = TRUE;
01121                 break;
01122       case TK_INT:
01123                 if (optop >= opstk_size) expand_opstk;
01124                 opstk[optop].typ = TK_INT;
01125                 opstk[optop].op = makeint(*(long *)token->value);
01126                 optop++;
01127                 postopreq = TRUE;
01128                 break;
01129       case TK_ATOM:
01130                 if (optop >= opstk_size) expand_opstk;
01131                 opstk[optop].typ = TK_ATOM;
01132                 opstk[optop].op = makestring((char *)string_find(token->value,1));
01133                 optop++;
01134                 postopreq = TRUE;
01135                 break;
01136       case TK_LIST:  /* "-list */
01137         if (optop >= opstk_size) expand_opstk;
01138         if ((token->value)[0] == 0) {
01139           opstk[optop].typ = TK_ATOM;
01140           opstk[optop].op = makenil;
01141           optop++;
01142           postopreq = TRUE;
01143           break;
01144         } else {
01145           CPtr this_term, prev_tail;
01146           char *charptr = token->value;
01147           ensure_term_space(h,2);
01148           this_term = h;
01149           cell(h) = makeint((int)*charptr); charptr++;
01150           h++;
01151           prev_tail = h;
01152           h++;
01153           size += 2;
01154           while (*charptr != 0) {
01155             ensure_term_space(h,2);
01156             cell(prev_tail) = makelist(h);
01157             cell(h) = makeint((int)*charptr); charptr++;
01158             h++;
01159             prev_tail = h;
01160             h++;
01161             size += 2;
01162           }
01163           cell(prev_tail) = makenil;
01164           opstk[optop].op = makelist(this_term);
01165           opstk[optop].typ = TK_FUNC;
01166           optop++;
01167           postopreq = TRUE;
01168           break;
01169         }
01170       case TK_EOF:
01171         free_term_buffer();
01172         if (isnonvar(prologvar)) 
01173           xsb_abort("[READ_CANONICAL] Argument must be a variable");
01174         unify(CTXTc prologvar,makestring(string_find("end_of_file",1)));
01175         return 0;
01176       default: return read_can_error(CTXTc filep,instr,prevchar,prologvar,findall_chunk_index);
01177       }
01178     }
01179     if (funtop == 0) {  /* term is finished */
01180       token = GetToken(CTXTc filep,instr,prevchar);
01181       /* print_token(token->type,token->value); */
01182       prevchar = token->nextch; /* accept EOF as end_of_clause */
01183       if (token->type != TK_EOF && token->type != TK_EOC) 
01184         return read_can_error(CTXTc filep,instr,prevchar,prologvar,findall_chunk_index);
01185 
01186       if (opstk[0].typ != TK_VAR) {  /* if a variable, then a noop */
01187         if (isnonvar(prologvar)) 
01188           xsb_abort("[READ_CANONICAL] Argument must be a variable");
01189         term = opstk[0].op;
01190         
01191         check_glstack_overflow(5, pcreg, (size+1)*sizeof(Cell)) ;
01192         /* get return location again, in case it moved, whole reasong for r_c_r_v */
01193         prologvar = read_canonical_return_var(CTXTc return_location_code); 
01194         gl_bot = (CPtr)glstack.low; gl_top = (CPtr)glstack.high;
01195         bind_ref((CPtr)prologvar,hreg);  /* build a new var to trail binding */
01196         new_heap_free(hreg);
01197         gl_bot = (CPtr)glstack.low; gl_top = (CPtr)glstack.high; /* so findall_copy* finds vars */
01198         findall_copy_to_heap(CTXTc term,(CPtr)prologvar,&hreg) ; /* this can't fail */
01199         free_term_buffer();
01200 
01201         XSB_Deref(prologvar);
01202         term = (prolog_term) prologvar;
01203         if ((isinteger(term)|isboxedinteger(term)) || 
01204             isofloat(term) || 
01205             isstring(term) ||
01206             varfound) {
01207           retpscptr = 0;
01208           prevpsc = 0;
01209         } else {
01210           termpsc = get_str_psc(term);
01211           if (termpsc == if_psc) {
01212             if (!isconstr(p2p_arg(term,1))) headpsc = 0;
01213             else headpsc = get_str_psc(p2p_arg(term,1));
01214           } else {
01215             headpsc = termpsc;
01216           }
01217           if (headpsc == prevpsc) {
01218               retpscptr = (Integer)prevpsc;
01219           } else {
01220             prevpsc = headpsc;
01221             retpscptr = 0;
01222           }
01223         }
01224       } else {
01225         retpscptr = 0;
01226         prevpsc = 0;
01227       }
01228 
01229       if (opstk_size > MAX_INIT_STK_SIZE) {
01230         mem_dealloc(opstk,opstk_size,READ_CAN_SPACE); opstk = NULL;
01231         mem_dealloc(funstk,funstk_size,READ_CAN_SPACE); funstk = NULL;
01232         opstk_size = 0; funstk_size = 0;
01233       }
01234       return retpscptr;
01235     }
01236   }
01237 }
01238 
01239 /* Scan format string and return format substrings ending with a conversion
01240    spec. The return value is a ptr to a struct that has the type of conversion
01241    spec (i, f, s) and the format substring ('.' if the whole format string has
01242    been scanned).
01243 
01244    This function doesn't fully check the validity of the conversion
01245    specifier. In case of a mistake, the result is unpredictable.
01246    We insist that a single % is a beginning of a format specifier.
01247 
01248    FORMAT: format string, INITIALIZE: 1-process new fmt string; 0 - continue
01249    with old fmt string. READ: 1 if this is called for read op; 0 for write.  */
01250 void next_format_substr(CTXTdeclc char *format, struct next_fmt_state *fmt_state,
01251                                     struct fmt_spec *result,
01252                                     int initialize, int read_op)
01253 {
01254   int pos, keep_going;
01255   char *ptr;
01256   //  static struct fmt_spec result;
01257   char *exclude, *expect; /* characters to exclude or expect */
01258 
01259   if (initialize) {
01260     current_substr_start = 0;
01261     XSB_StrSet(&workspace,format);
01262   } else {
01263     /* restore char that was replaced with \0 */
01264     workspace.string[current_substr_start] = saved_char;
01265   }
01266 
01267   pos = current_substr_start;
01268   result->type = '?';
01269   result->size = 1;
01270 
01271   /* done scanning format string */
01272   if (current_substr_start >= workspace.length) {
01273     result->type = '.'; /* last substring (and has no conversion spec) */
01274     result->fmt  = "";
01275     return;
01276   }
01277 
01278   /* find format specification: % not followed by % */
01279   do {
01280     /* last substring (and has no conversion spec) */
01281     if ((ptr=strchr(workspace.string+pos, '%')) == NULL) {
01282       current_substr_start = workspace.length;
01283       result->type = '.';  /* last substring with no type specifier */
01284       result->fmt  = workspace.string+pos;
01285       return;
01286     }
01287 
01288     pos = (ptr - workspace.string) + 1;
01289     if (workspace.string[pos] == '%')
01290       pos++;
01291     else break;
01292   } while (1);
01293 
01294   /* this doesn't do full parsing; it assumes anything that starts at % and
01295      ends at a valid conversion character is a conversion specifier. */ 
01296   keep_going = TRUE;
01297   expect = exclude = "";
01298   while ((pos < workspace.length) && keep_going) {
01299     if (strchr(exclude, workspace.string[pos]) != NULL) {
01300       xsb_abort("[FMT_READ/WRITE] Illegal format specifier `%c' in: %s",
01301                 workspace.string[pos],
01302                 workspace.string+current_substr_start);
01303     }
01304     if (strlen(expect) && strchr(expect, workspace.string[pos]) == NULL) {
01305       xsb_abort("[FMT_READ/WRITE] Illegal format specifier `%c' in: %s",
01306                 workspace.string[pos],
01307                 workspace.string+current_substr_start);
01308     }
01309 
01310     expect = exclude = "";
01311 
01312     switch (workspace.string[pos++]) {
01313     case '1': /* flags, precision, etc. */
01314     case '2':
01315     case '3':
01316     case '4':
01317     case '5':
01318     case '6':
01319     case '7':
01320     case '8':
01321     case '9':
01322       exclude = "+- #[]";
01323       break;
01324     case '.':
01325       exclude = "+- #[]";
01326       expect = "0123456789*";
01327       break;
01328     case '0':
01329     case '+':
01330     case '-':
01331       exclude = "+-[]";
01332       break;
01333     case 'h':
01334     case 'l':
01335       exclude = "+- #[]hlL";
01336       expect = "diouxXn";
01337       break;
01338     case 'L':
01339       expect = "eEfgG";
01340       exclude = "+- #[]hlL";
01341       break;
01342     case ' ':
01343     case '#':
01344       exclude = "+- #[]hlL";
01345       break;
01346     case 'c':
01347       if (read_op)
01348         result->type = 's';
01349       else
01350         result->type = 'i';
01351       keep_going = FALSE;
01352       break;
01353     case 'd':
01354     case 'i':
01355     case 'u':
01356     case 'o':
01357     case 'x':
01358     case 'X':
01359       keep_going = FALSE;
01360       result->type = 'i'; /* integer or character */
01361       break;
01362     case 'e':
01363     case 'E':
01364     case 'f':
01365     case 'g':
01366     case 'G':
01367       keep_going = FALSE;
01368       result->type = 'f'; /* float */
01369       break;
01370     case 's':
01371       keep_going = FALSE;
01372       result->type = 's'; /* string */
01373       break;
01374     case 'S':
01375       keep_going = FALSE;
01376       result->type = 'S'; /* string */
01377       workspace.string[pos-1] = 's';
01378       break;
01379     case 'p':
01380       xsb_abort("[FMT_READ/WRITE] Format specifier %%p not supported: %s",
01381                 workspace.string+current_substr_start);
01382     case 'n':
01383       if (read_op) {
01384         result->type = 'n'; /* %n is like integer, but in fmt_read we treat it
01385                               specially */
01386         keep_going = FALSE;
01387         break;
01388       }
01389       xsb_abort("[FMT_WRITE] Format specifier %%n not supported: %s",
01390                 workspace.string+current_substr_start);
01391     case '[':
01392       /* scanf feature: [...] */
01393       if (!read_op) {
01394         xsb_abort("[FMT_WRITE] Format specifier [ is invalid for output: %s",
01395                   workspace.string+current_substr_start);
01396       }
01397       while ((pos < workspace.length) && (workspace.string[pos++] != ']'));
01398       if (workspace.string[pos-1] != ']') {
01399         xsb_abort("[FMT_READ] Format specifier [ has no matching ] in: %s",
01400                   workspace.string+current_substr_start);
01401       }
01402       result->type = 's';
01403       keep_going = FALSE;
01404       break;
01405 
01406     case '*':
01407       if (read_op) {
01408         result->size = 0;
01409         break;
01410       }
01411       if (strncmp(workspace.string+pos, ".*", 2) == 0) {
01412         pos = pos+2;
01413         expect = "feEgEscdiuoxX";
01414         result->size = 3;
01415       } else if (workspace.string[pos] == '.') {
01416         pos++;
01417         expect = "0123456789";
01418         result->size = 2;
01419       } else {
01420         result->size = 2;
01421         expect = "feEgEscdiuoxX";
01422       }
01423       break;
01424 
01425     case '!':
01426       printf("set !\n");
01427       result->type = '!';
01428       keep_going = FALSE;
01429       break;
01430 
01431     default:
01432       xsb_abort("[FMT_READ/WRITE] Character `%c' in illegal format context: %s",
01433                 workspace.string[pos-1],
01434                 workspace.string+current_substr_start);
01435     }
01436   }
01437 
01438   saved_char = workspace.string[pos];
01439   workspace.string[pos] = '\0';
01440   result->fmt = workspace.string+current_substr_start;
01441   current_substr_start = pos;
01442   return;
01443 }
01444 
01445 /* TLS: changed the name of this function.  Here we are just checking
01446    whether a file pointer is present or not, rather than a file and
01447    I/O mode, as below. */
01448 
01449 int xsb_intern_fileptr(FILE *fptr, char *context,char* name,char *strmode)
01450 {
01451   int i;
01452   char mode = '\0';
01453 
01454   /*printf("xsb_intern_fileptr %x %s %s %s\n",fptr,context,name,strmode);*/
01455 
01456   if (!fptr) return -1;
01457 
01458   if (!strcmp(strmode,"rb") || !strcmp(strmode,"r"))
01459     mode = 'r';
01460   else if (!strcmp(strmode,"wb")  || !strcmp(strmode,"w"))
01461     mode = 'w';
01462   else if (!strcmp(strmode,"ab")  || !strcmp(strmode,"a"))
01463     mode = 'a';
01464   else if (!strcmp(strmode,"rb+") || !strcmp(strmode,"r+") || !strcmp(strmode,"r+b"))
01465     mode = 's';  /* (i.e. r+) */
01466   else if (!strcmp(strmode,"wb+") || !strcmp(strmode,"w+") || !strcmp(strmode,"w+b"))
01467     mode = 'x'; /* (i.e. r+) */
01468   else if (!strcmp(strmode,"ab+") || !strcmp(strmode,"a+") || !strcmp(strmode,"a+b"))
01469     mode = 'b'; /* (i.e. a+) */
01470 
01471   for (i=MIN_USR_OPEN_FILE; i < MAX_OPEN_FILES && open_files[i].file_ptr != NULL; i++);
01472   if (i == MAX_OPEN_FILES) {
01473     xsb_warn("[%s] Too many open files", context);
01474     return -1;
01475   } else {
01476     open_files[i].file_ptr = fptr;
01477     open_files[i].file_name = string_find(name,1);
01478     open_files[i].io_mode = mode;
01479   return i;
01480   }
01481 }
01482 
01483 /* static int open_files_high_water = MIN_USR_OPEN_FILE+1;*/
01484 
01485 /* Takes a file address, and mode and returns an ioport (in third
01486    argument) along with success/error.  The nonsense in the beginning
01487    is to handle possible Posix I/O modes, of which there is
01488    redundancy. */
01489 
01490 int xsb_intern_file(char *context,char *addr, int *ioport,char *strmode,int opennew)
01491 {
01492   FILE *fptr;                   /* working variable */
01493   int i, first_null, stream_found; 
01494   char mode = '\0';
01495 
01496   /*  printf("xif Context %s Addr %s strmode %s\n",context,addr,strmode);*/
01497   
01498   if (!strcmp(strmode,"rb") || !strcmp(strmode,"r"))
01499     mode = 'r';
01500   else if (!strcmp(strmode,"wb")  || !strcmp(strmode,"w"))
01501     mode = 'w';
01502   else if (!strcmp(strmode,"ab")  || !strcmp(strmode,"a"))
01503     mode = 'a';
01504   else if (!strcmp(strmode,"rb+") || !strcmp(strmode,"r+") || !strcmp(strmode,"r+b"))
01505     mode = 's';  /* (i.e. r+) */
01506   else if (!strcmp(strmode,"wb+") || !strcmp(strmode,"w+") || !strcmp(strmode,"w+b"))
01507     mode = 'x'; /* (i.e. r+) */
01508   else if (!strcmp(strmode,"ab+") || !strcmp(strmode,"a+") || !strcmp(strmode,"a+b"))
01509     mode = 'b'; /* (i.e. a+) */
01510 
01511   for (i=MIN_USR_OPEN_FILE, stream_found = -1, first_null = -1; 
01512        i < MAX_OPEN_FILES; 
01513        i++) {
01514     if (open_files[i].file_ptr != NULL) {
01515       if (!opennew && open_files[i].file_name != NULL &&
01516           !strcmp(addr,open_files[i].file_name) && 
01517           open_files[i].io_mode == mode) {
01518         stream_found = i;
01519         break;
01520       } } else if (first_null < 0) {first_null = i; if (opennew) break;}
01521   }
01522 
01523   /*
01524   printf("stream_found %d file_ptr %x file_name %s first_null %d\n",
01525          stream_found,open_files[stream_found].file_ptr,
01526          open_files[stream_found].file_name,first_null);
01527   */
01528 
01529   if (stream_found < 0 && first_null < 0) {
01530   for (i=MIN_USR_OPEN_FILE; 
01531        i < MAX_OPEN_FILES ;
01532        i++) printf("File Ptr %p Name %s\n",open_files[i].file_ptr, open_files[i].file_name);
01533 
01534     xsb_warn("[%s] Too many open files", context);
01535     *ioport = 0;
01536     return -1;
01537   }
01538   else if (stream_found >= 0) { /* File already interned */
01539     fptr = open_files[i].file_ptr;
01540     *ioport = stream_found;
01541     return 0;
01542   } 
01543   else { /* try to intern new file */
01544     struct stat stat_buff;
01545     fptr = fopen(addr, strmode);
01546     if (!fptr) {*ioport = 0; return -1;}
01547     else  if (!stat(addr, &stat_buff) && !S_ISDIR(stat_buff.st_mode)) {
01548         /* file exists and isn't a dir */
01549       open_files[first_null].file_ptr = fptr;
01550       open_files[first_null].file_name = string_find(addr,1);
01551       open_files[first_null].io_mode = mode;
01552       *ioport = first_null;
01553       return 0;
01554     }  else {
01555         xsb_warn("FILE_OPEN: File %s is a directory, cannot open!", addr);
01556         fclose(fptr);
01557         return -1;
01558     }
01559   }
01560 }
01561 
01562 void mark_open_filenames() {
01563   int i;
01564 
01565   for (i=MIN_USR_OPEN_FILE; i < MAX_OPEN_FILES; i++) {
01566     if (open_files[i].file_ptr != NULL) {
01567       mark_string(open_files[i].file_name,"Filename");
01568     }
01569   }
01570 }
01571 
01572 
01573 /*----------------------- write_quotedname/2 ---------------------------*/
01574 
01575 xsbBool quotes_are_needed(char *string)
01576 {
01577   int nextchar;
01578   int ctr, flag;
01579 
01580   if (*string == '\0') return TRUE;
01581   if (!strcmp(string,"[]")) return FALSE;
01582   if (string[0] == '/' && string[1] == '*') return TRUE;
01583   ctr = 0;
01584   nextchar = (int) string[0];
01585   flag = 0;
01586   if (nextchar >= 97 && nextchar <= 122) {    /* 0'a=97, 0'z=122  */
01587     while (nextchar != '\0' && !flag) {
01588       if (nextchar < 48 
01589           || (nextchar > 57 && nextchar < 65)
01590           || ((nextchar > 90 && nextchar < 97) && nextchar != 95)
01591           || (nextchar > 122))
01592         flag = 1;
01593       ctr++;
01594       nextchar = (int) string[ctr];
01595     }
01596     if (!flag) return FALSE;
01597   }
01598 
01599   if (string[1] == '\0') {
01600     if ((int) string[0] == 33 /*--- || (int) string[0] == 59 ---*/)
01601       return FALSE;
01602     if ((int) string[0] == 46) return TRUE;
01603   }
01604 
01605   nextchar = (int) string[0];
01606   ctr = 0; 
01607   while (nextchar != '\0' && !flag) {
01608     switch(nextchar) {
01609     case 35: case 36: case 38: case 42: case 43: case 45: case 46:
01610     case 47: case 58: case 60: case 61: case 62: case 63: case 64: 
01611     case 92: case 94: case 96: case 126:
01612       nextchar++;
01613       break;
01614     default: 
01615       flag = 1;
01616     }
01617     ctr++;
01618     nextchar = (int) string[ctr];
01619   }
01620   return flag;
01621 }
01622 
01623 
01624 void double_quotes(char *string, char *new_string)
01625 {
01626   int ctr = 0, nctr = 0;
01627 
01628   while (string[ctr] != '\0') {
01629     if (string[ctr] == '\'') {
01630       new_string[nctr] = '\'';
01631       nctr++;
01632     } else if (string[ctr] == '\\') {
01633       char ch = string[ctr+1];
01634       if (ch == 'a' || ch == 'b' || ch == 'f' || ch == 'n' || 
01635           ch == 'r' || ch == 't' || ch == 'v' || ch == 'x' || 
01636           ch == '0' || ch == '1' || ch == '2' || ch == '3' || 
01637           ch == '4' || ch == '5' || ch == '6' || ch == '7' || 
01638           ch == '\\' || ch == '"' || ch == '`') {
01639         new_string[nctr] = '\\';
01640         nctr++;
01641       }
01642     }
01643     new_string[nctr] = string[ctr];
01644     nctr++; ctr++;
01645   }
01646   new_string[nctr] = '\0';
01647 }
01648 
01649 void write_quotedname(FILE *file, char *string)
01650 {
01651   if (*string == '\0') 
01652     fprintf(file,"''");
01653   else {
01654     if (!quotes_are_needed(string)) {
01655       fprintf(file,"%s",string);
01656     }
01657     else {
01658       int neededlen = 2*strlen(string)+1;
01659       if (neededlen < 1000) {
01660         char lnew_string[1000];
01661         double_quotes(string,lnew_string);
01662         fprintf(file,"\'%s\'",lnew_string);
01663       } else {
01664         char* new_string;
01665         new_string  = (char *)mem_alloc(neededlen,LEAK_SPACE);
01666         double_quotes(string,new_string);
01667         fprintf(file,"\'%s\'",new_string);
01668         mem_dealloc(new_string,neededlen,LEAK_SPACE);
01669       }
01670     }
01671   }
01672 }
01673 
01674 /********************** write_canonical ****************/
01675 
01676 static Psc dollar_var_psc = NULL;
01677 #define wcan_string tsgLBuff1
01678 #define wcan_atombuff tsgLBuff2
01679 #define wcan_buff tsgSBuff1
01680 
01681 void call_conv write_canonical_term_rec(CTXTdeclc Cell prologterm, int letter_flag)
01682 {
01683   XSB_Deref(prologterm);
01684   switch (cell_tag(prologterm)) 
01685     {
01686     case XSB_INT:
01687       sprintf(wcan_buff->string,"%ld",(long)int_val(prologterm));
01688       XSB_StrAppendV(wcan_string,wcan_buff);
01689       break;
01690     case XSB_STRING: 
01691       if (quotes_are_needed(string_val(prologterm))) {
01692         int len_needed = 2*strlen(string_val(prologterm))+1;
01693         XSB_StrEnsureSize(wcan_atombuff,len_needed);
01694         double_quotes(string_val(prologterm),wcan_atombuff->string);
01695         XSB_StrAppendC(wcan_string,'\'');
01696         XSB_StrAppend(wcan_string,wcan_atombuff->string);
01697         XSB_StrAppendC(wcan_string,'\'');
01698       } else XSB_StrAppend(wcan_string,string_val(prologterm));
01699       break;
01700     case XSB_FLOAT:
01701       //      sprintf(wcan_buff->string,"%2.4f",float_val(prologterm));
01702       sprintf(wcan_buff->string,"%.15g",float_val(prologterm));
01703       XSB_StrAppendV(wcan_string,wcan_buff);
01704       break;
01705     case XSB_REF:
01706     case XSB_REF1: {
01707       int varval;
01708       XSB_StrAppendC(wcan_string,'_');
01709       if (prologterm >= (Cell)glstack.low && prologterm <= (Cell)top_of_heap) {
01710         XSB_StrAppendC(wcan_string,'h');
01711         varval = (long) ((prologterm-(Cell)glstack.low+1)/sizeof(CPtr));
01712       } else {
01713         if (prologterm >= (Cell)top_of_localstk && prologterm <= (Cell)glstack.high) {
01714           XSB_StrAppendC(wcan_string,'l');
01715           varval = (long) (((Cell)glstack.high-prologterm+1)/sizeof(CPtr));
01716         } else varval = prologterm;   /* Should never happen */
01717       }
01718       sprintf(wcan_buff->string,"%d",varval);
01719       XSB_StrAppendV(wcan_string,wcan_buff);
01720     }
01721       break;
01722     case XSB_STRUCT: /* lettervar: i.e., print '$VAR'(i) terms as Cap Alpha-Num */
01723      //first, check to see if we are dealing with boxed int or boxed float, and if so,
01724      // do it's respective case, but with boxed value, then break. Otherwise, 
01725      // do the struct case
01726      if (isboxedinteger(prologterm))
01727      {
01728       sprintf(wcan_buff->string,"%ld",(long)boxedint_val(prologterm));
01729       XSB_StrAppendV(wcan_string,wcan_buff);
01730           break;         
01731      }
01732      else if (isboxedfloat(prologterm))
01733      {
01734        //          sprintf(wcan_buff->string,"%2.4f",boxedfloat_val(prologterm));
01735           sprintf(wcan_buff->string,"%.15g",boxedfloat_val(prologterm));
01736           XSB_StrAppendV(wcan_string,wcan_buff);
01737           break;         
01738      }
01739         
01740       if (!dollar_var_psc) {
01741         int new_indicator;
01742         dollar_var_psc = pair_psc(insert("$VAR", 1, global_mod, &new_indicator));
01743       }
01744       if (letter_flag && (get_str_psc(prologterm) == dollar_var_psc)) {
01745         int ival, letter;
01746         Cell tempi = cell(clref_val(prologterm)+1);
01747         XSB_Deref(tempi);
01748         if (!isinteger(tempi)) xsb_abort("[write_canonical]: illegal $VAR argument");
01749         ival = int_val(tempi);
01750         letter = ival % 26;
01751         ival = ival / 26;
01752         XSB_StrAppendC(wcan_string,(char)(letter+'A'));
01753         if (ival != 0) {
01754           sprintf(wcan_buff->string,"%d",ival);
01755           XSB_StrAppendV(wcan_string,wcan_buff);
01756         }
01757       } else {
01758         int i; 
01759         char *fnname = get_name(get_str_psc(prologterm));
01760         if (quotes_are_needed(fnname)) {
01761           int len_needed = 2*strlen(fnname)+1;
01762           XSB_StrEnsureSize(wcan_atombuff,len_needed);
01763           double_quotes(fnname,wcan_atombuff->string);
01764           XSB_StrAppendC(wcan_string,'\'');
01765           XSB_StrAppend(wcan_string,wcan_atombuff->string);
01766           XSB_StrAppendC(wcan_string,'\'');
01767         } else XSB_StrAppend(wcan_string,fnname);
01768         XSB_StrAppendC(wcan_string,'(');
01769         for (i = 1; i < get_arity(get_str_psc(prologterm)); i++) {
01770           write_canonical_term_rec(CTXTc cell(clref_val(prologterm)+i),letter_flag);
01771           XSB_StrAppendC(wcan_string,',');
01772         }
01773         write_canonical_term_rec(CTXTc cell(clref_val(prologterm)+i),letter_flag);
01774         XSB_StrAppendC(wcan_string,')');
01775       }
01776       break;
01777     case XSB_LIST:
01778       {Cell tail;
01779       XSB_StrAppendC(wcan_string,'[');
01780       write_canonical_term_rec(CTXTc cell(clref_val(prologterm)),letter_flag);
01781       tail = cell(clref_val(prologterm)+1);
01782       XSB_Deref(tail);
01783       while (islist(tail)) {
01784         XSB_StrAppendC(wcan_string,',');
01785         write_canonical_term_rec(CTXTc cell(clref_val(tail)),letter_flag);
01786         tail = cell(clref_val(tail)+1);
01787         XSB_Deref(tail);
01788       } 
01789       if (!isnil(tail)) {
01790         XSB_StrAppendC(wcan_string,'|');
01791         write_canonical_term_rec(CTXTc tail,letter_flag);
01792       }
01793       XSB_StrAppendC(wcan_string,']');
01794       }
01795       break;
01796     default:
01797       xsb_abort("Unsupported subterm tag");
01798       return;
01799     }
01800   return;
01801 }
01802 
01803 DllExport void call_conv write_canonical_term(CTXTdeclc Cell prologterm, int letter_flag)
01804 {
01805   XSB_StrSet(wcan_string,"");
01806   XSB_StrEnsureSize(wcan_buff,40);
01807   write_canonical_term_rec(CTXTc prologterm, letter_flag);
01808 }
01809 
01810 void print_term_canonical(CTXTdeclc FILE *fptr, Cell prologterm, int letterflag)
01811 {
01812 
01813   write_canonical_term(CTXTc prologterm, letterflag);
01814   fprintf(fptr, "%s", wcan_string->string);
01815 }
01816 
01817 #undef wcan_string
01818 #undef wcan_atombuff
01819 #undef wcan_buff

Generated on Wed Jul 26 13:30:41 2006 for XSB by  doxygen 1.4.5