Testing latest pari + WASM + node.js... and it works?! Wow.
License: GPL3
ubuntu2004
/* Copyright (C) 2000 The PARI group.12This file is part of the PARI/GP package.34PARI/GP is free software; you can redistribute it and/or modify it under the5terms of the GNU General Public License as published by the Free Software6Foundation; either version 2 of the License, or (at your option) any later7version. It is distributed in the hope that it will be useful, but WITHOUT8ANY WARRANTY WHATSOEVER.910Check the License for details. You should have received a copy of it, along11with the package; see the file 'COPYING'. If not, write to the Free Software12Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */1314/*******************************************************************/15/** **/16/** INPUT/OUTPUT SUBROUTINES **/17/** **/18/*******************************************************************/19#ifdef _WIN3220#include "../systems/mingw/pwinver.h"21#include <windows.h>22#include <process.h> /* for getpid */23#include <fcntl.h>24#include <io.h> /* for setmode */25#include "../systems/mingw/mingw.h"26#endif27#include "paricfg.h"28#ifdef HAS_STAT29#include <sys/stat.h>30#endif31#ifdef HAS_OPENDIR32#include <dirent.h>33#endif3435#include "pari.h"36#include "paripriv.h"37#include "anal.h"38#ifdef __EMSCRIPTEN__39#include "../systems/emscripten/emscripten.h"40#endif4142#define DEBUGLEVEL DEBUGLEVEL_io4344typedef void (*OUT_FUN)(GEN, pariout_t *, pari_str *);4546static void bruti_sign(GEN g, pariout_t *T, pari_str *S, int addsign);47static void matbruti(GEN g, pariout_t *T, pari_str *S);48static void texi_sign(GEN g, pariout_t *T, pari_str *S, int addsign);4950static void bruti(GEN g, pariout_t *T, pari_str *S)51{ bruti_sign(g,T,S,1); }52static void texi(GEN g, pariout_t *T, pari_str *S)53{ texi_sign(g,T,S,1); }5455void56pari_ask_confirm(const char *s)57{58if (!cb_pari_ask_confirm)59pari_err(e_MISC,"Can't ask for confirmation. Please define cb_pari_ask_confirm()");60cb_pari_ask_confirm(s);61}6263static char *64strip_last_nl(char *s)65{66ulong l = strlen(s);67char *t;68if (l && s[l-1] != '\n') return s;69if (l>1 && s[l-2] == '\r') l--;70t = stack_malloc(l); memcpy(t, s, l-1); t[l-1] = 0;71return t;72}7374/********************************************************************/75/** **/76/** INPUT FILTER **/77/** **/78/********************************************************************/79#define ONE_LINE_COMMENT 280#define MULTI_LINE_COMMENT 181#define LBRACE '{'82#define RBRACE '}'8384static int85in_help(filtre_t *F)86{87char c;88if (!F->buf) return (*F->s == '?');89c = *F->buf->buf;90return c? (c == '?'): (*F->s == '?');91}92/* Filter F->s into F->t */93static char *94filtre0(filtre_t *F)95{96const char *s = F->s;97char c, *t = F->t;9899if (F->more_input == 1) F->more_input = 0;100while ((c = *s++))101{102if (F->in_string)103{104*t++ = c; /* copy verbatim */105switch(c)106{107case '\\': /* in strings, \ is the escape character */108if (*s) *t++ = *s++;109break;110111case '"': F->in_string = 0;112}113continue;114}115116if (F->in_comment)117{ /* look for comment's end */118if (F->in_comment == MULTI_LINE_COMMENT)119{120while (c != '*' || *s != '/')121{122if (!*s)123{124if (!F->more_input) F->more_input = 1;125goto END;126}127c = *s++;128}129s++;130}131else132while (c != '\n' && *s) c = *s++;133F->in_comment = 0;134continue;135}136137/* weed out comments and spaces */138if (c=='\\' && *s=='\\') { F->in_comment = ONE_LINE_COMMENT; continue; }139if (isspace((int)c)) continue;140*t++ = c;141switch(c)142{143case '/':144if (*s == '*') { t--; F->in_comment = MULTI_LINE_COMMENT; }145break;146147case '\\':148if (!*s) {149if (in_help(F)) break; /* '?...\' */150t--;151if (!F->more_input) F->more_input = 1;152goto END;153}154if (*s == '\r') s++; /* DOS */155if (*s == '\n') {156if (in_help(F)) break; /* '?...\' */157t--; s++;158if (!*s)159{160if (!F->more_input) F->more_input = 1;161goto END;162}163} /* skip \<CR> */164break;165166case '"': F->in_string = 1;167break;168169case LBRACE:170t--;171if (F->wait_for_brace) pari_err_IMPL("embedded braces (in parser)");172F->more_input = 2;173F->wait_for_brace = 1;174break;175176case RBRACE:177if (!F->wait_for_brace) pari_err(e_MISC,"unexpected closing brace");178F->more_input = 0; t--;179F->wait_for_brace = 0;180break;181}182}183184if (t != F->t) /* non empty input */185{186c = t[-1]; /* last char */187if (c == '=') { if (!in_help(F)) F->more_input = 2; }188else if (! F->wait_for_brace) F->more_input = 0;189else if (c == RBRACE) { F->more_input = 0; t--; F->wait_for_brace--;}190}191192END:193F->end = t; *t = 0; return F->t;194}195#undef ONE_LINE_COMMENT196#undef MULTI_LINE_COMMENT197198char *199gp_filter(const char *s)200{201filtre_t T;202T.buf = NULL;203T.s = s;204T.t = (char*)stack_malloc(strlen(s)+1);205T.in_string = 0; T.more_input = 0;206T.in_comment= 0; T.wait_for_brace = 0;207return filtre0(&T);208}209210void211init_filtre(filtre_t *F, Buffer *buf)212{213F->buf = buf;214F->in_string = 0;215F->in_comment = 0;216}217218/********************************************************************/219/** **/220/** INPUT METHODS **/221/** **/222/********************************************************************/223/* create */224Buffer *225new_buffer(void)226{227Buffer *b = (Buffer*) pari_malloc(sizeof(Buffer));228b->len = 1024;229b->buf = (char*)pari_malloc(b->len);230return b;231}232/* delete */233void234delete_buffer(Buffer *b)235{236if (!b) return;237pari_free((void*)b->buf); pari_free((void*)b);238}239/* resize */240void241fix_buffer(Buffer *b, long newlbuf)242{243b->len = newlbuf;244pari_realloc_ip((void**)&b->buf, b->len);245}246247static int248gp_read_stream_buf(FILE *fi, Buffer *b)249{250input_method IM;251filtre_t F;252253init_filtre(&F, b);254255IM.file = (void*)fi;256IM.myfgets = (fgets_t)&fgets;257IM.getline = &file_input;258IM.free = 0;259return input_loop(&F,&IM);260}261262GEN263gp_read_stream(FILE *fi)264{265Buffer *b = new_buffer();266GEN x = NULL;267while (gp_read_stream_buf(fi, b))268{269if (*(b->buf)) { x = readseq(b->buf); break; }270}271delete_buffer(b); return x;272}273274static GEN275gp_read_from_input(input_method* IM, int loop, char *last)276{277Buffer *b = new_buffer();278GEN x = gnil;279filtre_t F;280if (last) *last = 0;281do {282char *s;283init_filtre(&F, b);284if (!input_loop(&F, IM)) break;285s = b->buf;286if (s[0])287{288x = readseq(s);289if (last) *last = s[strlen(s) - 1];290}291} while (loop);292delete_buffer(b);293return x;294}295296GEN297gp_read_file(const char *s)298{299GEN x = gnil;300FILE *f = switchin(s);301if (file_is_binary(f))302{303x = readbin(s,f, NULL);304if (!x) pari_err_FILE("input file",s);305}306else {307pari_sp av = avma;308Buffer *b = new_buffer();309x = gnil;310for (;;) {311if (!gp_read_stream_buf(f, b)) break;312if (*(b->buf)) { set_avma(av); x = readseq(b->buf); }313}314delete_buffer(b);315}316popinfile(); return x;317}318319static char*320string_gets(char *s, int size, const char **ptr)321{322/* f is actually a const char** */323const char *in = *ptr;324int i;325char c;326327/* Copy from in to s */328for (i = 0; i+1 < size && in[i] != 0;)329{330s[i] = c = in[i]; i++;331if (c == '\n') break;332}333s[i] = 0; /* Terminating 0 byte */334if (i == 0) return NULL;335336*ptr += i;337return s;338}339340GEN341gp_read_str_multiline(const char *s, char *last)342{343input_method IM;344const char *ptr = s;345346IM.file = (void*)(&ptr);347IM.myfgets = (fgets_t)&string_gets;348IM.getline = &file_input;349IM.free = 0;350351return gp_read_from_input(&IM, 1, last);352}353354void355gp_embedded_init(long rsize, long vsize)356{357pari_init(rsize, 500000);358paristack_setsize(rsize, vsize);359}360361char *362gp_embedded(const char *s)363{364char last, *res;365struct gp_context state;366VOLATILE long t = 0, r = 0;367gp_context_save(&state);368timer_start(GP_DATA->T);369timer_start(GP_DATA->Tw);370pari_set_last_newline(1);371pari_CATCH(CATCH_ALL)372{373GENbin* err = copy_bin(pari_err_last());374gp_context_restore(&state);375res = pari_err2str(bin_copy(err));376} pari_TRY {377GEN z = gp_read_str_multiline(s, &last);378ulong n;379t = timer_delay(GP_DATA->T);380r = walltimer_delay(GP_DATA->Tw);381if (GP_DATA->simplify) z = simplify_shallow(z);382pari_add_hist(z, t, r);383n = pari_nb_hist();384set_avma(pari_mainstack->top);385parivstack_reset();386res = (z==gnil || last==';') ? stack_strdup("\n"):387stack_sprintf("%%%lu = %Ps\n", n, pari_get_hist(n));388if (t && GP_DATA->chrono)389res = stack_sprintf("%stime = %s.\n", res, gp_format_time(t));390} pari_ENDCATCH;391if (!pari_last_was_newline()) pari_putc('\n');392set_avma(pari_mainstack->top);393return res;394}395396GEN397gp_readvec_stream(FILE *fi)398{399pari_sp ltop = avma;400Buffer *b = new_buffer();401long i = 1, n = 16;402GEN z = cgetg(n+1,t_VEC);403for(;;)404{405if (!gp_read_stream_buf(fi, b)) break;406if (!*(b->buf)) continue;407if (i>n)408{409if (DEBUGLEVEL) err_printf("gp_readvec_stream: reaching %ld entries\n",n);410n <<= 1;411z = vec_lengthen(z,n);412}413gel(z,i++) = readseq(b->buf);414}415if (DEBUGLEVEL) err_printf("gp_readvec_stream: found %ld entries\n",i-1);416setlg(z,i); delete_buffer(b);417return gerepilecopy(ltop,z);418}419420GEN421gp_readvec_file(char *s)422{423GEN x = NULL;424FILE *f = switchin(s);425if (file_is_binary(f)) {426int junk;427x = readbin(s,f,&junk);428if (!x) pari_err_FILE("input file",s);429} else430x = gp_readvec_stream(f);431popinfile(); return x;432}433434char *435file_getline(Buffer *b, char **s0, input_method *IM)436{437const ulong MAX = (1UL << 31) - 1;438ulong used0, used;439440**s0 = 0; /* paranoia */441used0 = used = *s0 - b->buf;442for(;;)443{444ulong left = b->len - used, l, read;445char *s;446447/* If little space left, double the buffer size before next read. */448if (left < 512)449{450fix_buffer(b, b->len << 1);451left = b->len - used;452*s0 = b->buf + used0;453}454/* # of chars read by fgets is an int; be careful */455read = minuu(left, MAX);456s = b->buf + used;457if (! IM->myfgets(s, (int)read, IM->file)) return **s0? *s0: NULL; /* EOF */458459l = strlen(s);460if (l+1 < read || s[l-1] == '\n') return *s0; /* \n */461used += l;462}463}464465/* Read from file (up to '\n' or EOF) and copy at s0 (points in b->buf) */466char *467file_input(char **s0, int junk, input_method *IM, filtre_t *F)468{469(void)junk;470return file_getline(F->buf, s0, IM);471}472473/* Read a "complete line" and filter it. Return: 0 if EOF, 1 otherwise */474int475input_loop(filtre_t *F, input_method *IM)476{477Buffer *b = (Buffer*)F->buf;478char *to_read, *s = b->buf;479480/* read first line */481if (! (to_read = IM->getline(&s,1, IM, F)) )482{483if (F->in_string)484{485pari_warn(warner,"run-away string. Closing it");486F->in_string = 0;487}488if (F->in_comment)489{490pari_warn(warner,"run-away comment. Closing it");491F->in_comment = 0;492}493return 0;494}495496/* buffer is not empty, init filter */497F->in_string = 0;498F->more_input= 0;499F->wait_for_brace = 0;500for(;;)501{502if (GP_DATA->echo == 2) gp_echo_and_log("", strip_last_nl(to_read));503F->s = to_read;504F->t = s;505(void)filtre0(F); /* pre-processing of line, read by previous call to IM->getline */506if (IM->free) pari_free(to_read);507if (! F->more_input) break;508509/* read continuation line */510s = F->end;511to_read = IM->getline(&s,0, IM, F);512if (!to_read) break;513}514return 1;515}516517/********************************************************************/518/** **/519/** GENERAL PURPOSE PRINTING **/520/** **/521/********************************************************************/522PariOUT *pariOut, *pariErr;523static void524_fputs(const char *s, FILE *f ) {525#ifdef _WIN32526win32_ansi_fputs(s, f);527#else528fputs(s, f);529#endif530}531static void532_putc_log(char c) { if (pari_logfile) (void)putc(c, pari_logfile); }533static void534_puts_log(const char *s)535{536FILE *f = pari_logfile;537const char *p;538if (!f) return;539if (pari_logstyle != logstyle_color)540while ( (p = strchr(s, '\x1b')) )541{ /* skip ANSI color escape sequence */542if ( p!=s ) fwrite(s, 1, p-s, f);543s = strchr(p, 'm');544if (!s) return;545s++;546}547fputs(s, f);548}549static void550_flush_log(void)551{ if (pari_logfile != NULL) (void)fflush(pari_logfile); }552553static void554normalOutC(char c) { putc(c, pari_outfile); _putc_log(c); }555static void556normalOutS(const char *s) { _fputs(s, pari_outfile); _puts_log(s); }557static void558normalOutF(void) { fflush(pari_outfile); _flush_log(); }559static PariOUT defaultOut = {normalOutC, normalOutS, normalOutF};560561static void562normalErrC(char c) { putc(c, pari_errfile); _putc_log(c); }563static void564normalErrS(const char *s) { _fputs(s, pari_errfile); _puts_log(s); }565static void566normalErrF(void) { fflush(pari_errfile); _flush_log(); }567static PariOUT defaultErr = {normalErrC, normalErrS, normalErrF};568569/** GENERIC PRINTING **/570void571resetout(int initerr)572{573pariOut = &defaultOut;574if (initerr) pariErr = &defaultErr;575}576void577initout(int initerr)578{579pari_infile = stdin;580pari_outfile = stdout;581pari_errfile = stderr;582resetout(initerr);583}584585static int last_was_newline = 1;586587static void588set_last_newline(char c) { last_was_newline = (c == '\n'); }589590void591out_putc(PariOUT *out, char c) { set_last_newline(c); out->putch(c); }592void593pari_putc(char c) { out_putc(pariOut, c); }594595void596out_puts(PariOUT *out, const char *s) {597if (*s) { set_last_newline(s[strlen(s)-1]); out->puts(s); }598}599void600pari_puts(const char *s) { out_puts(pariOut, s); }601602int603pari_last_was_newline(void) { return last_was_newline; }604void605pari_set_last_newline(int last) { last_was_newline = last; }606607void608pari_flush(void) { pariOut->flush(); }609610void611err_flush(void) { pariErr->flush(); }612613static GEN614log10_2(void)615{ return divrr(mplog2(LOWDEFAULTPREC), mplog(utor(10,LOWDEFAULTPREC))); }616617/* e binary exponent, return exponent in base ten */618static long619ex10(long e) {620pari_sp av;621GEN z;622if (e >= 0) {623if (e < 1e15) return (long)(e*LOG10_2);624av = avma; z = mulur(e, log10_2());625z = floorr(z); e = itos(z);626}627else /* e < 0 */628{629if (e > -1e15) return (long)(-(-e*LOG10_2)-1);630av = avma; z = mulsr(e, log10_2());631z = floorr(z); e = itos(z) - 1;632}633set_avma(av); return e;634}635636static char *637zeros(char *b, long nb) { while (nb-- > 0) *b++ = '0'; *b = 0; return b; }638639/* # of decimal digits, assume l > 0 */640static long641numdig(ulong l)642{643if (l < 100000)644{645if (l < 100) return (l < 10)? 1: 2;646if (l < 10000) return (l < 1000)? 3: 4;647return 5;648}649if (l < 10000000) return (l < 1000000)? 6: 7;650if (l < 1000000000) return (l < 100000000)? 8: 9;651return 10;652}653654/* let ndig <= 9, x < 10^ndig, write in p[-ndig..-1] the decimal digits of x */655static void656utodec(char *p, ulong x, long ndig)657{658switch(ndig)659{660case 9: *--p = x % 10 + '0'; x = x/10;661case 8: *--p = x % 10 + '0'; x = x/10;662case 7: *--p = x % 10 + '0'; x = x/10;663case 6: *--p = x % 10 + '0'; x = x/10;664case 5: *--p = x % 10 + '0'; x = x/10;665case 4: *--p = x % 10 + '0'; x = x/10;666case 3: *--p = x % 10 + '0'; x = x/10;667case 2: *--p = x % 10 + '0'; x = x/10;668case 1: *--p = x % 10 + '0'; x = x/10;669}670}671672/* convert abs(x) != 0 to str. Prepend '-' if (sx < 0) */673static char *674itostr_sign(GEN x, int sx, long *len)675{676long l, d;677ulong *res = convi(x, &l);678/* l 9-digits words (< 10^9) + (optional) sign + \0 */679char *s = (char*)new_chunk(nchar2nlong(l*9 + 1 + 1)), *t = s;680681if (sx < 0) *t++ = '-';682d = numdig(*--res); t += d; utodec(t, *res, d);683while (--l > 0) { t += 9; utodec(t, *--res, 9); }684*t = 0; *len = t - s; return s;685}686687/********************************************************************/688/** **/689/** WRITE A REAL NUMBER **/690/** **/691/********************************************************************/692/* 19 digits (if 64 bits, at most 2^60-1) + 1 sign */693static const long MAX_EXPO_LEN = 20;694695/* write z to buf, inserting '.' at 'point', 0 < point < strlen(z) */696static void697wr_dec(char *buf, char *z, long point)698{699char *s = buf + point;700strncpy(buf, z, point); /* integer part */701*s++ = '.'; z += point;702while ( (*s++ = *z++) ) /* empty */;703}704705static char *706zerotostr(void)707{708char *s = (char*)new_chunk(1);709s[0] = '0';710s[1] = 0; return s;711}712713/* write a real 0 of exponent ex in format f */714static char *715real0tostr_width_frac(long width_frac)716{717char *buf, *s;718if (width_frac == 0) return zerotostr();719buf = s = stack_malloc(width_frac + 3);720*s++ = '0';721*s++ = '.';722(void)zeros(s, width_frac);723return buf;724}725726/* write a real 0 of exponent ex */727static char *728real0tostr(long ex, char format, char exp_char, long wanted_dec)729{730char *buf, *buf0;731732if (format == 'f') {733long width_frac = wanted_dec;734if (width_frac < 0) width_frac = (ex >= 0)? 0: (long)(-ex * LOG10_2);735return real0tostr_width_frac(width_frac);736} else {737buf0 = buf = stack_malloc(3 + MAX_EXPO_LEN + 1);738*buf++ = '0';739*buf++ = '.';740*buf++ = exp_char;741sprintf(buf, "%ld", ex10(ex) + 1);742}743return buf0;744}745746/* format f, width_frac >= 0: number of digits in fractional part, */747static char *748absrtostr_width_frac(GEN x, int width_frac)749{750long beta, ls, point, lx, sx = signe(x);751char *s, *buf;752GEN z;753754if (!sx) return real0tostr_width_frac(width_frac);755756/* x != 0 */757lx = realprec(x);758beta = width_frac;759if (beta) /* >= 0 */760{ /* z = |x| 10^beta, 10^b = 5^b * 2^b, 2^b goes into exponent */761if (beta > 4e9) lx++;762z = mulrr(x, rpowuu(5UL, (ulong)beta, lx+1));763setsigne(z, 1);764shiftr_inplace(z, beta);765}766else767z = mpabs(x);768z = roundr_safe(z);769if (!signe(z)) return real0tostr_width_frac(width_frac);770771s = itostr_sign(z, 1, &ls); /* ls > 0, number of digits in s */772point = ls - beta; /* position of . in s; <= ls, may be < 0 */773if (point > 0) /* write integer_part.fractional_part */774{775/* '.', trailing \0 */776buf = stack_malloc( ls + 1+1 );777if (ls == point)778strcpy(buf, s); /* no '.' */779else780wr_dec(buf, s, point);781} else { /* point <= 0, fractional part must be written */782char *t;783/* '0', '.', zeroes, trailing \0 */784buf = t = stack_malloc( 1 + 1 - point + ls + 1 );785*t++ = '0';786*t++ = '.';787t = zeros(t, -point);788strcpy(t, s);789}790return buf;791}792793/* Return t_REAL |x| in floating point format.794* Allocate freely, the caller must clean the stack.795* FORMAT: E/e (exponential), F/f (floating point), G/g796* wanted_dec: number of significant digits to print (all if < 0).797*/798static char *799absrtostr(GEN x, int sp, char FORMAT, long wanted_dec)800{801const char format = (char)tolower((int)FORMAT), exp_char = (format == FORMAT)? 'e': 'E';802long beta, ls, point, lx, sx = signe(x), ex = expo(x);803char *s, *buf, *buf0;804GEN z;805806if (!sx) return real0tostr(ex, format, exp_char, wanted_dec);807808/* x != 0 */809lx = realprec(x);810if (wanted_dec >= 0)811{ /* reduce precision if possible */812long w = ndec2prec(wanted_dec); /* digits -> pari precision in words */813if (lx > w) lx = w; /* truncature with guard, no rounding */814}815beta = ex10(prec2nbits(lx) - ex);816if (beta)817{ /* z = |x| 10^beta, 10^b = 5^b * 2^b, 2^b goes into exponent */818if (beta > 0)819{820if (beta > 18) { lx++; x = rtor(x, lx); }821z = mulrr(x, rpowuu(5UL, (ulong)beta, lx+1));822}823else824{825if (beta < -18) { lx++; x = rtor(x, lx); }826z = divrr(x, rpowuu(5UL, (ulong)-beta, lx+1));827}828setsigne(z, 1);829shiftr_inplace(z, beta);830}831else832z = x;833z = roundr_safe(z);834if (!signe(z)) return real0tostr(ex, format, exp_char, wanted_dec);835836s = itostr_sign(z, 1, &ls); /* ls > 0, number of digits in s */837if (wanted_dec < 0)838wanted_dec = ls;839else if (ls > wanted_dec)840{841beta -= ls - wanted_dec;842ls = wanted_dec;843if (s[ls] >= '5') /* round up */844{845long i;846for (i = ls-1; i >= 0; s[i--] = '0')847if (++s[i] <= '9') break;848if (i < 0) { s[0] = '1'; beta--; }849}850s[ls] = 0;851}852853/* '.', " E", exponent, trailing \0 */854point = ls - beta; /* position of . in s; < 0 or > 0 */855if (beta <= 0 || format == 'e' || (format == 'g' && point-1 < -4))856{ /* e format */857buf0 = buf = stack_malloc(ls+1+2+MAX_EXPO_LEN + 1);858wr_dec(buf, s, 1); buf += ls + 1;859if (sp) *buf++ = ' ';860*buf++ = exp_char;861sprintf(buf, "%ld", point-1);862}863else if (point > 0) /* f format, write integer_part.fractional_part */864{865buf0 = buf = stack_malloc(ls+1 + 1);866wr_dec(buf, s, point); /* point < ls since beta > 0 */867}868else /* f format, point <= 0, write fractional part */869{870buf0 = buf = stack_malloc(2-point+ls + 1);871*buf++ = '0';872*buf++ = '.';873buf = zeros(buf, -point);874strcpy(buf, s);875}876return buf0;877}878879/* vsnprintf implementation rewritten from snprintf.c to be found at880*881* http://www.nersc.gov/~scottc/misc/docs/snort-2.1.1-RC1/snprintf_8c-source.html882* The original code was883* Copyright (C) 1998-2002 Martin Roesch <[email protected]>884* available under the terms of the GNU GPL version 2 or later. It885* was itself adapted from an original version by Patrick Powell. */886887/* Modifications for format %Ps: R.Butel IMB/CNRS 2007/12/03 */888889/* l = old len, L = new len */890static void891str_alloc0(pari_str *S, long l, long L)892{893if (S->use_stack)894S->string = (char*) memcpy(stack_malloc(L), S->string, l);895else896pari_realloc_ip((void**)&S->string, L);897S->cur = S->string + l;898S->end = S->string + L;899S->size = L;900}901/* make sure S is large enough to write l further words (<= l * 20 chars).902* To avoid automatic extension in between av = avma / set_avma(av) pairs903* [ would destroy S->string if (S->use_stack) ] */904static void905str_alloc(pari_str *S, long l)906{907l *= 20;908if (S->end - S->cur <= l)909str_alloc0(S, S->cur - S->string, S->size + maxss(S->size, l));910}911void912str_putc(pari_str *S, char c)913{914*S->cur++ = c;915if (S->cur == S->end) str_alloc0(S, S->size, S->size << 1);916}917918void919str_init(pari_str *S, int use_stack)920{921char *s;922S->size = 1024;923S->use_stack = use_stack;924if (S->use_stack)925s = (char*)stack_malloc(S->size);926else927s = (char*)pari_malloc(S->size);928*s = 0;929S->string = S->cur = s;930S->end = S->string + S->size;931}932void933str_puts(pari_str *S, const char *s) { while (*s) str_putc(S, *s++); }934935static void936str_putscut(pari_str *S, const char *s, int cut)937{938if (cut < 0) str_puts(S, s);939else {940while (*s && cut-- > 0) str_putc(S, *s++);941}942}943944/* lbuf = strlen(buf), len < 0: unset */945static void946outpad(pari_str *S, const char *buf, long lbuf, int sign, long ljust, long len, long zpad)947{948long padlen = len - lbuf;949if (padlen < 0) padlen = 0;950if (ljust) padlen = -padlen;951if (padlen > 0)952{953if (zpad) {954if (sign) { str_putc(S, sign); --padlen; }955while (padlen > 0) { str_putc(S, '0'); --padlen; }956}957else958{959if (sign) --padlen;960while (padlen > 0) { str_putc(S, ' '); --padlen; }961if (sign) str_putc(S, sign);962}963} else964if (sign) str_putc(S, sign);965str_puts(S, buf);966while (padlen < 0) { str_putc(S, ' '); ++padlen; }967}968969/* len < 0 or maxwidth < 0: unset */970static void971fmtstr(pari_str *S, const char *buf, int ljust, int len, int maxwidth)972{973int padlen, lbuf = strlen(buf);974975if (maxwidth >= 0 && lbuf > maxwidth) lbuf = maxwidth;976977padlen = len - lbuf;978if (padlen < 0) padlen = 0;979if (ljust) padlen = -padlen;980while (padlen > 0) { str_putc(S, ' '); --padlen; }981str_putscut(S, buf, maxwidth);982while (padlen < 0) { str_putc(S, ' '); ++padlen; }983}984985/* abs(base) is 8, 10, 16. If base < 0, some "alternate" form986* -- print hex in uppercase987* -- prefix octal with 0988* signvalue = -1: unsigned, otherwise ' ' or '+'. Leaves a messy stack if989* S->use_stack */990static void991fmtnum(pari_str *S, long lvalue, GEN gvalue, int base, int signvalue,992int ljust, int len, int zpad)993{994int caps;995char *buf0, *buf;996long lbuf, mxl;997GEN uvalue = NULL;998ulong ulvalue = 0;999pari_sp av = avma;10001001if (gvalue)1002{1003long s, l;1004if (typ(gvalue) != t_INT) {1005long i, j, h;1006l = lg(gvalue);1007switch(typ(gvalue))1008{1009case t_VEC:1010str_putc(S, '[');1011for (i = 1; i < l; i++)1012{1013fmtnum(S, 0, gel(gvalue,i), base, signvalue, ljust,len,zpad);1014if (i < l-1) str_putc(S, ',');1015}1016str_putc(S, ']');1017return;1018case t_COL:1019str_putc(S, '[');1020for (i = 1; i < l; i++)1021{1022fmtnum(S, 0, gel(gvalue,i), base, signvalue, ljust,len,zpad);1023if (i < l-1) str_putc(S, ',');1024}1025str_putc(S, ']');1026str_putc(S, '~');1027return;1028case t_MAT:1029if (l == 1)1030str_puts(S, "[;]");1031else1032{1033h = lgcols(gvalue);1034for (i=1; i<h; i++)1035{1036str_putc(S, '[');1037for (j=1; j<l; j++)1038{1039fmtnum(S, 0, gcoeff(gvalue,i,j), base, signvalue, ljust,len,zpad);1040if (j<l-1) str_putc(S, ' ');1041}1042str_putc(S, ']');1043str_putc(S, '\n');1044if (i<h-1) str_putc(S, '\n');1045}1046}1047return;1048}1049gvalue = gfloor( simplify_shallow(gvalue) );1050if (typ(gvalue) != t_INT)1051pari_err(e_MISC,"not a t_INT in integer format conversion: %Ps", gvalue);1052}1053s = signe(gvalue);1054if (!s) { lbuf = 1; buf = zerotostr(); signvalue = 0; goto END; }10551056l = lgefint(gvalue);1057uvalue = gvalue;1058if (signvalue < 0)1059{1060if (s < 0) uvalue = addii(int2n(bit_accuracy(l)), gvalue);1061signvalue = 0;1062}1063else1064{1065if (s < 0) { signvalue = '-'; uvalue = absi(uvalue); }1066}1067mxl = (l-2)* 22 + 1; /* octal at worst; 22 octal chars per 64bit word */1068} else {1069ulvalue = lvalue;1070if (signvalue < 0)1071signvalue = 0;1072else1073if (lvalue < 0) { signvalue = '-'; ulvalue = - lvalue; }1074mxl = 22 + 1; /* octal at worst; 22 octal chars to write down 2^64 - 1 */1075}1076if (base > 0) caps = 0; else { caps = 1; base = -base; }10771078buf0 = buf = stack_malloc(mxl) + mxl; /* fill from the right */1079*--buf = 0; /* trailing \0 */1080if (gvalue) {1081if (base == 10) {1082long i, l, cnt;1083ulong *larray = convi(uvalue, &l);1084larray -= l;1085for (i = 0; i < l; i++) {1086cnt = 0;1087ulvalue = larray[i];1088do {1089*--buf = '0' + ulvalue%10;1090ulvalue = ulvalue / 10;1091cnt++;1092} while (ulvalue);1093if (i + 1 < l)1094for (;cnt<9;cnt++) *--buf = '0';1095}1096} else if (base == 16) {1097long i, l = lgefint(uvalue);1098GEN up = int_LSW(uvalue);1099for (i = 2; i < l; i++, up = int_nextW(up)) {1100ulong ucp = (ulong)*up;1101long j;1102for (j=0; j < BITS_IN_LONG/4; j++) {1103unsigned char cv = ucp & 0xF;1104*--buf = (caps? "0123456789ABCDEF":"0123456789abcdef")[cv];1105ucp >>= 4;1106if (ucp == 0 && i+1 == l) break;1107}1108} /* loop on hex digits in word */1109} else if (base == 8) {1110long i, l = lgefint(uvalue);1111GEN up = int_LSW(uvalue);1112ulong rem = 0;1113int shift = 0;1114int mask[3] = {0, 1, 3};1115for (i = 2; i < l; i++, up = int_nextW(up)) {1116ulong ucp = (ulong)*up;1117long j, ldispo = BITS_IN_LONG;1118if (shift) { /* 0, 1 or 2 */1119unsigned char cv = ((ucp & mask[shift]) <<(3-shift)) + rem;1120*--buf = "01234567"[cv];1121ucp >>= shift;1122ldispo -= shift;1123};1124shift = (shift + 3 - BITS_IN_LONG % 3) % 3;1125for (j=0; j < BITS_IN_LONG/3; j++) {1126unsigned char cv = ucp & 0x7;1127if (ucp == 0 && i+1 == l) { rem = 0; break; };1128*--buf = "01234567"[cv];1129ucp >>= 3;1130ldispo -= 3;1131rem = ucp;1132if (ldispo < 3) break;1133}1134} /* loop on hex digits in word */1135if (rem) *--buf = "01234567"[rem];1136}1137} else { /* not a gvalue, thus a standard integer */1138do {1139*--buf = (caps? "0123456789ABCDEF":"0123456789abcdef")[ulvalue % (unsigned)base ];1140ulvalue /= (unsigned)base;1141} while (ulvalue);1142}1143/* leading 0 if octal and alternate # form */1144if (caps && base == 8) *--buf = '0';1145lbuf = (buf0 - buf) - 1;1146END:1147outpad(S, buf, lbuf, signvalue, ljust, len, zpad);1148if (!S->use_stack) set_avma(av);1149}11501151static GEN1152v_get_arg(pari_str *S, GEN arg_vector, int *index, const char *save_fmt)1153{1154if (*index >= lg(arg_vector))1155{1156if (!S->use_stack) pari_free(S->string);1157pari_err(e_MISC, "missing arg %d for printf format '%s'", *index, save_fmt); }1158return gel(arg_vector, (*index)++);1159}11601161static int1162dosign(int blank, int plus)1163{1164if (plus) return('+');1165if (blank) return(' ');1166return 0;1167}11681169/* x * 10 + 'digit whose char value is ch'. Do not check for overflow */1170static int1171shift_add(int x, int ch)1172{1173if (x < 0) /* was unset */1174x = ch - '0';1175else1176x = x*10 + ch - '0';1177return x;1178}11791180static long1181get_sigd(GEN gvalue, char ch, int maxwidth)1182{1183long e;1184if (maxwidth < 0) return nbits2ndec(precreal);1185switch(ch)1186{1187case 'E': case 'e': return maxwidth+1;1188case 'F': case 'f':1189e = gexpo(gvalue);1190return (e == -(long)HIGHEXPOBIT)? 0: ex10(e) + 1 + maxwidth;1191}1192return maxwidth? maxwidth: 1; /* 'g', 'G' */1193}11941195static void1196fmtreal(pari_str *S, GEN gvalue, int space, int signvalue, int FORMAT,1197int maxwidth, int ljust, int len, int zpad)1198{1199pari_sp av = avma;1200long sigd;1201char *buf;12021203if (typ(gvalue) == t_REAL)1204sigd = get_sigd(gvalue, FORMAT, maxwidth);1205else1206{1207long i, j, h, l = lg(gvalue);1208switch(typ(gvalue))1209{1210case t_VEC:1211str_putc(S, '[');1212for (i = 1; i < l; i++)1213{1214fmtreal(S, gel(gvalue,i), space, signvalue, FORMAT, maxwidth,1215ljust,len,zpad);1216if (i < l-1) str_putc(S, ',');1217}1218str_putc(S, ']');1219return;1220case t_COL:1221str_putc(S, '[');1222for (i = 1; i < l; i++)1223{1224fmtreal(S, gel(gvalue,i), space, signvalue, FORMAT, maxwidth,1225ljust,len,zpad);1226if (i < l-1) str_putc(S, ',');1227}1228str_putc(S, ']');1229str_putc(S, '~');1230return;1231case t_MAT:1232if (l == 1)1233str_puts(S, "[;]");1234else1235{1236h = lgcols(gvalue);1237for (j=1; j<h; j++)1238{1239str_putc(S, '[');1240for (i=1; i<l; i++)1241{1242fmtreal(S, gcoeff(gvalue,j,i), space, signvalue, FORMAT, maxwidth,1243ljust,len,zpad);1244if (i<l-1) str_putc(S, ' ');1245}1246str_putc(S, ']');1247str_putc(S, '\n');1248if (j<h-1) str_putc(S, '\n');1249}1250}1251return;1252}1253sigd = get_sigd(gvalue, FORMAT, maxwidth);1254gvalue = gtofp(gvalue, maxss(ndec2prec(sigd), LOWDEFAULTPREC));1255if (typ(gvalue) != t_REAL)1256{1257if (!S->use_stack) free(S->string);1258pari_err(e_MISC,"impossible conversion to t_REAL: %Ps",gvalue);1259}1260}1261if ((FORMAT == 'f' || FORMAT == 'F') && maxwidth >= 0)1262buf = absrtostr_width_frac(gvalue, maxwidth);1263else1264buf = absrtostr(gvalue, space, FORMAT, sigd);1265if (signe(gvalue) < 0) signvalue = '-';1266outpad(S, buf, strlen(buf), signvalue, ljust, len, zpad);1267if (!S->use_stack) set_avma(av);1268}1269static long1270gtolong_OK(GEN x)1271{1272switch(typ(x))1273{1274case t_INT: case t_REAL: case t_FRAC: return 1;1275case t_COMPLEX: return gequal0(gel(x,2)) && gtolong_OK(gel(x,1));1276case t_QUAD: return gequal0(gel(x,3)) && gtolong_OK(gel(x,2));1277}1278return 0;1279}1280/* Format handling "inspired" by the standard draft at1281-- http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf pages 274ff1282* fmt is a standard printf format, except 'P' is a "length modifier"1283* allowing GEN arguments. Use either the arg_vector or (if NULL) the va_list.1284* Appent output to the pari_str S, which must be initialized; clean if1285* !S->use_stack, else leaves objects of stack. */1286static void1287str_arg_vprintf(pari_str *S, const char *fmt, GEN arg_vector, va_list args)1288{1289int GENflag = 0, longflag = 0, pointflag = 0;1290int print_plus, print_blank, with_sharp, ch, ljust, len, maxwidth, zpad;1291long lvalue;1292int index = 1;1293GEN gvalue;1294const char *save_fmt = fmt;12951296while ((ch = *fmt++) != '\0') {1297switch(ch) {1298case '%':1299ljust = zpad = 0;1300len = maxwidth = -1;1301GENflag = longflag = pointflag = 0;1302print_plus = print_blank = with_sharp = 0;1303nextch:1304ch = *fmt++;1305switch(ch) {1306case 0:1307pari_err(e_MISC, "printf: end of format");1308/*------------------------------------------------------------------------1309-- flags1310------------------------------------------------------------------------*/1311case '-':1312ljust = 1;1313goto nextch;1314case '+':1315print_plus = 1;1316goto nextch;1317case '#':1318with_sharp = 1;1319goto nextch;1320case ' ':1321print_blank = 1;1322goto nextch;1323case '0':1324/* appears as a flag: set zero padding */1325if (len < 0 && !pointflag) { zpad = '0'; goto nextch; }13261327/* else part of a field width or precision */1328/* fall through */1329/*------------------------------------------------------------------------1330-- maxwidth or precision1331------------------------------------------------------------------------*/1332case '1':1333case '2':1334case '3':1335case '4':1336case '5':1337case '6':1338case '7':1339case '8':1340case '9':1341if (pointflag)1342maxwidth = shift_add(maxwidth, ch);1343else1344len = shift_add(len, ch);1345goto nextch;13461347case '*':1348{1349int *t = pointflag? &maxwidth: &len;1350if (arg_vector)1351{1352gvalue = v_get_arg(S, arg_vector, &index, save_fmt);1353if (!gtolong_OK(gvalue) && !S->use_stack) pari_free(S->string);1354*t = (int)gtolong(gvalue);1355}1356else1357*t = va_arg(args, int);1358goto nextch;1359}1360case '.':1361if (pointflag)1362pari_err(e_MISC, "two '.' in conversion specification");1363pointflag = 1;1364goto nextch;1365/*------------------------------------------------------------------------1366-- length modifiers1367------------------------------------------------------------------------*/1368case 'l':1369if (GENflag)1370pari_err(e_MISC, "P/l length modifiers in the same conversion");1371#if !defined(_WIN64)1372if (longflag)1373pari_err_IMPL( "ll length modifier in printf");1374#endif1375longflag = 1;1376goto nextch;1377case 'P':1378if (longflag)1379pari_err(e_MISC, "P/l length modifiers in the same conversion");1380if (GENflag)1381pari_err(e_MISC, "'P' length modifier appears twice");1382GENflag = 1;1383goto nextch;1384case 'h': /* dummy: va_arg promotes short into int */1385goto nextch;1386/*------------------------------------------------------------------------1387-- conversions1388------------------------------------------------------------------------*/1389case 'u': /* not a signed conversion: print_(blank|plus) ignored */1390#define get_num_arg() \1391if (arg_vector) { \1392lvalue = 0; \1393gvalue = v_get_arg(S, arg_vector, &index, save_fmt); \1394} else { \1395if (GENflag) { \1396lvalue = 0; \1397gvalue = va_arg(args, GEN); \1398} else { \1399lvalue = longflag? va_arg(args, long): va_arg(args, int); \1400gvalue = NULL; \1401} \1402}1403get_num_arg();1404fmtnum(S, lvalue, gvalue, 10, -1, ljust, len, zpad);1405break;1406case 'o': /* not a signed conversion: print_(blank|plus) ignored */1407get_num_arg();1408fmtnum(S, lvalue, gvalue, with_sharp? -8: 8, -1, ljust, len, zpad);1409break;1410case 'd':1411case 'i':1412get_num_arg();1413fmtnum(S, lvalue, gvalue, 10,1414dosign(print_blank, print_plus), ljust, len, zpad);1415break;1416case 'p':1417str_putc(S, '0'); str_putc(S, 'x');1418if (arg_vector)1419lvalue = (long)v_get_arg(S, arg_vector, &index, save_fmt);1420else1421lvalue = (long)va_arg(args, void*);1422fmtnum(S, lvalue, NULL, 16, -1, ljust, len, zpad);1423break;1424case 'x': /* not a signed conversion: print_(blank|plus) ignored */1425if (with_sharp) { str_putc(S, '0'); str_putc(S, 'x'); }1426get_num_arg();1427fmtnum(S, lvalue, gvalue, 16, -1, ljust, len, zpad);1428break;1429case 'X': /* not a signed conversion: print_(blank|plus) ignored */1430if (with_sharp) { str_putc(S, '0'); str_putc(S, 'X'); }1431get_num_arg();1432fmtnum(S, lvalue, gvalue,-16, -1, ljust, len, zpad);1433break;1434case 's':1435{1436char *strvalue;1437pari_sp av = avma;14381439if (arg_vector) {1440gvalue = v_get_arg(S, arg_vector, &index, save_fmt);1441strvalue = NULL;1442} else {1443if (GENflag) {1444gvalue = va_arg(args, GEN);1445strvalue = NULL;1446} else {1447gvalue = NULL;1448strvalue = va_arg(args, char *);1449}1450}1451if (gvalue) strvalue = GENtostr_unquoted(gvalue);1452fmtstr(S, strvalue, ljust, len, maxwidth);1453if (!S->use_stack) set_avma(av);1454break;1455}1456case 'c':1457gvalue = NULL;1458if (arg_vector)1459gvalue = v_get_arg(S, arg_vector, &index, save_fmt);1460else if (GENflag)1461gvalue = va_arg(args,GEN);1462else1463{1464ch = va_arg(args, int);1465str_putc(S, ch); break;1466}1467if (!gtolong_OK(gvalue) && !S->use_stack) free(S->string);1468str_putc(S, (int)gtolong(gvalue));1469break;14701471case '%':1472str_putc(S, ch);1473continue;1474case 'g':1475case 'G':1476case 'e':1477case 'E':1478case 'f':1479case 'F':1480{1481pari_sp av = avma;1482if (arg_vector)1483gvalue = simplify_shallow(v_get_arg(S, arg_vector, &index, save_fmt));1484else {1485if (GENflag)1486gvalue = simplify_shallow( va_arg(args, GEN) );1487else1488gvalue = dbltor( va_arg(args, double) );1489}1490fmtreal(S, gvalue, GP_DATA->fmt->sp, dosign(print_blank,print_plus),1491ch, maxwidth, ljust, len, zpad);1492if (!S->use_stack) set_avma(av);1493break;1494}1495default:1496if (!S->use_stack) free(S->string);1497pari_err(e_MISC, "invalid conversion or specification %c in format `%s'", ch, save_fmt);1498} /* second switch on ch */1499break;1500default:1501str_putc(S, ch);1502break;1503} /* first switch on ch */1504} /* while loop on ch */1505*S->cur = 0;1506}15071508void1509decode_color(long n, long *c)1510{1511c[1] = n & 0xf; n >>= 4; /* foreground */1512c[2] = n & 0xf; n >>= 4; /* background */1513c[0] = n & 0xf; /* attribute */1514}15151516#define COLOR_LEN 161517/* start printing in "color" c */1518/* terminal has to support ANSI color escape sequences */1519void1520out_term_color(PariOUT *out, long c)1521{1522static char s[COLOR_LEN];1523out->puts(term_get_color(s, c));1524}1525void1526term_color(long c) { out_term_color(pariOut, c); }15271528/* s must be able to store 12 chars (including final \0) */1529char *1530term_get_color(char *s, long n)1531{1532long c[3], a;1533if (!s) s = stack_malloc(COLOR_LEN);15341535if (disable_color) { *s = 0; return s; }1536if (n == c_NONE || (a = gp_colors[n]) == c_NONE)1537strcpy(s, "\x1b[0m"); /* reset */1538else1539{1540decode_color(a,c);1541if (c[1]<8) c[1] += 30; else c[1] += 82;1542if (a & (1L<<12)) /* transparent background */1543sprintf(s, "\x1b[%ld;%ldm", c[0], c[1]);1544else1545{1546if (c[2]<8) c[2] += 40; else c[2] += 92;1547sprintf(s, "\x1b[%ld;%ld;%ldm", c[0], c[1], c[2]);1548}1549}1550return s;1551}15521553static long1554strlen_real(const char *s)1555{1556const char *t = s;1557long len = 0;1558while (*t)1559{1560if (t[0] == '\x1b' && t[1] == '[')1561{ /* skip ANSI escape sequence */1562t += 2;1563while (*t && *t++ != 'm') /* empty */;1564continue;1565}1566t++; len++;1567}1568return len;1569}15701571#undef COLOR_LEN15721573/********************************************************************/1574/** **/1575/** PRINTING BASED ON SCREEN WIDTH **/1576/** **/1577/********************************************************************/1578#undef larg /* problems with SCO Unix headers (ioctl_arg) */1579#ifdef HAS_TIOCGWINSZ1580# ifdef __sun1581# include <sys/termios.h>1582# endif1583# include <sys/ioctl.h>1584#endif15851586static int1587term_width_intern(void)1588{1589#ifdef _WIN321590return win32_terminal_width();1591#endif1592#ifdef HAS_TIOCGWINSZ1593{1594struct winsize s;1595if (!(GP_DATA->flags & (gpd_EMACS|gpd_TEXMACS))1596&& !ioctl(0, TIOCGWINSZ, &s)) return s.ws_col;1597}1598#endif1599{1600char *str;1601if ((str = os_getenv("COLUMNS"))) return atoi(str);1602}1603#ifdef __EMX__1604{1605int scrsize[2];1606_scrsize(scrsize); return scrsize[0];1607}1608#endif1609return 0;1610}16111612static int1613term_height_intern(void)1614{1615#ifdef _WIN321616return win32_terminal_height();1617#endif1618#ifdef HAS_TIOCGWINSZ1619{1620struct winsize s;1621if (!(GP_DATA->flags & (gpd_EMACS|gpd_TEXMACS))1622&& !ioctl(0, TIOCGWINSZ, &s)) return s.ws_row;1623}1624#endif1625{1626char *str;1627if ((str = os_getenv("LINES"))) return atoi(str);1628}1629#ifdef __EMX__1630{1631int scrsize[2];1632_scrsize(scrsize); return scrsize[1];1633}1634#endif1635return 0;1636}16371638#define DFT_TERM_WIDTH 801639#define DFT_TERM_HEIGHT 2016401641int1642term_width(void)1643{1644int n = term_width_intern();1645return (n>1)? n: DFT_TERM_WIDTH;1646}16471648int1649term_height(void)1650{1651int n = term_height_intern();1652return (n>1)? n: DFT_TERM_HEIGHT;1653}16541655static ulong col_index;16561657/* output string wrapped after MAX_WIDTH characters (for gp -test) */1658static void1659putc_lw(char c)1660{1661if (c == '\n') col_index = 0;1662else if (col_index >= GP_DATA->linewrap) { normalOutC('\n'); col_index = 1; }1663else col_index++;1664normalOutC(c);1665}1666static void1667puts_lw(const char *s) { while (*s) putc_lw(*s++); }16681669static PariOUT pariOut_lw= {putc_lw, puts_lw, normalOutF};16701671void1672init_linewrap(long w) { col_index=0; GP_DATA->linewrap=w; pariOut=&pariOut_lw; }16731674/* output stopped after max_line have been printed, for default(lines,).1675* n = length of prefix already printed (print up to max_lin lines) */1676void1677lim_lines_output(char *s, long n, long max_lin)1678{1679long lin, col, width;1680char c;1681if (!*s) return;1682width = term_width();1683lin = 1;1684col = n;16851686if (lin > max_lin) return;1687while ( (c = *s++) )1688{1689if (lin >= max_lin)1690if (c == '\n' || col >= width-5)1691{1692pari_sp av = avma;1693pari_puts(term_get_color(NULL, c_ERR)); set_avma(av);1694pari_puts("[+++]"); return;1695}1696if (c == '\n') { col = -1; lin++; }1697else if (col == width) { col = 0; lin++; }1698set_last_newline(c);1699col++; pari_putc(c);1700}1701}17021703static void1704new_line(PariOUT *out, const char *prefix)1705{1706out_putc(out, '\n'); if (prefix) out_puts(out, prefix);1707}17081709#define is_blank(c) ((c) == ' ' || (c) == '\n' || (c) == '\t')1710/* output: <prefix>< s wrapped at EOL >1711* <prefix>< ... > <str>1712* ^--- (no \n at the end)1713* If str is NULL, omit the arrow, end the text with '\n'.1714* If prefix is NULL, use "" */1715void1716print_prefixed_text(PariOUT *out, const char *s, const char *prefix,1717const char *str)1718{1719const long prelen = prefix? strlen_real(prefix): 0;1720const long W = term_width(), ls = strlen(s);1721long linelen = prelen;1722char *word = (char*)pari_malloc(ls + 3);17231724if (prefix) out_puts(out, prefix);1725for(;;)1726{1727long len;1728int blank = 0;1729char *u = word;1730while (*s && !is_blank(*s)) *u++ = *s++;1731*u = 0; /* finish "word" */1732len = strlen_real(word);1733linelen += len;1734if (linelen >= W) { new_line(out, prefix); linelen = prelen + len; }1735out_puts(out, word);1736while (is_blank(*s)) {1737switch (*s) {1738case ' ': break;1739case '\t':1740linelen = (linelen & ~7UL) + 8; out_putc(out, '\t');1741blank = 1; break;1742case '\n':1743linelen = W;1744blank = 1; break;1745}1746if (linelen >= W) { new_line(out, prefix); linelen = prelen; }1747s++;1748}1749if (!*s) break;1750if (!blank) { out_putc(out, ' '); linelen++; }1751}1752if (!str)1753out_putc(out, '\n');1754else1755{1756long i,len = strlen_real(str);1757int space = (*str == ' ' && str[1]);1758if (linelen + len >= W)1759{1760new_line(out, prefix); linelen = prelen;1761if (space) { str++; len--; space = 0; }1762}1763out_term_color(out, c_OUTPUT);1764out_puts(out, str);1765if (!len || str[len-1] != '\n') out_putc(out, '\n');1766if (space) { linelen++; len--; }1767out_term_color(out, c_ERR);1768if (prefix) { out_puts(out, prefix); linelen -= prelen; }1769for (i=0; i<linelen; i++) out_putc(out, ' ');1770out_putc(out, '^');1771for (i=0; i<len; i++) out_putc(out, '-');1772}1773pari_free(word);1774}17751776#define CONTEXT_LEN 461777#define MAX_TERM_COLOR 161778/* Outputs a beautiful error message (not \n terminated)1779* msg is errmessage to print.1780* s points to the offending chars.1781* entry tells how much we can go back from s[0] */1782void1783print_errcontext(PariOUT *out,1784const char *msg, const char *s, const char *entry)1785{1786const long MAX_PAST = 25;1787long past = s - entry, future, lmsg;1788char str[CONTEXT_LEN + 1 + 1], pre[MAX_TERM_COLOR + 8 + 1];1789char *buf, *t;17901791if (!s || !entry) { print_prefixed_text(out, msg," *** ",NULL); return; }17921793/* message + context */1794lmsg = strlen(msg);1795/* msg + past + ': ' + '...' + term_get_color + \0 */1796t = buf = (char*)pari_malloc(lmsg + MAX_PAST + 2 + 3 + MAX_TERM_COLOR + 1);1797memcpy(t, msg, lmsg); t += lmsg;1798strcpy(t, ": "); t += 2;1799if (past <= 0) past = 0;1800else1801{1802if (past > MAX_PAST) { past = MAX_PAST; strcpy(t, "..."); t += 3; }1803term_get_color(t, c_OUTPUT);1804t += strlen(t);1805memcpy(t, s - past, past); t[past] = 0;1806}18071808/* suffix (past arrow) */1809t = str; if (!past) *t++ = ' ';1810future = CONTEXT_LEN - past;1811strncpy(t, s, future); t[future] = 0;1812/* prefix '***' */1813term_get_color(pre, c_ERR);1814strcat(pre, " *** ");1815/* now print */1816print_prefixed_text(out, buf, pre, str);1817pari_free(buf);1818}18191820/********************************************************************/1821/** **/1822/** GEN <---> CHARACTER STRINGS **/1823/** **/1824/********************************************************************/1825static OUT_FUN1826get_fun(long flag)1827{1828switch(flag) {1829case f_RAW : return bruti;1830case f_TEX : return texi;1831default: return matbruti;1832}1833}18341835/* not stack clean */1836static char *1837stack_GENtostr_fun(GEN x, pariout_t *T, OUT_FUN out)1838{1839pari_str S; str_init(&S, 1);1840out(x, T, &S); *S.cur = 0;1841return S.string;1842}1843/* same but remove quotes "" around t_STR */1844static char *1845stack_GENtostr_fun_unquoted(GEN x, pariout_t *T, OUT_FUN out)1846{ return (typ(x)==t_STR)? GSTR(x): stack_GENtostr_fun(x, T, out); }18471848/* stack-clean: pari-malloc'ed */1849static char *1850GENtostr_fun(GEN x, pariout_t *T, OUT_FUN out)1851{1852pari_sp av = avma;1853pari_str S; str_init(&S, 0);1854out(x, T, &S); *S.cur = 0;1855set_avma(av); return S.string;1856}1857/* returns a malloc-ed string, which should be freed after usage */1858/* Returns pari_malloc()ed string */1859char *1860GENtostr(GEN x)1861{ return GENtostr_fun(x, GP_DATA->fmt, get_fun(GP_DATA->fmt->prettyp)); }1862char *1863GENtoTeXstr(GEN x) { return GENtostr_fun(x, GP_DATA->fmt, &texi); }1864char *1865GENtostr_unquoted(GEN x)1866{ return stack_GENtostr_fun_unquoted(x, GP_DATA->fmt, &bruti); }1867/* alloc-ed on PARI stack */1868char *1869GENtostr_raw(GEN x) { return stack_GENtostr_fun(x,GP_DATA->fmt,&bruti); }18701871GEN1872GENtoGENstr(GEN x)1873{1874char *s = GENtostr_fun(x, GP_DATA->fmt, &bruti);1875GEN z = strtoGENstr(s); pari_free(s); return z;1876}1877GEN1878GENtoGENstr_nospace(GEN x)1879{1880pariout_t T = *(GP_DATA->fmt);1881char *s;1882GEN z;1883T.sp = 0;1884s = GENtostr_fun(x, &T, &bruti);1885z = strtoGENstr(s); pari_free(s); return z;1886}18871888/********************************************************************/1889/** **/1890/** WRITE AN INTEGER **/1891/** **/1892/********************************************************************/1893char *1894itostr(GEN x) {1895long sx = signe(x), l;1896return sx? itostr_sign(x, sx, &l): zerotostr();1897}18981899/* x != 0 t_INT, write abs(x) to S */1900static void1901str_absint(pari_str *S, GEN x)1902{1903pari_sp av;1904long l;1905str_alloc(S, lgefint(x)); /* careful ! */1906av = avma;1907str_puts(S, itostr_sign(x, 1, &l)); set_avma(av);1908}19091910#define putsigne_nosp(S, x) str_putc(S, (x>0)? '+' : '-')1911#define putsigne(S, x) str_puts(S, (x>0)? " + " : " - ")1912#define sp_sign_sp(T,S, x) ((T)->sp? putsigne(S,x): putsigne_nosp(S,x))1913#define semicolon_sp(T,S) ((T)->sp? str_puts(S, "; "): str_putc(S, ';'))1914#define comma_sp(T,S) ((T)->sp? str_puts(S, ", "): str_putc(S, ','))19151916/* print e to S (more efficient than sprintf) */1917static void1918str_ulong(pari_str *S, ulong e)1919{1920if (e == 0) str_putc(S, '0');1921else1922{1923char buf[21], *p = buf + numberof(buf);1924*--p = 0;1925if (e > 9) {1926do1927*--p = "0123456789"[e % 10];1928while ((e /= 10) > 9);1929}1930*--p = "0123456789"[e];1931str_puts(S, p);1932}1933}1934static void1935str_long(pari_str *S, long e)1936{1937if (e >= 0) str_ulong(S, (ulong)e);1938else { str_putc(S, '-'); str_ulong(S, -(ulong)e); }1939}19401941static void1942wr_vecsmall(pariout_t *T, pari_str *S, GEN g)1943{1944long i, l;1945str_puts(S, "Vecsmall(["); l = lg(g);1946for (i=1; i<l; i++)1947{1948str_long(S, g[i]);1949if (i<l-1) comma_sp(T,S);1950}1951str_puts(S, "])");1952}19531954/********************************************************************/1955/** **/1956/** HEXADECIMAL OUTPUT **/1957/** **/1958/********************************************************************/1959/* English ordinal numbers */1960char *1961uordinal(ulong i)1962{1963const char *suff[] = {"st","nd","rd","th"};1964char *s = stack_malloc(23);1965long k = 3;1966switch (i%10)1967{1968case 1: if (i%100!=11) k = 0;1969break;1970case 2: if (i%100!=12) k = 1;1971break;1972case 3: if (i%100!=13) k = 2;1973break;1974}1975sprintf(s, "%lu%s", i, suff[k]); return s;1976}19771978static char1979vsigne(GEN x)1980{1981long s = signe(x);1982if (!s) return '0';1983return (s > 0) ? '+' : '-';1984}19851986static void1987blancs(long nb) { while (nb-- > 0) pari_putc(' '); }19881989/* write an "address" */1990static void1991str_addr(pari_str *S, ulong x)1992{ char s[128]; sprintf(s,"%0*lx", BITS_IN_LONG/4, x); str_puts(S, s); }1993static void1994dbg_addr(ulong x) { pari_printf("[&=%0*lx] ", BITS_IN_LONG/4, x); }1995/* write a "word" */1996static void1997dbg_word(ulong x) { pari_printf("%0*lx ", BITS_IN_LONG/4, x); }19981999/* bl: indent level */2000static void2001dbg(GEN x, long nb, long bl)2002{2003long tx,i,j,e,dx,lx;20042005if (!x) { pari_puts("NULL\n"); return; }2006tx = typ(x);2007if (tx == t_INT && x == gen_0) { pari_puts("gen_0\n"); return; }2008dbg_addr((ulong)x);20092010lx = lg(x);2011pari_printf("%s(lg=%ld%s):",type_name(tx)+2,lx,isclone(x)? ",CLONE" : "");2012dbg_word(x[0]);2013if (! is_recursive_t(tx)) /* t_INT, t_REAL, t_STR, t_VECSMALL */2014{2015if (tx == t_STR)2016pari_puts("chars:");2017else if (tx == t_INT)2018{2019lx = lgefint(x);2020pari_printf("(%c,lgefint=%ld):", vsigne(x), lx);2021}2022else if (tx == t_REAL)2023pari_printf("(%c,expo=%ld):", vsigne(x), expo(x));2024if (nb < 0) nb = lx;2025for (i=1; i < nb; i++) dbg_word(x[i]);2026pari_putc('\n'); return;2027}20282029if (tx == t_PADIC)2030pari_printf("(precp=%ld,valp=%ld):", precp(x), valp(x));2031else if (tx == t_POL)2032pari_printf("(%c,varn=%ld):", vsigne(x), varn(x));2033else if (tx == t_SER)2034pari_printf("(%c,varn=%ld,prec=%ld,valp=%ld):",2035vsigne(x), varn(x), lg(x)-2, valp(x));2036else if (tx == t_LIST)2037{2038pari_printf("(subtyp=%ld,lmax=%ld):", list_typ(x), list_nmax(x));2039x = list_data(x); lx = x? lg(x): 1;2040tx = t_VEC; /* print list_data as vec */2041} else if (tx == t_CLOSURE)2042pari_printf("(arity=%ld%s):", closure_arity(x),2043closure_is_variadic(x)?"+":"");2044for (i=1; i<lx; i++) dbg_word(x[i]);2045bl+=2; pari_putc('\n');2046switch(tx)2047{2048case t_INTMOD: case t_POLMOD:2049{2050const char *s = (tx==t_INTMOD)? "int = ": "pol = ";2051blancs(bl); pari_puts("mod = "); dbg(gel(x,1),nb,bl);2052blancs(bl); pari_puts(s); dbg(gel(x,2),nb,bl);2053break;2054}2055case t_FRAC: case t_RFRAC:2056blancs(bl); pari_puts("num = "); dbg(gel(x,1),nb,bl);2057blancs(bl); pari_puts("den = "); dbg(gel(x,2),nb,bl);2058break;20592060case t_FFELT:2061blancs(bl); pari_puts("pol = "); dbg(gel(x,2),nb,bl);2062blancs(bl); pari_puts("mod = "); dbg(gel(x,3),nb,bl);2063blancs(bl); pari_puts("p = "); dbg(gel(x,4),nb,bl);2064break;20652066case t_COMPLEX:2067blancs(bl); pari_puts("real = "); dbg(gel(x,1),nb,bl);2068blancs(bl); pari_puts("imag = "); dbg(gel(x,2),nb,bl);2069break;20702071case t_PADIC:2072blancs(bl); pari_puts(" p : "); dbg(gel(x,2),nb,bl);2073blancs(bl); pari_puts("p^l : "); dbg(gel(x,3),nb,bl);2074blancs(bl); pari_puts(" I : "); dbg(gel(x,4),nb,bl);2075break;20762077case t_QUAD:2078blancs(bl); pari_puts("pol = "); dbg(gel(x,1),nb,bl);2079blancs(bl); pari_puts("real = "); dbg(gel(x,2),nb,bl);2080blancs(bl); pari_puts("imag = "); dbg(gel(x,3),nb,bl);2081break;20822083case t_POL: case t_SER:2084e = (tx==t_SER)? valp(x): 0;2085for (i=2; i<lx; i++)2086{2087blancs(bl); pari_printf("coef of degree %ld = ",e);2088e++; dbg(gel(x,i),nb,bl);2089}2090break;20912092case t_QFB: case t_VEC: case t_COL:2093for (i=1; i<lx; i++)2094{2095blancs(bl); pari_printf("%s component = ",uordinal(i));2096dbg(gel(x,i),nb,bl);2097}2098break;20992100case t_CLOSURE:2101blancs(bl); pari_puts("code = "); dbg(closure_get_code(x),nb,bl);2102blancs(bl); pari_puts("operand = "); dbg(closure_get_oper(x),nb,bl);2103blancs(bl); pari_puts("data = "); dbg(closure_get_data(x),nb,bl);2104blancs(bl); pari_puts("dbg/frpc/fram = "); dbg(closure_get_dbg(x),nb,bl);2105if (lg(x)>=7)2106{2107blancs(bl); pari_puts("text = "); dbg(closure_get_text(x),nb,bl);2108if (lg(x)>=8)2109{2110blancs(bl); pari_puts("frame = "); dbg(closure_get_frame(x),nb,bl);2111}2112}2113break;21142115case t_ERROR:2116blancs(bl);2117pari_printf("error type = %s\n", numerr_name(err_get_num(x)));2118for (i=2; i<lx; i++)2119{2120blancs(bl); pari_printf("%s component = ",uordinal(i-1));2121dbg(gel(x,i),nb,bl);2122}2123break;21242125case t_INFINITY:2126blancs(bl); pari_printf("1st component = ");2127dbg(gel(x,1),nb,bl);2128break;21292130case t_MAT:2131{2132GEN c = gel(x,1);2133if (lx == 1) return;2134if (typ(c) == t_VECSMALL)2135{2136for (i = 1; i < lx; i++)2137{2138blancs(bl); pari_printf("%s column = ",uordinal(i));2139dbg(gel(x,i),nb,bl);2140}2141}2142else2143{2144dx = lg(c);2145for (i=1; i<dx; i++)2146for (j=1; j<lx; j++)2147{2148blancs(bl); pari_printf("mat(%ld,%ld) = ",i,j);2149dbg(gcoeff(x,i,j),nb,bl);2150}2151}2152}2153}2154}21552156void2157dbgGEN(GEN x, long nb) { dbg(x,nb,0); }21582159static void2160print_entree(entree *ep)2161{2162pari_printf(" %s ",ep->name); dbg_addr((ulong)ep);2163pari_printf(": hash = %ld [%ld]\n", ep->hash % functions_tblsz, ep->hash);2164pari_printf(" menu = %2ld, code = %-10s",2165ep->menu, ep->code? ep->code: "NULL");2166if (ep->next)2167{2168pari_printf("next = %s ",(ep->next)->name);2169dbg_addr((ulong)ep->next);2170}2171pari_puts("\n");2172}21732174/* s = digit n : list of entrees in functions_hash[n] (s = $: last entry)2175* = range m-n: functions_hash[m..n]2176* = identifier: entree for that identifier */2177void2178print_functions_hash(const char *s)2179{2180long m, n, Max, Total;2181entree *ep;21822183if (isdigit((int)*s) || *s == '$')2184{2185m = functions_tblsz-1; n = atol(s);2186if (*s=='$') n = m;2187if (m<n) pari_err(e_MISC,"invalid range in print_functions_hash");2188while (isdigit((int)*s)) s++;21892190if (*s++ != '-') m = n;2191else2192{2193if (*s !='$') m = minss(atol(s),m);2194if (m<n) pari_err(e_MISC,"invalid range in print_functions_hash");2195}21962197for(; n<=m; n++)2198{2199pari_printf("*** hashcode = %lu\n",n);2200for (ep=functions_hash[n]; ep; ep=ep->next) print_entree(ep);2201}2202return;2203}2204if (is_keyword_char((int)*s))2205{2206ep = is_entry(s);2207if (!ep) pari_err(e_MISC,"no such function");2208print_entree(ep); return;2209}2210if (*s=='-')2211{2212for (n=0; n<functions_tblsz; n++)2213{2214m=0;2215for (ep=functions_hash[n]; ep; ep=ep->next) m++;2216pari_printf("%3ld:%3ld ",n,m);2217if (n%9 == 8) pari_putc('\n');2218}2219pari_putc('\n'); return;2220}2221Max = Total = 0;2222for (n=0; n<functions_tblsz; n++)2223{2224long cnt = 0;2225for (ep=functions_hash[n]; ep; ep=ep->next) { print_entree(ep); cnt++; }2226Total += cnt;2227if (cnt > Max) Max = cnt;2228}2229pari_printf("Total: %ld, Max: %ld\n", Total, Max);2230}22312232/********************************************************************/2233/** **/2234/** FORMATTED OUTPUT **/2235/** **/2236/********************************************************************/2237static const char *2238get_var(long v, char *buf)2239{2240entree *ep = varentries[v];2241if (ep) return (char*)ep->name;2242sprintf(buf,"t%d",(int)v); return buf;2243}22442245static void2246do_append(char **sp, char c, char *last, int count)2247{2248if (*sp + count > last)2249pari_err(e_MISC, "TeX variable name too long");2250while (count--)2251*(*sp)++ = c;2252}22532254static char *2255get_texvar(long v, char *buf, unsigned int len)2256{2257entree *ep = varentries[v];2258char *t = buf, *e = buf + len - 1;2259const char *s;22602261if (!ep) pari_err(e_MISC, "this object uses debugging variables");2262s = ep->name;2263if (strlen(s) >= len) pari_err(e_MISC, "TeX variable name too long");2264while (isalpha((int)*s)) *t++ = *s++;2265*t = 0;2266if (isdigit((int)*s) || *s == '_') {2267int seen1 = 0, seen = 0;22682269/* Skip until the first non-underscore */2270while (*s == '_') s++, seen++;22712272/* Special-case integers and empty subscript */2273if (*s == 0 || isdigit((unsigned char)*s))2274seen++;22752276do_append(&t, '_', e, 1);2277do_append(&t, '{', e, 1);2278do_append(&t, '[', e, seen - 1);2279while (1) {2280if (*s == '_')2281seen1++, s++;2282else {2283if (seen1) {2284do_append(&t, ']', e, (seen >= seen1 ? seen1 : seen) - 1);2285do_append(&t, ',', e, 1);2286do_append(&t, '[', e, seen1 - 1);2287if (seen1 > seen)2288seen = seen1;2289seen1 = 0;2290}2291if (*s == 0)2292break;2293do_append(&t, *s++, e, 1);2294}2295}2296do_append(&t, ']', e, seen - 1);2297do_append(&t, '}', e, 1);2298*t = 0;2299}2300return buf;2301}23022303void2304dbg_pari_heap(void)2305{2306long nu, l, u, s;2307pari_sp av = avma;2308GEN adr = getheap();2309pari_sp top = pari_mainstack->top, bot = pari_mainstack->bot;23102311nu = (top-avma)/sizeof(long);2312l = pari_mainstack->size/sizeof(long);2313pari_printf("\n Top : %lx Bottom : %lx Current stack : %lx\n",2314top, bot, avma);2315pari_printf(" Used : %ld long words (%ld K)\n",2316nu, nu/1024*sizeof(long));2317pari_printf(" Available : %ld long words (%ld K)\n",2318(l-nu), (l-nu)/1024*sizeof(long));2319pari_printf(" Occupation of the PARI stack : %6.2f percent\n", 100.0*nu/l);2320pari_printf(" %ld objects on heap occupy %ld long words\n\n",2321itos(gel(adr,1)), itos(gel(adr,2)));2322u = pari_var_next();2323s = MAXVARN - pari_var_next_temp();2324pari_printf(" %ld variable names used (%ld user + %ld private) out of %d\n\n",2325u+s, u, s, MAXVARN);2326set_avma(av);2327}23282329/* is to be printed as '0' */2330static long2331isnull(GEN g)2332{2333long i;2334switch (typ(g))2335{2336case t_INT:2337return !signe(g);2338case t_COMPLEX:2339return isnull(gel(g,1)) && isnull(gel(g,2));2340case t_FFELT:2341return FF_equal0(g);2342case t_QUAD:2343return isnull(gel(g,2)) && isnull(gel(g,3));2344case t_FRAC: case t_RFRAC:2345return isnull(gel(g,1));2346case t_POL:2347for (i=lg(g)-1; i>1; i--)2348if (!isnull(gel(g,i))) return 0;2349return 1;2350}2351return 0;2352}2353/* 0 coeff to be omitted in t_POL ? */2354static int2355isnull_for_pol(GEN g)2356{2357switch(typ(g))2358{2359case t_INTMOD: return !signe(gel(g,2));2360case t_POLMOD: return isnull(gel(g,2));2361default: return isnull(g);2362}2363}23642365/* return 1 or -1 if g is 1 or -1, 0 otherwise*/2366static long2367isone(GEN g)2368{2369long i;2370switch (typ(g))2371{2372case t_INT:2373return (signe(g) && is_pm1(g))? signe(g): 0;2374case t_FFELT:2375return FF_equal1(g);2376case t_COMPLEX:2377return isnull(gel(g,2))? isone(gel(g,1)): 0;2378case t_QUAD:2379return isnull(gel(g,3))? isone(gel(g,2)): 0;2380case t_FRAC: case t_RFRAC:2381return isone(gel(g,1)) * isone(gel(g,2));2382case t_POL:2383if (!signe(g)) return 0;2384for (i=lg(g)-1; i>2; i--)2385if (!isnull(gel(g,i))) return 0;2386return isone(gel(g,2));2387}2388return 0;2389}23902391/* if g is a "monomial", return its sign, 0 otherwise */2392static long2393isfactor(GEN g)2394{2395long i,deja,sig;2396switch(typ(g))2397{2398case t_INT: case t_REAL:2399return (signe(g)<0)? -1: 1;2400case t_FRAC: case t_RFRAC:2401return isfactor(gel(g,1));2402case t_FFELT:2403return isfactor(FF_to_FpXQ_i(g));2404case t_COMPLEX:2405if (isnull(gel(g,1))) return isfactor(gel(g,2));2406if (isnull(gel(g,2))) return isfactor(gel(g,1));2407return 0;2408case t_PADIC:2409return !signe(gel(g,4));2410case t_QUAD:2411if (isnull(gel(g,2))) return isfactor(gel(g,3));2412if (isnull(gel(g,3))) return isfactor(gel(g,2));2413return 0;2414case t_POL: deja = 0; sig = 1;2415for (i=lg(g)-1; i>1; i--)2416if (!isnull_for_pol(gel(g,i)))2417{2418if (deja) return 0;2419sig=isfactor(gel(g,i)); deja=1;2420}2421return sig? sig: 1;2422case t_SER:2423for (i=lg(g)-1; i>1; i--)2424if (!isnull(gel(g,i))) return 0;2425return 1;2426case t_CLOSURE:2427return 0;2428}2429return 1;2430}24312432/* return 1 if g is a "truc" (see anal.c) */2433static long2434isdenom(GEN g)2435{2436long i,deja;2437switch(typ(g))2438{2439case t_FRAC: case t_RFRAC:2440return 0;2441case t_COMPLEX: return isnull(gel(g,2));2442case t_PADIC: return !signe(gel(g,4));2443case t_QUAD: return isnull(gel(g,3));24442445case t_POL: deja = 0;2446for (i=lg(g)-1; i>1; i--)2447if (!isnull(gel(g,i)))2448{2449if (deja) return 0;2450if (i==2) return isdenom(gel(g,2));2451if (!isone(gel(g,i))) return 0;2452deja=1;2453}2454return 1;2455case t_SER:2456for (i=lg(g)-1; i>1; i--)2457if (!isnull(gel(g,i))) return 0;2458}2459return 1;2460}24612462/********************************************************************/2463/** **/2464/** RAW OUTPUT **/2465/** **/2466/********************************************************************/2467/* ^e */2468static void2469texexpo(pari_str *S, long e)2470{2471if (e != 1) {2472str_putc(S, '^');2473if (e >= 0 && e < 10)2474{ str_putc(S, '0' + e); }2475else2476{2477str_putc(S, '{'); str_long(S, e); str_putc(S, '}');2478}2479}2480}2481static void2482wrexpo(pari_str *S, long e)2483{ if (e != 1) { str_putc(S, '^'); str_long(S, e); } }24842485/* v^e */2486static void2487VpowE(pari_str *S, const char *v, long e) { str_puts(S, v); wrexpo(S,e); }2488static void2489texVpowE(pari_str *S, const char *v, long e) { str_puts(S, v); texexpo(S,e); }2490static void2491monome(pari_str *S, const char *v, long e)2492{ if (e) VpowE(S, v, e); else str_putc(S, '1'); }2493static void2494texnome(pari_str *S, const char *v, long e)2495{ if (e) texVpowE(S, v, e); else str_putc(S, '1'); }24962497/* ( a ) */2498static void2499paren(pariout_t *T, pari_str *S, GEN a)2500{ str_putc(S, '('); bruti(a,T,S); str_putc(S, ')'); }2501static void2502texparen(pariout_t *T, pari_str *S, GEN a)2503{2504if (T->TeXstyle & TEXSTYLE_PAREN)2505str_puts(S, " (");2506else2507str_puts(S, " \\left(");2508texi(a,T,S);2509if (T->TeXstyle & TEXSTYLE_PAREN)2510str_puts(S, ") ");2511else2512str_puts(S, "\\right) ");2513}25142515/* * v^d */2516static void2517times_texnome(pari_str *S, const char *v, long d)2518{ if (d) { str_puts(S, "\\*"); texnome(S,v,d); } }2519static void2520times_monome(pari_str *S, const char *v, long d)2521{ if (d) { str_putc(S, '*'); monome(S,v,d); } }25222523/* write a * v^d */2524static void2525wr_monome(pariout_t *T, pari_str *S, GEN a, const char *v, long d)2526{2527long sig = isone(a);25282529if (sig) {2530sp_sign_sp(T,S,sig); monome(S,v,d);2531} else {2532sig = isfactor(a);2533if (sig) { sp_sign_sp(T,S,sig); bruti_sign(a,T,S,0); }2534else { sp_sign_sp(T,S,1); paren(T,S, a); }2535times_monome(S, v, d);2536}2537}2538static void2539wr_texnome(pariout_t *T, pari_str *S, GEN a, const char *v, long d)2540{2541long sig = isone(a);25422543str_putc(S, '\n'); /* Avoid TeX buffer overflow */2544if (T->TeXstyle & TEXSTYLE_BREAK) str_puts(S, "\\PARIbreak ");25452546if (sig) {2547putsigne(S,sig); texnome(S,v,d);2548} else {2549sig = isfactor(a);2550if (sig) { putsigne(S,sig); texi_sign(a,T,S,0); }2551else { str_puts(S, " +"); texparen(T,S, a); }2552times_texnome(S, v, d);2553}2554}25552556static void2557wr_lead_monome(pariout_t *T, pari_str *S, GEN a,const char *v, long d, int addsign)2558{2559long sig = isone(a);2560if (sig) {2561if (addsign && sig<0) str_putc(S, '-');2562monome(S,v,d);2563} else {2564if (isfactor(a)) bruti_sign(a,T,S,addsign);2565else paren(T,S, a);2566times_monome(S, v, d);2567}2568}2569static void2570wr_lead_texnome(pariout_t *T, pari_str *S, GEN a,const char *v, long d, int addsign)2571{2572long sig = isone(a);2573if (sig) {2574if (addsign && sig<0) str_putc(S, '-');2575texnome(S,v,d);2576} else {2577if (isfactor(a)) texi_sign(a,T,S,addsign);2578else texparen(T,S, a);2579times_texnome(S, v, d);2580}2581}25822583static void2584prints(GEN g, pariout_t *T, pari_str *S)2585{ (void)T; str_long(S, (long)g); }25862587static void2588quote_string(pari_str *S, char *s)2589{2590str_putc(S, '"');2591while (*s)2592{2593char c=*s++;2594if (c=='\\' || c=='"' || c=='\033' || c=='\n' || c=='\t')2595{2596str_putc(S, '\\');2597switch(c)2598{2599case '\\': case '"': break;2600case '\n': c='n'; break;2601case '\033': c='e'; break;2602case '\t': c='t'; break;2603}2604}2605str_putc(S, c);2606}2607str_putc(S, '"');2608}26092610static int2611print_0_or_pm1(GEN g, pari_str *S, int addsign)2612{2613long r;2614if (!g) { str_puts(S, "NULL"); return 1; }2615if (isnull(g)) { str_putc(S, '0'); return 1; }2616r = isone(g);2617if (r)2618{2619if (addsign && r<0) str_putc(S, '-');2620str_putc(S, '1'); return 1;2621}2622return 0;2623}26242625static void2626print_precontext(GEN g, pari_str *S, long tex)2627{2628if (lg(g)<8 || lg(gel(g,7))==1) return;2629else2630{2631long i, n = closure_arity(g);2632str_puts(S,"(");2633for(i=1; i<=n; i++)2634{2635str_puts(S,"v");2636if (tex) str_puts(S,"_{");2637str_ulong(S,i);2638if (tex) str_puts(S,"}");2639if (i < n) str_puts(S,",");2640}2641str_puts(S,")->");2642}2643}26442645static void2646print_context(GEN g, pariout_t *T, pari_str *S, long tex)2647{2648GEN str = closure_get_text(g);2649if (lg(g)<8 || lg(gel(g,7))==1) return;2650if (typ(str)==t_VEC && lg(gel(closure_get_dbg(g),3)) >= 2)2651{2652GEN v = closure_get_frame(g), d = gmael(closure_get_dbg(g),3,1);2653long i, l = lg(v), n=0;2654for(i=1; i<l; i++)2655if (gel(d,i))2656n++;2657if (n==0) return;2658str_puts(S,"my(");2659for(i=1; i<l; i++)2660if (gel(d,i))2661{2662entree *ep = (entree*) gel(d,i);2663GEN vi = gel(v,l-i);2664str_puts(S,ep->name);2665if (!isintzero(vi))2666{2667str_putc(S,'=');2668if (tex) texi(gel(v,l-i),T,S); else bruti(gel(v,l-i),T,S);2669}2670if (--n)2671str_putc(S,',');2672}2673str_puts(S,");");2674}2675else2676{2677GEN v = closure_get_frame(g);2678long i, l = lg(v), n = closure_arity(g);2679str_puts(S,"(");2680for(i=1; i<=n; i++)2681{2682str_puts(S,"v");2683if (tex) str_puts(S,"_{");2684str_ulong(S,i);2685if (tex) str_puts(S,"}");2686str_puts(S,",");2687}2688for(i=1; i<l; i++)2689{2690if (tex) texi(gel(v,i),T,S); else bruti(gel(v,i),T,S);2691if (i<l-1)2692str_putc(S,',');2693}2694str_puts(S,")");2695}2696}2697static void2698mat0n(pari_str *S, long n)2699{ str_puts(S, "matrix(0,"); str_long(S, n); str_putc(S, ')'); }27002701static const char *2702cxq_init(GEN g, long tg, GEN *a, GEN *b, char *buf)2703{2704int r = (tg==t_QUAD);2705*a = gel(g,r+1);2706*b = gel(g,r+2); return r? get_var(varn(gel(g,1)), buf): "I";2707}27082709static void2710print_coef(GEN g, long i, long j, pariout_t *T, pari_str *S)2711{ (void)T; str_long(S, coeff(g,i,j)); }2712static void2713print_gcoef(GEN g, long i, long j, pariout_t *T, pari_str *S)2714{2715GEN gij = gcoeff(g, i, j);2716if (typ(gij)==t_CLOSURE)2717{ str_putc(S, '('); bruti(gij, T, S); str_putc(S, ')'); }2718else2719bruti(gij, T, S);2720}27212722static void2723bruti_intern(GEN g, pariout_t *T, pari_str *S, int addsign)2724{2725long l,i,j,r, tg = typ(g);2726GEN a,b;2727const char *v;2728char buf[32];27292730switch(tg)2731{2732case t_INT:2733if (addsign && signe(g) < 0) str_putc(S, '-');2734str_absint(S, g); break;2735case t_REAL:2736{2737pari_sp av;2738str_alloc(S, lg(g)); /* careful! */2739av = avma;2740if (addsign && signe(g) < 0) str_putc(S, '-');2741str_puts(S, absrtostr(g, T->sp, (char)toupper((int)T->format), T->sigd) );2742set_avma(av); break;2743}27442745case t_INTMOD: case t_POLMOD:2746str_puts(S, "Mod(");2747bruti(gel(g,2),T,S); comma_sp(T,S);2748bruti(gel(g,1),T,S); str_putc(S, ')'); break;27492750case t_FFELT:2751bruti_sign(FF_to_FpXQ_i(g),T,S,addsign);2752break;27532754case t_FRAC: case t_RFRAC:2755r = isfactor(gel(g,1)); if (!r) str_putc(S, '(');2756bruti_sign(gel(g,1),T,S,addsign);2757if (!r) str_putc(S, ')');2758str_putc(S, '/');2759r = isdenom(gel(g,2)); if (!r) str_putc(S, '(');2760bruti(gel(g,2),T,S);2761if (!r) str_putc(S, ')');2762break;27632764case t_COMPLEX: case t_QUAD: r = (tg==t_QUAD);2765v = cxq_init(g, tg, &a, &b, buf);2766if (isnull(a))2767{2768wr_lead_monome(T,S,b,v,1,addsign);2769return;2770}2771bruti_sign(a,T,S,addsign);2772if (!isnull(b)) wr_monome(T,S,b,v,1);2773break;27742775case t_POL: v = get_var(varn(g), buf);2776/* hack: we want g[i] = coeff of degree i. */2777i = degpol(g); g += 2; while (isnull(gel(g,i))) i--;2778wr_lead_monome(T,S,gel(g,i),v,i,addsign);2779while (i--)2780{2781a = gel(g,i);2782if (!isnull_for_pol(a)) wr_monome(T,S,a,v,i);2783}2784break;27852786case t_SER: v = get_var(varn(g), buf);2787i = valp(g);2788l = lg(g)-2;2789if (l)2790{2791/* See normalize(): Mod(0,2)*x^i*(1+O(x)), has valp = i+1 */2792if (l == 1 && !signe(g) && isexactzero(gel(g,2))) i--;2793/* hack: we want g[i] = coeff of degree i */2794l += i; g -= i-2;2795wr_lead_monome(T,S,gel(g,i),v,i,addsign);2796while (++i < l)2797{2798a = gel(g,i);2799if (!isnull_for_pol(a)) wr_monome(T,S,a,v,i);2800}2801sp_sign_sp(T,S,1);2802}2803str_puts(S, "O("); VpowE(S, v, i); str_putc(S, ')'); break;28042805case t_PADIC:2806{2807GEN p = gel(g,2);2808pari_sp av, av0;2809char *ev;2810str_alloc(S, (precp(g)+1) * lgefint(p)); /* careful! */2811av0 = avma;2812ev = itostr(p);2813av = avma;2814i = valp(g); l = precp(g)+i;2815g = gel(g,4);2816for (; i<l; i++)2817{2818g = dvmdii(g,p,&a);2819if (signe(a))2820{2821if (!i || !is_pm1(a))2822{2823str_absint(S, a); if (i) str_putc(S, '*');2824}2825if (i) VpowE(S, ev,i);2826sp_sign_sp(T,S,1);2827}2828if ((i & 0xff) == 0) g = gerepileuptoint(av,g);2829}2830str_puts(S, "O("); VpowE(S, ev,i); str_putc(S, ')');2831set_avma(av0); break;2832}28332834case t_QFB:2835str_puts(S, "Qfb(");2836bruti(gel(g,1),T,S); comma_sp(T,S);2837bruti(gel(g,2),T,S); comma_sp(T,S);2838bruti(gel(g,3),T,S);2839str_putc(S, ')'); break;28402841case t_VEC: case t_COL:2842str_putc(S, '['); l = lg(g);2843for (i=1; i<l; i++)2844{2845bruti(gel(g,i),T,S);2846if (i<l-1) comma_sp(T,S);2847}2848str_putc(S, ']'); if (tg==t_COL) str_putc(S, '~');2849break;2850case t_VECSMALL: wr_vecsmall(T,S,g); break;28512852case t_LIST:2853switch (list_typ(g))2854{2855case t_LIST_RAW:2856str_puts(S, "List([");2857g = list_data(g);2858l = g? lg(g): 1;2859for (i=1; i<l; i++)2860{2861bruti(gel(g,i),T,S);2862if (i<l-1) comma_sp(T,S);2863}2864str_puts(S, "])"); break;2865case t_LIST_MAP:2866str_puts(S, "Map(");2867bruti(maptomat_shallow(g),T,S);2868str_puts(S, ")"); break;2869}2870break;2871case t_STR:2872quote_string(S, GSTR(g)); break;2873case t_ERROR:2874{2875char *s = pari_err2str(g);2876str_puts(S, "error(");2877quote_string(S, s); pari_free(s);2878str_puts(S, ")"); break;2879}2880case t_CLOSURE:2881if (lg(g)>=7)2882{2883GEN str = closure_get_text(g);2884if (typ(str)==t_STR)2885{2886print_precontext(g, S, 0);2887str_puts(S, GSTR(str));2888print_context(g, T, S, 0);2889}2890else2891{2892str_putc(S,'('); str_puts(S,GSTR(gel(str,1)));2893str_puts(S,")->");2894print_context(g, T, S, 0);2895str_puts(S,GSTR(gel(str,2)));2896}2897}2898else2899{2900str_puts(S,"{\""); str_puts(S,GSTR(closure_get_code(g)));2901str_puts(S,"\","); wr_vecsmall(T,S,closure_get_oper(g));2902str_putc(S,','); bruti(gel(g,4),T,S);2903str_putc(S,','); bruti(gel(g,5),T,S);2904str_putc(S,'}');2905}2906break;2907case t_INFINITY: str_puts(S, inf_get_sign(g) == 1? "+oo": "-oo");2908break;29092910case t_MAT:2911{2912void (*print)(GEN,long,long,pariout_t *,pari_str *);29132914r = lg(g); if (r==1) { str_puts(S, "[;]"); return; }2915l = lgcols(g); if (l==1) { mat0n(S, r-1); return; }2916print = (typ(gel(g,1)) == t_VECSMALL)? print_coef: print_gcoef;2917if (l==2)2918{2919str_puts(S, "Mat(");2920if (r == 2) { print(g, 1, 1,T, S); str_putc(S, ')'); return; }2921}2922str_putc(S, '[');2923for (i=1; i<l; i++)2924{2925for (j=1; j<r; j++)2926{2927print(g, i, j, T, S);2928if (j<r-1) comma_sp(T,S);2929}2930if (i<l-1) semicolon_sp(T,S);2931}2932str_putc(S, ']'); if (l==2) str_putc(S, ')');2933break;2934}29352936default: str_addr(S, *g);2937}2938}29392940static void2941bruti_sign(GEN g, pariout_t *T, pari_str *S, int addsign)2942{2943if (!print_0_or_pm1(g, S, addsign))2944bruti_intern(g, T, S, addsign);2945}29462947static void2948matbruti(GEN g, pariout_t *T, pari_str *S)2949{2950long i, j, r, w, l, *pad = NULL;2951pari_sp av;2952OUT_FUN print;29532954if (typ(g) != t_MAT) { bruti(g,T,S); return; }29552956r=lg(g); if (r==1) { str_puts(S, "[;]"); return; }2957l = lgcols(g); if (l==1) { mat0n(S, r-1); return; }2958str_putc(S, '\n');2959print = (typ(gel(g,1)) == t_VECSMALL)? prints: bruti;2960av = avma;2961w = term_width();2962if (2*r < w)2963{2964long lgall = 2; /* opening [ and closing ] */2965pari_sp av2;2966pari_str str;2967pad = cgetg(l*r+1, t_VECSMALL); /* left on stack if (S->use_stack)*/2968av2 = avma;2969str_init(&str, 1);2970for (j=1; j<r; j++)2971{2972GEN col = gel(g,j);2973long maxc = 0;2974for (i=1; i<l; i++)2975{2976long lgs;2977str.cur = str.string;2978print(gel(col,i),T,&str);2979lgs = str.cur - str.string;2980pad[j*l+i] = -lgs;2981if (maxc < lgs) maxc = lgs;2982}2983for (i=1; i<l; i++) pad[j*l+i] += maxc;2984lgall += maxc + 1; /* column width, including separating space */2985if (lgall > w) { pad = NULL; break; } /* doesn't fit, abort padding */2986}2987set_avma(av2);2988}2989for (i=1; i<l; i++)2990{2991str_putc(S, '[');2992for (j=1; j<r; j++)2993{2994if (pad) {2995long white = pad[j*l+i];2996while (white-- > 0) str_putc(S, ' ');2997}2998print(gcoeff(g,i,j),T,S); if (j<r-1) str_putc(S, ' ');2999}3000if (i<l-1) str_puts(S, "]\n\n"); else str_puts(S, "]\n");3001}3002if (!S->use_stack) set_avma(av);3003}30043005/********************************************************************/3006/** **/3007/** TeX OUTPUT **/3008/** **/3009/********************************************************************/3010/* this follows bruti_sign */3011static void3012texi_sign(GEN g, pariout_t *T, pari_str *S, int addsign)3013{3014long tg,i,j,l,r;3015GEN a,b;3016const char *v;3017char buf[67];30183019if (print_0_or_pm1(g, S, addsign)) return;30203021tg = typ(g);3022switch(tg)3023{3024case t_INT: case t_REAL: case t_QFB:3025bruti_intern(g, T, S, addsign); break;30263027case t_INTMOD: case t_POLMOD:3028texi(gel(g,2),T,S); str_puts(S, " mod ");3029texi(gel(g,1),T,S); break;30303031case t_FRAC:3032if (addsign && isfactor(gel(g,1)) < 0) str_putc(S, '-');3033str_puts(S, "\\frac{");3034texi_sign(gel(g,1),T,S,0);3035str_puts(S, "}{");3036texi_sign(gel(g,2),T,S,0);3037str_puts(S, "}"); break;30383039case t_RFRAC:3040str_puts(S, "\\frac{");3041texi(gel(g,1),T,S); /* too complicated otherwise */3042str_puts(S, "}{");3043texi(gel(g,2),T,S);3044str_puts(S, "}"); break;30453046case t_FFELT:3047bruti_sign(FF_to_FpXQ_i(g),T,S,addsign);3048break;30493050case t_COMPLEX: case t_QUAD: r = (tg==t_QUAD);3051v = cxq_init(g, tg, &a, &b, buf);3052if (isnull(a))3053{3054wr_lead_texnome(T,S,b,v,1,addsign);3055break;3056}3057texi_sign(a,T,S,addsign);3058if (!isnull(b)) wr_texnome(T,S,b,v,1);3059break;30603061case t_POL: v = get_texvar(varn(g), buf, sizeof(buf));3062/* hack: we want g[i] = coeff of degree i. */3063i = degpol(g); g += 2; while (isnull(gel(g,i))) i--;3064wr_lead_texnome(T,S,gel(g,i),v,i,addsign);3065while (i--)3066{3067a = gel(g,i);3068if (!isnull_for_pol(a)) wr_texnome(T,S,a,v,i);3069}3070break;30713072case t_SER: v = get_texvar(varn(g), buf, sizeof(buf));3073i = valp(g);3074if (lg(g)-2)3075{ /* hack: we want g[i] = coeff of degree i. */3076l = i + lg(g)-2; g -= i-2;3077wr_lead_texnome(T,S,gel(g,i),v,i,addsign);3078while (++i < l)3079{3080a = gel(g,i);3081if (!isnull_for_pol(a)) wr_texnome(T,S,a,v,i);3082}3083str_puts(S, "+ ");3084}3085str_puts(S, "O("); texnome(S,v,i); str_putc(S, ')'); break;30863087case t_PADIC:3088{3089GEN p = gel(g,2);3090pari_sp av;3091char *ev;3092str_alloc(S, (precp(g)+1) * lgefint(p)); /* careful! */3093av = avma;3094i = valp(g); l = precp(g)+i;3095g = gel(g,4); ev = itostr(p);3096for (; i<l; i++)3097{3098g = dvmdii(g,p,&a);3099if (signe(a))3100{3101if (!i || !is_pm1(a))3102{3103str_absint(S, a); if (i) str_puts(S, "\\cdot");3104}3105if (i) texVpowE(S, ev,i);3106str_putc(S, '+');3107}3108}3109str_puts(S, "O("); texVpowE(S, ev,i); str_putc(S, ')');3110set_avma(av); break;3111}31123113case t_VEC:3114str_puts(S, "\\pmatrix{ "); l = lg(g);3115for (i=1; i<l; i++)3116{3117texi(gel(g,i),T,S); if (i < l-1) str_putc(S, '&');3118}3119str_puts(S, "\\cr}\n"); break;31203121case t_LIST:3122switch(list_typ(g))3123{3124case t_LIST_RAW:3125str_puts(S, "\\pmatrix{ ");3126g = list_data(g);3127l = g? lg(g): 1;3128for (i=1; i<l; i++)3129{3130texi(gel(g,i),T,S); if (i < l-1) str_putc(S, '&');3131}3132str_puts(S, "\\cr}\n"); break;3133case t_LIST_MAP:3134{3135pari_sp av = avma;3136texi(maptomat_shallow(g),T,S);3137set_avma(av);3138break;3139}3140}3141break;3142case t_COL:3143str_puts(S, "\\pmatrix{ "); l = lg(g);3144for (i=1; i<l; i++)3145{3146texi(gel(g,i),T,S); str_puts(S, "\\cr\n");3147}3148str_putc(S, '}'); break;31493150case t_VECSMALL:3151str_puts(S, "\\pmatrix{ "); l = lg(g);3152for (i=1; i<l; i++)3153{3154str_long(S, g[i]);3155if (i < l-1) str_putc(S, '&');3156}3157str_puts(S, "\\cr}\n"); break;31583159case t_STR:3160str_puts(S, GSTR(g)); break;31613162case t_CLOSURE:3163if (lg(g)>=6)3164{3165GEN str = closure_get_text(g);3166if (typ(str)==t_STR)3167{3168print_precontext(g, S, 1);3169str_puts(S, GSTR(str));3170print_context(g, T, S ,1);3171}3172else3173{3174str_putc(S,'('); str_puts(S,GSTR(gel(str,1)));3175str_puts(S,")\\mapsto ");3176print_context(g, T, S ,1); str_puts(S,GSTR(gel(str,2)));3177}3178}3179else3180{3181str_puts(S,"\\{\""); str_puts(S,GSTR(closure_get_code(g)));3182str_puts(S,"\","); texi(gel(g,3),T,S);3183str_putc(S,','); texi(gel(g,4),T,S);3184str_putc(S,','); texi(gel(g,5),T,S); str_puts(S,"\\}");3185}3186break;3187case t_INFINITY: str_puts(S, inf_get_sign(g) == 1? "+\\infty": "-\\infty");3188break;31893190case t_MAT:3191{3192str_puts(S, "\\pmatrix{\n "); r = lg(g);3193if (r>1)3194{3195OUT_FUN print = (typ(gel(g,1)) == t_VECSMALL)? prints: texi;31963197l = lgcols(g);3198for (i=1; i<l; i++)3199{3200for (j=1; j<r; j++)3201{3202print(gcoeff(g,i,j),T,S); if (j<r-1) str_putc(S, '&');3203}3204str_puts(S, "\\cr\n ");3205}3206}3207str_putc(S, '}'); break;3208}3209}3210}32113212/*******************************************************************/3213/** **/3214/** USER OUTPUT FUNCTIONS **/3215/** **/3216/*******************************************************************/3217static void3218_initout(pariout_t *T, char f, long sigd, long sp)3219{3220T->format = f;3221T->sigd = sigd;3222T->sp = sp;3223}32243225static void3226gen_output_fun(GEN x, pariout_t *T, OUT_FUN out)3227{ pari_sp av = avma; pari_puts( stack_GENtostr_fun(x,T,out) ); set_avma(av); }32283229void3230fputGEN_pariout(GEN x, pariout_t *T, FILE *out)3231{3232pari_sp av = avma;3233char *s = stack_GENtostr_fun(x, T, get_fun(T->prettyp));3234if (*s) { set_last_newline(s[strlen(s)-1]); fputs(s, out); }3235set_avma(av);3236}32373238void3239brute(GEN g, char f, long d)3240{3241pariout_t T; _initout(&T,f,d,0);3242gen_output_fun(g, &T, &bruti);3243}3244void3245matbrute(GEN g, char f, long d)3246{3247pariout_t T; _initout(&T,f,d,1);3248gen_output_fun(g, &T, &matbruti);3249}3250void3251texe(GEN g, char f, long d)3252{3253pariout_t T; _initout(&T,f,d,0);3254gen_output_fun(g, &T, &texi);3255}32563257void3258gen_output(GEN x)3259{3260gen_output_fun(x, GP_DATA->fmt, get_fun(GP_DATA->fmt->prettyp));3261pari_putc('\n'); pari_flush();3262}3263void3264output(GEN x)3265{ brute(x,'g',-1); pari_putc('\n'); pari_flush(); }3266void3267outmat(GEN x)3268{ matbrute(x,'g',-1); pari_putc('\n'); pari_flush(); }32693270/*******************************************************************/3271/** FILES **/3272/*******************************************************************/3273/* to cache '~' expansion */3274static char *homedir;3275/* last file read successfully from try_name() */3276static THREAD char *last_filename;3277/* stack of temporary files (includes all infiles + some output) */3278static THREAD pariFILE *last_tmp_file;3279/* stack of "permanent" (output) files */3280static THREAD pariFILE *last_file;32813282typedef struct gpfile3283{3284const char *name;3285FILE *fp;3286int type;3287long serial;3288} gpfile;32893290static THREAD gpfile *gp_file;3291static THREAD pari_stack s_gp_file;3292static THREAD long gp_file_serial;32933294#if defined(UNIX) || defined(__EMX__)3295# include <fcntl.h>3296# include <sys/stat.h> /* for open */3297# ifdef __EMX__3298# include <process.h>3299# endif3300# define HAVE_PIPES3301#endif3302#if defined(_WIN32)3303# define HAVE_PIPES3304#endif3305#ifndef O_RDONLY3306# define O_RDONLY 03307#endif33083309pariFILE *3310newfile(FILE *f, const char *name, int type)3311{3312pariFILE *file = (pariFILE*) pari_malloc(strlen(name) + 1 + sizeof(pariFILE));3313file->type = type;3314file->name = strcpy((char*)(file+1), name);3315file->file = f;3316file->next = NULL;3317if (type & mf_PERM)3318{3319file->prev = last_file;3320last_file = file;3321}3322else3323{3324file->prev = last_tmp_file;3325last_tmp_file = file;3326}3327if (file->prev) (file->prev)->next = file;3328if (DEBUGFILES)3329err_printf("I/O: new pariFILE %s (code %d) \n",name,type);3330return file;3331}33323333static void3334pari_kill_file(pariFILE *f)3335{3336if ((f->type & mf_PIPE) == 0)3337{3338if (f->file != stdin && fclose(f->file))3339pari_warn(warnfile, "close", f->name);3340}3341#ifdef HAVE_PIPES3342else3343{3344if (f->type & mf_FALSE)3345{3346if (f->file != stdin && fclose(f->file))3347pari_warn(warnfile, "close", f->name);3348if (unlink(f->name)) pari_warn(warnfile, "delete", f->name);3349}3350else3351if (pclose(f->file) < 0) pari_warn(warnfile, "close pipe", f->name);3352}3353#endif3354if (DEBUGFILES)3355err_printf("I/O: closing file %s (code %d) \n",f->name,f->type);3356pari_free(f);3357}33583359void3360pari_fclose(pariFILE *f)3361{3362if (f->next) (f->next)->prev = f->prev;3363else if (f == last_tmp_file) last_tmp_file = f->prev;3364else if (f == last_file) last_file = f->prev;3365if (f->prev) (f->prev)->next = f->next;3366pari_kill_file(f);3367}33683369static pariFILE *3370pari_open_file(FILE *f, const char *s, const char *mode)3371{3372if (!f) pari_err_FILE("requested file", s);3373if (DEBUGFILES)3374err_printf("I/O: opening file %s (mode %s)\n", s, mode);3375return newfile(f,s,0);3376}33773378pariFILE *3379pari_fopen_or_fail(const char *s, const char *mode)3380{3381return pari_open_file(fopen(s, mode), s, mode);3382}3383pariFILE *3384pari_fopen(const char *s, const char *mode)3385{3386FILE *f = fopen(s, mode);3387return f? pari_open_file(f, s, mode): NULL;3388}33893390void3391pari_fread_chars(void *b, size_t n, FILE *f)3392{3393if (fread(b, sizeof(char), n, f) < n)3394pari_err_FILE("input file [fread]", "FILE*");3395}33963397/* FIXME: HAS_FDOPEN & allow standard open() flags */3398#ifdef UNIX3399/* open tmpfile s (a priori for writing) avoiding symlink attacks */3400pariFILE *3401pari_safefopen(const char *s, const char *mode)3402{3403long fd = open(s, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);34043405if (fd == -1) pari_err(e_MISC,"tempfile %s already exists",s);3406return pari_open_file(fdopen(fd, mode), s, mode);3407}3408#else3409pariFILE *3410pari_safefopen(const char *s, const char *mode)3411{3412return pari_fopen_or_fail(s, mode);3413}3414#endif34153416void3417pari_unlink(const char *s)3418{3419if (unlink(s)) pari_warn(warner, "I/O: can\'t remove file %s", s);3420else if (DEBUGFILES)3421err_printf("I/O: removed file %s\n", s);3422}34233424/* Remove one INFILE from the stack. Reset pari_infile (to the most recent3425* infile)3426* Return -1, if we're trying to pop out stdin itself; 0 otherwise3427* Check for leaked file handlers (temporary files) */3428int3429popinfile(void)3430{3431pariFILE *f = last_tmp_file, *g;3432while (f)3433{3434if (f->type & mf_IN) break;3435pari_warn(warner, "I/O: leaked file descriptor (%d): %s", f->type, f->name);3436g = f; f = f->prev; pari_fclose(g);3437}3438last_tmp_file = f; if (!f) return -1;3439pari_fclose(last_tmp_file);3440for (f = last_tmp_file; f; f = f->prev)3441if (f->type & mf_IN) { pari_infile = f->file; return 0; }3442pari_infile = stdin; return 0;3443}34443445/* delete all "temp" files open since last reference point F */3446void3447tmp_restore(pariFILE *F)3448{3449pariFILE *f = last_tmp_file;3450if (DEBUGFILES>1) err_printf("gp_context_restore: deleting open files...\n");3451while (f)3452{3453pariFILE *g = f->prev;3454if (f == F) break;3455pari_fclose(f); f = g;3456}3457for (; f; f = f->prev) {3458if (f->type & mf_IN) {3459pari_infile = f->file;3460if (DEBUGFILES>1)3461err_printf("restoring pari_infile to %s\n", f->name);3462break;3463}3464}3465if (!f) {3466pari_infile = stdin;3467if (DEBUGFILES>1)3468err_printf("gp_context_restore: restoring pari_infile to stdin\n");3469}3470if (DEBUGFILES>1) err_printf("done\n");3471}34723473void3474filestate_save(struct pari_filestate *file)3475{3476file->file = last_tmp_file;3477file->serial = gp_file_serial;3478}34793480static void3481filestate_close(long serial)3482{3483long i;3484for (i = 0; i < s_gp_file.n; i++)3485if (gp_file[i].fp && gp_file[i].serial >= serial)3486gp_fileclose(i);3487gp_file_serial = serial;3488}34893490void3491filestate_restore(struct pari_filestate *file)3492{3493tmp_restore(file->file);3494filestate_close(file->serial);3495}34963497static void3498kill_file_stack(pariFILE **s)3499{3500pariFILE *f = *s;3501while (f)3502{3503pariFILE *t = f->prev;3504pari_kill_file(f);3505*s = f = t; /* have to update *s in case of ^C */3506}3507}35083509void3510killallfiles(void)3511{3512kill_file_stack(&last_tmp_file);3513pari_infile = stdin;3514}35153516void3517pari_init_homedir(void)3518{3519homedir = NULL;3520}35213522void3523pari_close_homedir(void)3524{3525if (homedir) pari_free(homedir);3526}35273528void3529pari_init_files(void)3530{3531last_filename = NULL;3532last_tmp_file = NULL;3533last_file=NULL;3534pari_stack_init(&s_gp_file, sizeof(*gp_file), (void**)&gp_file);3535gp_file_serial = 0;3536}35373538void3539pari_thread_close_files(void)3540{3541popinfile(); /* look for leaks */3542kill_file_stack(&last_file);3543if (last_filename) pari_free(last_filename);3544kill_file_stack(&last_tmp_file);3545filestate_close(-1);3546pari_stack_delete(&s_gp_file);3547}35483549void3550pari_close_files(void)3551{3552if (pari_logfile) { fclose(pari_logfile); pari_logfile = NULL; }3553pari_infile = stdin;3554}35553556static int3557ok_pipe(FILE *f)3558{3559if (DEBUGFILES) err_printf("I/O: checking output pipe...\n");3560pari_CATCH(CATCH_ALL) {3561return 0;3562}3563pari_TRY {3564int i;3565fprintf(f,"\n\n"); fflush(f);3566for (i=1; i<1000; i++) fprintf(f," \n");3567fprintf(f,"\n"); fflush(f);3568} pari_ENDCATCH;3569return 1;3570}35713572pariFILE *3573try_pipe(const char *cmd, int fl)3574{3575#ifndef HAVE_PIPES3576pari_err(e_ARCH,"pipes");3577return NULL;/*LCOV_EXCL_LINE*/3578#else3579FILE *file;3580const char *f;3581VOLATILE int flag = fl;35823583# ifdef __EMX__3584if (_osmode == DOS_MODE) /* no pipes under DOS */3585{3586pari_sp av = avma;3587char *s;3588if (flag & mf_OUT) pari_err(e_ARCH,"pipes");3589f = pari_unique_filename("pipe");3590s = stack_malloc(strlen(cmd)+strlen(f)+4);3591sprintf(s,"%s > %s",cmd,f);3592file = system(s)? NULL: fopen(f,"r");3593flag |= mf_FALSE; pari_free(f); set_avma(av);3594}3595else3596# endif3597{3598file = (FILE *) popen(cmd, (flag & mf_OUT)? "w": "r");3599if (flag & mf_OUT) {3600if (!ok_pipe(file)) return NULL;3601flag |= mf_PERM;3602}3603f = cmd;3604}3605if (!file) pari_err(e_MISC,"[pipe:] '%s' failed",cmd);3606return newfile(file, f, mf_PIPE|flag);3607#endif3608}36093610char *3611os_getenv(const char *s)3612{3613#ifdef HAS_GETENV3614return getenv(s);3615#else3616(void) s; return NULL;3617#endif3618}36193620GEN3621gp_getenv(const char *s)3622{3623char *t = os_getenv(s);3624return t?strtoGENstr(t):gen_0;3625}36263627/* FIXME: HAS_GETPWUID */3628#if defined(UNIX) || defined(__EMX__)3629#include <pwd.h>3630#include <sys/types.h>3631/* user = "": use current uid */3632char *3633pari_get_homedir(const char *user)3634{3635struct passwd *p;3636char *dir = NULL;36373638if (!*user)3639{3640if (homedir) dir = homedir;3641else3642{3643p = getpwuid(geteuid());3644if (p)3645{3646dir = p->pw_dir;3647homedir = pari_strdup(dir); /* cache result */3648}3649}3650}3651else3652{3653p = getpwnam(user);3654if (p) dir = p->pw_dir;3655/* warn, but don't kill session on startup (when expanding path) */3656if (!dir) pari_warn(warner,"can't expand ~%s", user? user: "");3657}3658return dir;3659}3660#else3661char *3662pari_get_homedir(const char *user) { (void) user; return NULL; }3663#endif36643665/*******************************************************************/3666/** **/3667/** GP STANDARD INPUT AND OUTPUT **/3668/** **/3669/*******************************************************************/3670#ifdef HAS_OPENDIR3671/* slow, but more portable than stat + S_ISDIR */3672static int3673is_dir_opendir(const char *name)3674{3675DIR *d = opendir(name);3676if (d) { (void)closedir(d); return 1; }3677return 0;3678}3679#endif36803681#ifdef HAS_STAT3682static int3683is_dir_stat(const char *name)3684{3685struct stat buf;3686if (stat(name, &buf)) return 0;3687return S_ISDIR(buf.st_mode);3688}3689#endif36903691/* Does name point to a directory? */3692int3693pari_is_dir(const char *name)3694{3695#ifdef HAS_STAT3696return is_dir_stat(name);3697#else3698# ifdef HAS_OPENDIR3699return is_dir_opendir(name);3700# else3701(void) name; return 0;3702# endif3703#endif3704}37053706/* Does name point to a regular file? */3707/* If unknown, assume that it is indeed regular. */3708int3709pari_is_file(const char *name)3710{3711#ifdef HAS_STAT3712struct stat buf;3713if (stat(name, &buf)) return 1;3714return S_ISREG(buf.st_mode);3715#else3716(void) name; return 1;3717#endif3718}37193720int3721pari_stdin_isatty(void)3722{3723#ifdef HAS_ISATTY3724return isatty( fileno(stdin) );3725#else3726return 1;3727#endif3728}37293730/* expand tildes in filenames, return a malloc'ed buffer */3731static char *3732_path_expand(const char *s)3733{3734const char *t;3735char *ret, *dir = NULL;37363737if (*s != '~') return pari_strdup(s);3738s++; /* skip ~ */3739t = s; while (*t && *t != '/') t++;3740if (t == s)3741dir = pari_get_homedir("");3742else3743{3744char *user = pari_strndup(s, t - s);3745dir = pari_get_homedir(user);3746pari_free(user);3747}3748if (!dir) return pari_strdup(s);3749ret = (char*)pari_malloc(strlen(dir) + strlen(t) + 1);3750sprintf(ret,"%s%s",dir,t); return ret;3751}37523753/* expand environment variables in str, return a malloc'ed buffer3754* assume no \ remain and str can be freed */3755static char *3756_expand_env(char *str)3757{3758long i, l, len = 0, xlen = 16, xnum = 0;3759char *s = str, *s0 = s;3760char **x = (char **)pari_malloc(xlen * sizeof(char*));37613762while (*s)3763{3764char *env;3765if (*s != '$') { s++; continue; }3766l = s - s0;3767if (l) { x[xnum++] = pari_strndup(s0, l); len += l; }3768if (xnum > xlen - 3) /* need room for possibly two more elts */3769{3770xlen <<= 1;3771pari_realloc_ip((void**)&x, xlen * sizeof(char*));3772}37733774s0 = ++s; /* skip $ */3775while (is_keyword_char(*s)) s++;3776l = s - s0; env = pari_strndup(s0, l);3777s0 = os_getenv(env);3778if (!s0) pari_warn(warner,"undefined environment variable: %s",env);3779else3780{3781l = strlen(s0);3782if (l) { x[xnum++] = pari_strndup(s0,l); len += l; }3783}3784pari_free(env); s0 = s;3785}3786l = s - s0;3787if (l) { x[xnum++] = pari_strndup(s0,l); len += l; }37883789s = (char*)pari_malloc(len+1); *s = 0;3790for (i = 0; i < xnum; i++) { (void)strcat(s, x[i]); pari_free(x[i]); }3791pari_free(str); pari_free(x); return s;3792}37933794char *3795path_expand(const char *s)3796{3797#ifdef _WIN323798char *ss, *p;3799ss = pari_strdup(s);3800for (p = ss; *p != 0; ++p)3801if (*p == '\\') *p = '/';3802p = _expand_env(_path_expand(ss));3803pari_free(ss);3804return p;3805#else3806return _expand_env(_path_expand(s));3807#endif3808}38093810#ifdef HAS_STRFTIME3811# include <time.h>3812void3813strftime_expand(const char *s, char *buf, long max)3814{3815time_t t;3816BLOCK_SIGINT_START3817t = time(NULL);3818(void)strftime(buf,max,s,localtime(&t));3819BLOCK_SIGINT_END3820}3821#else3822void3823strftime_expand(const char *s, char *buf, long max)3824{ strcpy(buf,s); }3825#endif38263827/* name is a malloc'ed (existing) filename. Accept it as new pari_infile3828* (unzip if needed). */3829static pariFILE *3830pari_get_infile(const char *name, FILE *file)3831{3832#ifdef ZCAT3833long l = strlen(name);3834const char *end = name + l-1;38353836if (l > 2 && (!strncmp(end-1,".Z",2)3837#ifdef GNUZCAT3838|| !strncmp(end-2,".gz",3)3839#endif3840))3841{ /* compressed file (compress or gzip) */3842char *cmd = stack_malloc(strlen(ZCAT) + l + 4);3843sprintf(cmd,"%s \"%s\"",ZCAT,name);3844fclose(file);3845return try_pipe(cmd, mf_IN);3846}3847#endif3848return newfile(file, name, mf_IN);3849}38503851pariFILE *3852pari_fopengz(const char *s)3853{3854pari_sp av = avma;3855char *name;3856long l;3857FILE *f = fopen(s, "r");3858pariFILE *pf;38593860if (f) return pari_get_infile(s, f);38613862#ifdef __EMSCRIPTEN__3863if (pari_is_dir(pari_datadir)) pari_emscripten_wget(s);3864#endif3865l = strlen(s);3866name = stack_malloc(l + 3 + 1);3867strcpy(name, s); (void)sprintf(name + l, ".gz");3868f = fopen(name, "r");3869pf = f ? pari_get_infile(name, f): NULL;3870set_avma(av); return pf;3871}38723873static FILE*3874try_open(char *s)3875{3876if (!pari_is_dir(s)) return fopen(s, "r");3877pari_warn(warner,"skipping directory %s",s);3878return NULL;3879}38803881void3882forpath_init(forpath_t *T, gp_path *path, const char *s)3883{3884T->s = s;3885T->ls = strlen(s);3886T->dir = path->dirs;3887}3888char *3889forpath_next(forpath_t *T)3890{3891char *t, *dir = T->dir[0];38923893if (!dir) return NULL; /* done */3894/* room for dir + '/' + s + '\0' */3895t = (char*)pari_malloc(strlen(dir) + T->ls + 2);3896if (!t) return NULL; /* can't happen but kills a warning */3897sprintf(t,"%s/%s", dir, T->s);3898T->dir++; return t;3899}39003901/* If a file called "name" exists (possibly after appending ".gp")3902* record it in the file_stack (as a pipe if compressed).3903* name is malloc'ed, we free it before returning3904*/3905static FILE *3906try_name(char *name)3907{3908pari_sp av = avma;3909char *s = name;3910FILE *file = try_open(name);39113912if (!file)3913{ /* try appending ".gp" to name */3914s = stack_malloc(strlen(name)+4);3915sprintf(s, "%s.gp", name);3916file = try_open(s);3917}3918if (file)3919{3920if (! last_tmp_file)3921{ /* empty file stack, record this name */3922if (last_filename) pari_free(last_filename);3923last_filename = pari_strdup(s);3924}3925file = pari_infile = pari_get_infile(s,file)->file;3926}3927pari_free(name); set_avma(av);3928return file;3929}3930static FILE *3931switchin_last(void)3932{3933char *s = last_filename;3934FILE *file;3935if (!s) pari_err(e_MISC,"You never gave me anything to read!");3936file = try_open(s);3937if (!file) pari_err_FILE("input file",s);3938return pari_infile = pari_get_infile(s,file)->file;3939}39403941/* return 1 if s starts by '/' or './' or '../' */3942static int3943path_is_absolute(char *s)3944{3945#ifdef _WIN323946if( (*s >= 'A' && *s <= 'Z') ||3947(*s >= 'a' && *s <= 'z') )3948{3949return *(s+1) == ':';3950}3951#endif3952if (*s == '/') return 1;3953if (*s++ != '.') return 0;3954if (*s == '/') return 1;3955if (*s++ != '.') return 0;3956return *s == '/';3957}39583959/* If name = "", re-read last file */3960FILE *3961switchin(const char *name)3962{3963FILE *f;3964char *s;39653966if (!*name) return switchin_last();3967s = path_expand(name);3968/* if s is an absolute path, don't use dir_list */3969if (path_is_absolute(s)) { if ((f = try_name(s))) return f; }3970else3971{3972char *t;3973forpath_t T;3974forpath_init(&T, GP_DATA->path, s);3975while ( (t = forpath_next(&T)) )3976if ((f = try_name(t))) { pari_free(s); return f; }3977pari_free(s);3978}3979pari_err_FILE("input file",name);3980return NULL; /*LCOV_EXCL_LINE*/3981}39823983static int is_magic_ok(FILE *f);39843985static FILE *3986switchout_get_FILE(const char *name)3987{3988FILE* f;3989/* only for ordinary files (to avoid blocking on pipes). */3990if (pari_is_file(name))3991{3992f = fopen(name, "r");3993if (f)3994{3995int magic = is_magic_ok(f);3996fclose(f);3997if (magic) pari_err_FILE("binary output file [ use writebin ! ]", name);3998}3999}4000f = fopen(name, "a");4001if (!f) pari_err_FILE("output file",name);4002return f;4003}40044005void4006switchout(const char *name)4007{4008if (name)4009pari_outfile = switchout_get_FILE(name);4010else if (pari_outfile != stdout)4011{4012fclose(pari_outfile);4013pari_outfile = stdout;4014}4015}40164017/*******************************************************************/4018/** **/4019/** SYSTEM, READSTR/EXTERNSTR/EXTERN **/4020/** **/4021/*******************************************************************/4022static void4023check_secure(const char *s)4024{4025if (GP_DATA->secure)4026pari_err(e_MISC, "[secure mode]: system commands not allowed\nTried to run '%s'",s);4027}40284029void4030gpsystem(const char *s)4031{4032#ifdef HAS_SYSTEM4033check_secure(s);4034if (system(s) < 0)4035pari_err(e_MISC, "system(\"%s\") failed", s);4036#else4037pari_err(e_ARCH,"system");4038#endif4039}40404041static GEN4042get_lines(FILE *F)4043{4044pari_sp av = avma;4045long i, nz = 16;4046GEN z = cgetg(nz + 1, t_VEC);4047Buffer *b = new_buffer();4048input_method IM;4049IM.myfgets = (fgets_t)&fgets;4050IM.file = (void*)F;4051for(i = 1;;)4052{4053char *s = b->buf, *e;4054if (!file_getline(b, &s, &IM)) break;4055if (i > nz) { nz <<= 1; z = vec_lengthen(z, nz); }4056e = s + strlen(s)-1;4057if (*e == '\n') *e = 0;4058gel(z,i++) = strtoGENstr(s);4059}4060delete_buffer(b); setlg(z, i);4061return gerepilecopy(av, z);4062}40634064GEN4065externstr(const char *s)4066{4067pariFILE *F;4068GEN z;4069check_secure(s);4070F = try_pipe(s, mf_IN);4071z = get_lines(F->file);4072pari_fclose(F); return z;4073}4074GEN4075gpextern(const char *s)4076{4077pariFILE *F;4078GEN z;4079check_secure(s);4080F = try_pipe(s, mf_IN);4081z = gp_read_stream(F->file);4082pari_fclose(F); return z ? z : gnil;4083}40844085GEN4086readstr(const char *s)4087{4088GEN z = get_lines(switchin(s));4089popinfile(); return z;4090}40914092/*******************************************************************/4093/** **/4094/** I/O IN BINARY FORM **/4095/** **/4096/*******************************************************************/4097static void4098pari_fread_longs(void *a, size_t c, FILE *d)4099{ if (fread(a,sizeof(long),c,d) < c)4100pari_err_FILE("input file [fread]", "FILE*"); }41014102static void4103_fwrite(const void *a, size_t b, size_t c, FILE *d)4104{ if (fwrite(a,b,c,d) < c) pari_err_FILE("output file [fwrite]", "FILE*"); }4105static void4106_lfwrite(const void *a, size_t b, FILE *c) { _fwrite(a,sizeof(long),b,c); }4107static void4108_cfwrite(const void *a, size_t b, FILE *c) { _fwrite(a,sizeof(char),b,c); }41094110enum { BIN_GEN, NAM_GEN, VAR_GEN, RELINK_TABLE };41114112static long4113rd_long(FILE *f) { long L; pari_fread_longs(&L, 1UL, f); return L; }4114static void4115wr_long(long L, FILE *f) { _lfwrite(&L, 1UL, f); }41164117/* append x to file f */4118static void4119wrGEN(GEN x, FILE *f)4120{4121GENbin *p = copy_bin_canon(x);4122size_t L = p->len;41234124wr_long(L,f);4125if (L)4126{4127wr_long((long)p->x,f);4128wr_long((long)p->base,f);4129_lfwrite(GENbinbase(p), L,f);4130}4131pari_free((void*)p);4132}41334134static void4135wrstr(const char *s, FILE *f)4136{4137size_t L = strlen(s)+1;4138wr_long(L,f);4139_cfwrite(s, L, f);4140}41414142static char *4143rdstr(FILE *f)4144{4145size_t L = (size_t)rd_long(f);4146char *s;4147if (!L) return NULL;4148s = (char*)pari_malloc(L);4149pari_fread_chars(s, L, f); return s;4150}41514152static void4153writeGEN(GEN x, FILE *f)4154{4155fputc(BIN_GEN,f);4156wrGEN(x, f);4157}41584159static void4160writenamedGEN(GEN x, const char *s, FILE *f)4161{4162fputc(x ? NAM_GEN : VAR_GEN,f);4163wrstr(s, f);4164if (x) wrGEN(x, f);4165}41664167/* read a GEN from file f */4168static GEN4169rdGEN(FILE *f)4170{4171size_t L = (size_t)rd_long(f);4172GENbin *p;41734174if (!L) return gen_0;4175p = (GENbin*)pari_malloc(sizeof(GENbin) + L*sizeof(long));4176p->len = L;4177p->x = (GEN)rd_long(f);4178p->base = (GEN)rd_long(f);4179p->rebase = &shiftaddress_canon;4180pari_fread_longs(GENbinbase(p), L,f);4181return bin_copy(p);4182}41834184/* read a binary object in file f. Set *ptc to the object "type":4185* BIN_GEN: an anonymous GEN x; return x.4186* NAM_GEN: a named GEN x, with name v; set 'v to x (changevalue) and return x4187* VAR_GEN: a name v; create the (unassigned) variable v and return gnil4188* RELINK_TABLE: a relinking table for gen_relink(), to replace old adresses4189* in * the original session by new incarnations in the current session.4190* H is the current relinking table4191* */4192static GEN4193readobj(FILE *f, int *ptc, hashtable *H)4194{4195int c = fgetc(f);4196GEN x = NULL;4197switch(c)4198{4199case BIN_GEN:4200x = rdGEN(f);4201if (H) gen_relink(x, H);4202break;4203case NAM_GEN:4204case VAR_GEN:4205{4206char *s = rdstr(f);4207if (!s) pari_err(e_MISC,"malformed binary file (no name)");4208if (c == NAM_GEN)4209{4210x = rdGEN(f);4211if (H) gen_relink(x, H);4212err_printf("setting %s\n",s);4213changevalue(varentries[fetch_user_var(s)], x);4214}4215else4216{4217pari_var_create(fetch_entry(s));4218x = gnil;4219}4220break;4221}4222case RELINK_TABLE:4223x = rdGEN(f); break;4224case EOF: break;4225default: pari_err(e_MISC,"unknown code in readobj");4226}4227*ptc = c; return x;4228}42294230#define MAGIC "\020\001\022\011-\007\020" /* ^P^A^R^I-^G^P */4231#ifdef LONG_IS_64BIT4232# define ENDIAN_CHECK 0x0102030405060708L4233#else4234# define ENDIAN_CHECK 0x01020304L4235#endif4236static const long BINARY_VERSION = 1; /* since 2.2.9 */42374238static int4239is_magic_ok(FILE *f)4240{4241pari_sp av = avma;4242size_t L = strlen(MAGIC);4243char *s = stack_malloc(L);4244int r = (fread(s,1,L, f) == L && strncmp(s,MAGIC,L) == 0);4245set_avma(av); return r;4246}42474248static int4249is_sizeoflong_ok(FILE *f)4250{4251char c;4252return (fread(&c,1,1, f) == 1 && c == (char)sizeof(long));4253}42544255static int4256is_long_ok(FILE *f, long L)4257{4258long c;4259return (fread(&c,sizeof(long),1, f) == 1 && c == L);4260}42614262/* return 1 if valid binary file */4263static int4264check_magic(const char *name, FILE *f)4265{4266if (!is_magic_ok(f))4267pari_warn(warner, "%s is not a GP binary file",name);4268else if (!is_sizeoflong_ok(f))4269pari_warn(warner, "%s not written for a %ld bit architecture",4270name, sizeof(long)*8);4271else if (!is_long_ok(f, ENDIAN_CHECK))4272pari_warn(warner, "unexpected endianness in %s",name);4273else if (!is_long_ok(f, BINARY_VERSION))4274pari_warn(warner, "%s written by an incompatible version of GP",name);4275else return 1;4276return 0;4277}42784279static void4280write_magic(FILE *f)4281{4282fprintf(f, MAGIC);4283fprintf(f, "%c", (char)sizeof(long));4284wr_long(ENDIAN_CHECK, f);4285wr_long(BINARY_VERSION, f);4286}42874288int4289file_is_binary(FILE *f)4290{4291int r, c = fgetc(f);4292ungetc(c,f);4293r = (c != EOF && isprint(c) == 0 && isspace(c) == 0);4294#ifdef _WIN324295if (r) { setmode(fileno(f), _O_BINARY); rewind(f); }4296#endif4297return r;4298}42994300void4301writebin(const char *name, GEN x)4302{4303FILE *f = fopen(name,"rb");4304pari_sp av = avma;4305GEN V;4306int already = f? 1: 0;43074308if (f) {4309int ok = check_magic(name,f);4310fclose(f);4311if (!ok) pari_err_FILE("binary output file",name);4312}4313f = fopen(name,"ab");4314if (!f) pari_err_FILE("binary output file",name);4315if (!already) write_magic(f);43164317V = copybin_unlink(x);4318if (lg(gel(V,1)) > 1)4319{4320fputc(RELINK_TABLE,f);4321wrGEN(V, f);4322}4323if (x) writeGEN(x,f);4324else4325{4326long v, maxv = pari_var_next();4327for (v=0; v<maxv; v++)4328{4329entree *ep = varentries[v];4330if (!ep) continue;4331writenamedGEN((GEN)ep->value,ep->name,f);4332}4333}4334set_avma(av); fclose(f);4335}43364337/* read all objects in f. If f contains BIN_GEN that would be silently ignored4338* [i.e f contains more than one objet, not all of them 'named GENs'], return4339* them all in a vector and set 'vector'. */4340GEN4341readbin(const char *name, FILE *f, int *vector)4342{4343pari_sp av = avma;4344hashtable *H = NULL;4345pari_stack s_obj;4346GEN obj, x, y;4347int cy;4348if (vector) *vector = 0;4349if (!check_magic(name,f)) return NULL;4350pari_stack_init(&s_obj, sizeof(GEN), (void**)&obj);4351/* HACK: push codeword so as to be able to treat s_obj.data as a t_VEC */4352pari_stack_pushp(&s_obj, (void*) (evaltyp(t_VEC)|evallg(1)));4353x = gnil;4354while ((y = readobj(f, &cy, H)))4355{4356x = y;4357switch(cy)4358{4359case BIN_GEN:4360pari_stack_pushp(&s_obj, (void*)y); break;4361case RELINK_TABLE:4362if (H) hash_destroy(H);4363H = hash_from_link(gel(y,1),gel(y,2), 0);4364}4365}4366if (H) hash_destroy(H);4367switch(s_obj.n) /* >= 1 */4368{4369case 1: break; /* nothing but the codeword */4370case 2: x = gel(obj,1); break; /* read a single BIN_GEN */4371default: /* more than one BIN_GEN */4372setlg(obj, s_obj.n);4373if (DEBUGLEVEL)4374pari_warn(warner,"%ld unnamed objects read. Returning then in a vector",4375s_obj.n - 1);4376x = gerepilecopy(av, obj);4377if (vector) *vector = 1;4378}4379pari_stack_delete(&s_obj);4380return x;4381}43824383/*******************************************************************/4384/** **/4385/** GP I/O **/4386/** **/4387/*******************************************************************/4388/* print a vector of GENs, in output context 'out', using 'sep' as a4389* separator between sucessive entries [ NULL = no separator ]*/4390void4391out_print0(PariOUT *out, const char *sep, GEN g, long flag)4392{4393pari_sp av = avma;4394OUT_FUN f = get_fun(flag);4395long i, l = lg(g);4396for (i = 1; i < l; i++, set_avma(av))4397{4398out_puts(out, stack_GENtostr_fun_unquoted(gel(g,i), GP_DATA->fmt, f));4399if (sep && i+1 < l) out_puts(out, sep);4400}4401}4402static void4403str_print0(pari_str *S, GEN g, long flag)4404{4405pari_sp av = avma;4406OUT_FUN f = get_fun(flag);4407long i, l = lg(g);4408for (i = 1; i < l; i++)4409{4410GEN x = gel(g,i);4411if (typ(x) == t_STR) str_puts(S, GSTR(x)); else f(x, GP_DATA->fmt, S);4412if (!S->use_stack) set_avma(av);4413}4414*(S->cur) = 0;4415}44164417/* see print0(). Returns pari_malloc()ed string */4418char *4419RgV_to_str(GEN g, long flag)4420{4421pari_str S; str_init(&S,0);4422str_print0(&S, g, flag);4423return S.string;4424}44254426static GEN4427Str_fun(GEN g, long flag) {4428char *t = RgV_to_str(g, flag);4429GEN z = strtoGENstr(t);4430pari_free(t); return z;4431}4432GEN Str(GEN g) { return Str_fun(g, f_RAW); }4433GEN strtex(GEN g) { return Str_fun(g, f_TEX); }4434GEN4435strexpand(GEN g) {4436char *s = RgV_to_str(g, f_RAW), *t = path_expand(s);4437GEN z = strtoGENstr(t);4438pari_free(t); pari_free(s); return z;4439}44404441/* display s, followed by the element of g */4442char *4443pari_sprint0(const char *s, GEN g, long flag)4444{4445pari_str S; str_init(&S, 0);4446str_puts(&S, s);4447str_print0(&S, g, flag);4448return S.string;4449}44504451static void4452print0_file(FILE *out, GEN g, long flag)4453{4454pari_sp av = avma;4455pari_str S; str_init(&S, 1);4456str_print0(&S, g, flag);4457fputs(S.string, out);4458set_avma(av);4459}44604461void4462print0(GEN g, long flag) { out_print0(pariOut, NULL, g, flag); }4463void4464printsep(const char *s, GEN g)4465{ out_print0(pariOut, s, g, f_RAW); pari_putc('\n'); pari_flush(); }4466void4467printsep1(const char *s, GEN g)4468{ out_print0(pariOut, s, g, f_RAW); pari_flush(); }44694470static char *4471sm_dopr(const char *fmt, GEN arg_vector, va_list args)4472{4473pari_str s; str_init(&s, 0);4474str_arg_vprintf(&s, fmt, arg_vector, args);4475return s.string;4476}4477char *4478pari_vsprintf(const char *fmt, va_list ap)4479{ return sm_dopr(fmt, NULL, ap); }44804481/* dummy needed to pass an empty va_list to sm_dopr */4482static char *4483dopr_arg_vector(GEN arg_vector, const char* fmt, ...)4484{4485va_list ap;4486char *s;4487va_start(ap, fmt);4488s = sm_dopr(fmt, arg_vector, ap);4489va_end(ap); return s;4490}4491/* GP only */4492void4493printf0(const char *fmt, GEN args)4494{ char *s = dopr_arg_vector(args, fmt);4495pari_puts(s); pari_free(s); pari_flush(); }4496/* GP only */4497GEN4498strprintf(const char *fmt, GEN args)4499{ char *s = dopr_arg_vector(args, fmt);4500GEN z = strtoGENstr(s); pari_free(s); return z; }45014502void4503out_vprintf(PariOUT *out, const char *fmt, va_list ap)4504{4505char *s = pari_vsprintf(fmt, ap);4506out_puts(out, s); pari_free(s);4507}4508void4509pari_vprintf(const char *fmt, va_list ap) { out_vprintf(pariOut, fmt, ap); }45104511void4512err_printf(const char* fmt, ...)4513{4514va_list args; va_start(args, fmt);4515out_vprintf(pariErr,fmt,args); va_end(args);4516}45174518/* variadic version of printf0 */4519void4520out_printf(PariOUT *out, const char *fmt, ...)4521{4522va_list args; va_start(args,fmt);4523out_vprintf(out,fmt,args); va_end(args);4524}4525void4526pari_printf(const char *fmt, ...) /* variadic version of printf0 */4527{4528va_list args; va_start(args,fmt);4529pari_vprintf(fmt,args); va_end(args);4530}45314532GEN4533gvsprintf(const char *fmt, va_list ap)4534{4535char *s = pari_vsprintf(fmt, ap);4536GEN z = strtoGENstr(s);4537pari_free(s); return z;4538}45394540char *4541pari_sprintf(const char *fmt, ...) /* variadic version of strprintf */4542{4543char *s;4544va_list ap;4545va_start(ap, fmt);4546s = pari_vsprintf(fmt, ap);4547va_end(ap); return s;4548}45494550void4551str_printf(pari_str *S, const char *fmt, ...)4552{4553va_list ap; va_start(ap, fmt);4554str_arg_vprintf(S, fmt, NULL, ap);4555va_end(ap);4556}45574558char *4559stack_sprintf(const char *fmt, ...)4560{4561char *s, *t;4562va_list ap;4563va_start(ap, fmt);4564s = pari_vsprintf(fmt, ap);4565va_end(ap);4566t = stack_strdup(s);4567pari_free(s); return t;4568}45694570GEN4571gsprintf(const char *fmt, ...) /* variadic version of gvsprintf */4572{4573GEN s;4574va_list ap;4575va_start(ap, fmt);4576s = gvsprintf(fmt, ap);4577va_end(ap); return s;4578}45794580/* variadic version of fprintf0. FIXME: fprintf0 not yet available */4581void4582pari_vfprintf(FILE *file, const char *fmt, va_list ap)4583{4584char *s = pari_vsprintf(fmt, ap);4585fputs(s, file); pari_free(s);4586}4587void4588pari_fprintf(FILE *file, const char *fmt, ...)4589{4590va_list ap; va_start(ap, fmt);4591pari_vfprintf(file, fmt, ap); va_end(ap);4592}45934594void print (GEN g) { print0(g, f_RAW); pari_putc('\n'); pari_flush(); }4595void printp (GEN g) { print0(g, f_PRETTYMAT); pari_putc('\n'); pari_flush(); }4596void printtex(GEN g) { print0(g, f_TEX); pari_putc('\n'); pari_flush(); }4597void print1 (GEN g) { print0(g, f_RAW); pari_flush(); }45984599void4600error0(GEN g)4601{4602if (lg(g)==2 && typ(gel(g,1))==t_ERROR) pari_err(0, gel(g,1));4603else pari_err(e_USER, g);4604}46054606void warning0(GEN g) { pari_warn(warnuser, g); }46074608static void4609wr_check(const char *t) {4610if (GP_DATA->secure)4611{4612char *msg = pari_sprintf("[secure mode]: about to write to '%s'",t);4613pari_ask_confirm(msg);4614pari_free(msg);4615}4616}46174618/* write to file s */4619static void4620wr(const char *s, GEN g, long flag, int addnl)4621{4622char *t = path_expand(s);4623FILE *out;46244625wr_check(t);4626out = switchout_get_FILE(t);4627pari_free(t);4628print0_file(out, g, flag);4629if (addnl) fputc('\n', out);4630fflush(out);4631if (fclose(out)) pari_warn(warnfile, "close", t);4632}4633void write0 (const char *s, GEN g) { wr(s, g, f_RAW, 1); }4634void writetex(const char *s, GEN g) { wr(s, g, f_TEX, 1); }4635void write1 (const char *s, GEN g) { wr(s, g, f_RAW, 0); }4636void gpwritebin(const char *s, GEN x)4637{4638char *t = path_expand(s);4639wr_check(t); writebin(t, x); pari_free(t);4640}46414642/*******************************************************************/4643/** **/4644/** HISTORY HANDLING **/4645/** **/4646/*******************************************************************/4647/* history management function:4648* p > 0, called from %p or %#p4649* p <= 0, called from %` or %#` (|p| backquotes, possibly 0) */4650static gp_hist_cell *4651history(long p)4652{4653gp_hist *H = GP_DATA->hist;4654ulong t = H->total, s = H->size;4655gp_hist_cell *c;46564657if (!t) pari_err(e_MISC,"The result history is empty");46584659if (p <= 0) p += t; /* count |p| entries starting from last */4660if (p <= 0 || p <= (long)(t - s) || (ulong)p > t)4661{4662long pmin = (long)(t - s) + 1;4663if (pmin <= 0) pmin = 1;4664pari_err(e_MISC,"History result %%%ld not available [%%%ld-%%%lu]",4665p,pmin,t);4666}4667c = H->v + ((p-1) % s);4668if (!c->z)4669pari_err(e_MISC,"History result %%%ld has been deleted (histsize changed)", p);4670return c;4671}4672GEN4673pari_get_hist(long p) { return history(p)->z; }4674long4675pari_get_histtime(long p) { return history(p)->t; }4676long4677pari_get_histrtime(long p) { return history(p)->r; }4678GEN4679pari_histtime(long p) { return mkvec2s(history(p)->t, history(p)->r); }46804681void4682pari_add_hist(GEN x, long time, long rtime)4683{4684gp_hist *H = GP_DATA->hist;4685ulong i = H->total % H->size;4686H->total++;4687guncloneNULL(H->v[i].z);4688H->v[i].t = time;4689H->v[i].r = rtime;4690H->v[i].z = gclone(x);4691}46924693ulong4694pari_nb_hist(void)4695{4696return GP_DATA->hist->total;4697}46984699/*******************************************************************/4700/** **/4701/** TEMPORARY FILES **/4702/** **/4703/*******************************************************************/47044705#ifndef R_OK4706# define R_OK 44707# define W_OK 24708# define X_OK 14709# define F_OK 04710#endif47114712#ifdef __EMX__4713#include <io.h>4714static int4715unix_shell(void)4716{4717char *base, *sh = getenv("EMXSHELL");4718if (!sh) {4719sh = getenv("COMSPEC");4720if (!sh) return 0;4721}4722base = _getname(sh);4723return (stricmp (base, "cmd.exe") && stricmp (base, "4os2.exe")4724&& stricmp (base, "command.com") && stricmp (base, "4dos.com"));4725}4726#endif47274728/* check if s has rwx permissions for us */4729static int4730pari_is_rwx(const char *s)4731{4732/* FIXME: HAS_ACCESS */4733#if defined(UNIX) || defined (__EMX__)4734return access(s, R_OK | W_OK | X_OK) == 0;4735#else4736(void) s; return 1;4737#endif4738}47394740#if defined(UNIX) || defined (__EMX__)4741#include <sys/types.h>4742#include <sys/stat.h>4743static int4744pari_file_exists(const char *s)4745{4746int id = open(s, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);4747return id < 0 || close(id);4748}4749static int4750pari_dir_exists(const char *s) { return mkdir(s, 0777); }4751#elif defined(_WIN32)4752static int4753pari_file_exists(const char *s) { return GetFileAttributesA(s) != ~0UL; }4754static int4755pari_dir_exists(const char *s) { return mkdir(s); }4756#else4757static int4758pari_file_exists(const char *s) { return 0; }4759static int4760pari_dir_exists(const char *s) { return 0; }4761#endif47624763static char *4764env_ok(const char *s)4765{4766char *t = os_getenv(s);4767if (t && !pari_is_rwx(t))4768{4769pari_warn(warner,"%s is set (%s), but is not writable", s,t);4770t = NULL;4771}4772if (t && !pari_is_dir(t))4773{4774pari_warn(warner,"%s is set (%s), but is not a directory", s,t);4775t = NULL;4776}4777return t;4778}47794780static const char*4781pari_tmp_dir(void)4782{4783char *s;4784s = env_ok("GPTMPDIR"); if (s) return s;4785s = env_ok("TMPDIR"); if (s) return s;4786#if defined(_WIN32) || defined(__EMX__)4787s = env_ok("TMP"); if (s) return s;4788s = env_ok("TEMP"); if (s) return s;4789#endif4790#if defined(UNIX) || defined(__EMX__)4791if (pari_is_rwx("/tmp")) return "/tmp";4792if (pari_is_rwx("/var/tmp")) return "/var/tmp";4793#endif4794return ".";4795}47964797/* loop through 26^2 variants [suffix 'aa' to 'zz'] */4798static int4799get_file(char *buf, int test(const char *), const char *suf)4800{4801char c, d, *end = buf + strlen(buf) - 1;4802if (suf) end -= strlen(suf);4803for (d = 'a'; d <= 'z'; d++)4804{4805end[-1] = d;4806for (c = 'a'; c <= 'z'; c++)4807{4808*end = c;4809if (! test(buf)) return 1;4810if (DEBUGFILES) err_printf("I/O: file %s exists!\n", buf);4811}4812}4813return 0;4814}48154816#if defined(__EMX__) || defined(_WIN32)4817static void4818swap_slash(char *s)4819{4820#ifdef __EMX__4821if (!unix_shell())4822#endif4823{4824char *t;4825for (t=s; *t; t++)4826if (*t == '/') *t = '\\';4827}4828}4829#endif48304831/* s truncated to 8 chars, suf possibly NULL */4832static char *4833init_unique(const char *s, const char *suf)4834{4835const char *pre = pari_tmp_dir();4836char *buf, salt[64];4837size_t lpre, lsalt, lsuf;4838#ifdef UNIX4839sprintf(salt,"-%ld-%ld", (long)getuid(), (long)getpid());4840#else4841sprintf(salt,"-%ld", (long)time(NULL));4842#endif4843lsuf = suf? strlen(suf): 0;4844lsalt = strlen(salt);4845lpre = strlen(pre);4846/* room for prefix + '/' + s + salt + suf + '\0' */4847buf = (char*) pari_malloc(lpre + 1 + 8 + lsalt + lsuf + 1);4848strcpy(buf, pre);4849if (buf[lpre-1] != '/') { (void)strcat(buf, "/"); lpre++; }4850#if defined(__EMX__) || defined(_WIN32)4851swap_slash(buf);4852#endif4853sprintf(buf + lpre, "%.8s%s", s, salt);4854if (lsuf) strcat(buf, suf);4855if (DEBUGFILES) err_printf("I/O: prefix for unique file/dir = %s\n", buf);4856return buf;4857}48584859/* Return a "unique filename" built from the string s, possibly the user id4860* and the process pid (on Unix systems). A "temporary" directory name is4861* prepended. The name returned is pari_malloc'ed. It is DOS-safe4862* (s truncated to 8 chars) */4863char*4864pari_unique_filename_suffix(const char *s, const char *suf)4865{4866char *buf = init_unique(s, suf);4867if (pari_file_exists(buf) && !get_file(buf, pari_file_exists, suf))4868pari_err(e_MISC,"couldn't find a suitable name for a tempfile (%s)",s);4869return buf;4870}4871char*4872pari_unique_filename(const char *s)4873{ return pari_unique_filename_suffix(s, NULL); }48744875/* Create a "unique directory" and return its name built from the string4876* s, the user id and process pid (on Unix systems). A "temporary"4877* directory name is prepended. The name returned is pari_malloc'ed.4878* It is DOS-safe (truncated to 8 chars) */4879char*4880pari_unique_dir(const char *s)4881{4882char *buf = init_unique(s, NULL);4883if (pari_dir_exists(buf) && !get_file(buf, pari_dir_exists, NULL))4884pari_err(e_MISC,"couldn't find a suitable name for a tempdir (%s)",s);4885return buf;4886}48874888static long4889get_free_gp_file(void)4890{4891long i, l = s_gp_file.n;4892for (i=0; i<l; i++)4893if (!gp_file[i].fp)4894return i;4895return pari_stack_new(&s_gp_file);4896}48974898static void4899check_gp_file(const char *s, long n)4900{4901if (n < 0 || n >= s_gp_file.n || !gp_file[n].fp)4902pari_err_FILEDESC(s, n);4903}49044905static long4906new_gp_file(const char *s, FILE *f, int t)4907{4908long n;4909n = get_free_gp_file();4910gp_file[n].name = pari_strdup(s);4911gp_file[n].fp = f;4912gp_file[n].type = t;4913gp_file[n].serial = gp_file_serial++;4914if (DEBUGFILES) err_printf("fileopen:%ld (%ld)\n", n, gp_file[n].serial);4915return n;4916}49174918#if defined(ZCAT) && defined(HAVE_PIPES)4919static long4920check_compress(const char *name)4921{4922long l = strlen(name);4923const char *end = name + l-1;4924if (l > 2 && (!strncmp(end-1,".Z",2)4925#ifdef GNUZCAT4926|| !strncmp(end-2,".gz",3)4927#endif4928))4929{ /* compressed file (compress or gzip) */4930char *cmd = stack_malloc(strlen(ZCAT) + l + 4);4931sprintf(cmd,"%s \"%s\"",ZCAT,name);4932return gp_fileextern(cmd);4933}4934return -1;4935}4936#endif49374938long4939gp_fileopen(char *s, char *mode)4940{4941FILE *f;4942if (mode[0]==0 || mode[1]!=0)4943pari_err_TYPE("fileopen",strtoGENstr(mode));4944switch (mode[0])4945{4946case 'r':4947#if defined(ZCAT) && defined(HAVE_PIPES)4948{4949long n = check_compress(s);4950if (n >= 0) return n;4951}4952#endif4953f = fopen(s, "r");4954if (!f) pari_err_FILE("requested file", s);4955return new_gp_file(s, f, mf_IN);4956case 'w':4957case 'a':4958wr_check(s);4959f = fopen(s, mode[0]=='w' ? "w": "a");4960if (!f) pari_err_FILE("requested file", s);4961return new_gp_file(s, f, mf_OUT);4962default:4963pari_err_TYPE("fileopen",strtoGENstr(mode));4964return -1; /* LCOV_EXCL_LINE */4965}4966}49674968long4969gp_fileextern(char *s)4970{4971#ifndef HAVE_PIPES4972pari_err(e_ARCH,"pipes");4973return NULL;/*LCOV_EXCL_LINE*/4974#else4975FILE *f;4976check_secure(s);4977f = popen(s, "r");4978if (!f) pari_err(e_MISC,"[pipe:] '%s' failed",s);4979return new_gp_file(s,f, mf_PIPE);4980#endif4981}49824983void4984gp_fileclose(long n)4985{4986check_gp_file("fileclose", n);4987if (DEBUGFILES) err_printf("fileclose(%ld)\n",n);4988if (gp_file[n].type == mf_PIPE)4989pclose(gp_file[n].fp);4990else4991fclose(gp_file[n].fp);4992pari_free((void*)gp_file[n].name);4993gp_file[n].name = NULL;4994gp_file[n].fp = NULL;4995gp_file[n].type = mf_FALSE;4996gp_file[n].serial = -1;4997while (s_gp_file.n > 0 && !gp_file[s_gp_file.n-1].fp)4998s_gp_file.n--;4999}50005001void5002gp_fileflush(long n)5003{5004check_gp_file("fileflush", n);5005if (DEBUGFILES) err_printf("fileflush(%ld)\n",n);5006if (gp_file[n].type == mf_OUT) (void)fflush(gp_file[n].fp);5007}5008void5009gp_fileflush0(GEN gn)5010{5011long i;5012if (gn)5013{5014if (typ(gn) != t_INT) pari_err_TYPE("fileflush",gn);5015gp_fileflush(itos(gn));5016}5017else for (i = 0; i < s_gp_file.n; i++)5018if (gp_file[i].fp && gp_file[i].type == mf_OUT) gp_fileflush(i);5019}50205021GEN5022gp_fileread(long n)5023{5024Buffer *b;5025FILE *fp;5026GEN z;5027int t;5028check_gp_file("fileread", n);5029t = gp_file[n].type;5030if (t!=mf_IN && t!=mf_PIPE)5031pari_err_FILEDESC("fileread",n);5032fp = gp_file[n].fp;5033b = new_buffer();5034while(1)5035{5036if (!gp_read_stream_buf(fp, b)) { delete_buffer(b); return gen_0; }5037if (*(b->buf)) break;5038}5039z = strtoGENstr(b->buf);5040delete_buffer(b);5041return z;5042}50435044void5045gp_filewrite(long n, const char *s)5046{5047FILE *fp;5048check_gp_file("filewrite", n);5049if (gp_file[n].type!=mf_OUT)5050pari_err_FILEDESC("filewrite",n);5051fp = gp_file[n].fp;5052fputs(s, fp);5053fputc('\n',fp);5054}50555056void5057gp_filewrite1(long n, const char *s)5058{5059FILE *fp;5060check_gp_file("filewrite1", n);5061if (gp_file[n].type!=mf_OUT)5062pari_err_FILEDESC("filewrite1",n);5063fp = gp_file[n].fp;5064fputs(s, fp);5065}50665067GEN5068gp_filereadstr(long n)5069{5070Buffer *b;5071char *s, *e;5072GEN z;5073int t;5074input_method IM;5075check_gp_file("filereadstr", n);5076t = gp_file[n].type;5077if (t!=mf_IN && t!=mf_PIPE)5078pari_err_FILEDESC("fileread",n);5079b = new_buffer();5080IM.myfgets = (fgets_t)&fgets;5081IM.file = (void*) gp_file[n].fp;5082s = b->buf;5083if (!file_getline(b, &s, &IM)) { delete_buffer(b); return gen_0; }5084e = s + strlen(s)-1;5085if (*e == '\n') *e = 0;5086z = strtoGENstr(s);5087delete_buffer(b);5088return z;5089}50905091/*******************************************************************/5092/** **/5093/** INSTALL **/5094/** **/5095/*******************************************************************/50965097#ifdef HAS_DLOPEN5098#include <dlfcn.h>50995100/* see try_name() */5101static void *5102try_dlopen(const char *s, int flag)5103{ void *h = dlopen(s, flag); pari_free((void*)s); return h; }51045105/* like dlopen, but using default(sopath) */5106static void *5107gp_dlopen(const char *name, int flag)5108{5109void *handle;5110char *s;51115112if (!name) return dlopen(NULL, flag);5113s = path_expand(name);51145115/* if sopath empty or path is absolute, use dlopen */5116if (!GP_DATA || *(GP_DATA->sopath->PATH)==0 || path_is_absolute(s))5117return try_dlopen(s, flag);5118else5119{5120forpath_t T;5121char *t;5122forpath_init(&T, GP_DATA->sopath, s);5123while ( (t = forpath_next(&T)) )5124{5125if ( (handle = try_dlopen(t,flag)) ) { pari_free(s); return handle; }5126(void)dlerror(); /* clear error message */5127}5128pari_free(s);5129}5130return NULL;5131}51325133static void *5134install0(const char *name, const char *lib)5135{5136void *handle;51375138#ifndef RTLD_GLOBAL /* OSF1 has dlopen but not RTLD_GLOBAL*/5139# define RTLD_GLOBAL 05140#endif5141handle = gp_dlopen(lib, RTLD_LAZY|RTLD_GLOBAL);51425143if (!handle)5144{5145const char *s = dlerror(); if (s) err_printf("%s\n\n",s);5146if (lib) pari_err(e_MISC,"couldn't open dynamic library '%s'",lib);5147pari_err(e_MISC,"couldn't open dynamic symbol table of process");5148}5149return dlsym(handle, name);5150}5151#else5152# ifdef _WIN325153static HMODULE5154try_LoadLibrary(const char *s)5155{ void *h = LoadLibrary(s); pari_free((void*)s); return h; }51565157/* like LoadLibrary, but using default(sopath) */5158static HMODULE5159gp_LoadLibrary(const char *name)5160{5161HMODULE handle;5162char *s = path_expand(name);51635164/* if sopath empty or path is absolute, use LoadLibrary */5165if (!GP_DATA || *(GP_DATA->sopath->PATH)==0 || path_is_absolute(s))5166return try_LoadLibrary(s);5167else5168{5169forpath_t T;5170char *t;5171forpath_init(&T, GP_DATA->sopath, s);5172while ( (t = forpath_next(&T)) )5173if ( (handle = try_LoadLibrary(t)) ) { pari_free(s); return handle; }5174pari_free(s);5175}5176return NULL;5177}5178static void *5179install0(const char *name, const char *lib)5180{5181HMODULE handle;5182if (lib == pari_library_path)5183{5184handle = GetModuleHandleA(NULL);5185void * fun = (void *) GetProcAddress(handle,name);5186if (fun) return fun;5187}5188handle = gp_LoadLibrary(lib);5189if (!handle)5190{5191if (lib) pari_err(e_MISC,"couldn't open dynamic library '%s'",lib);5192pari_err(e_MISC,"couldn't open dynamic symbol table of process");5193}5194return (void *) GetProcAddress(handle,name);5195}5196# else5197static void *5198install0(const char *name, const char *lib)5199{ pari_err(e_ARCH,"install"); return NULL; }5200#endif5201#endif52025203static char *5204dft_help(const char *gp, const char *s, const char *code)5205{ return stack_sprintf("%s: installed function\nlibrary name: %s\nprototype: %s" , gp, s, code); }52065207void5208gpinstall(const char *s, const char *code, const char *gpname, const char *lib)5209{5210pari_sp av = avma;5211const char *gp = *gpname? gpname: s;5212int update_help;5213void *f;5214entree *ep;5215if (GP_DATA->secure)5216{5217char *msg = pari_sprintf("[secure mode]: about to install '%s'", s);5218pari_ask_confirm(msg);5219pari_free(msg);5220}5221f = install0(s, *lib ?lib :pari_library_path);5222if (!f)5223{5224if (*lib) pari_err(e_MISC,"can't find symbol '%s' in library '%s'",s,lib);5225pari_err(e_MISC,"can't find symbol '%s' in dynamic symbol table of process",s);5226}5227ep = is_entry(gp);5228/* Delete help if 1) help is the default (don't delete user addhelp)5229* and 2) default help changes */5230update_help = (ep && ep->valence == EpINSTALL && ep->help5231&& strcmp(ep->code, code)5232&& !strcmp(ep->help, dft_help(gp,s,ep->code)));5233ep = install(f,gp,code);5234if (update_help || !ep->help) addhelp(gp, dft_help(gp,s,code));5235mt_broadcast(snm_closure(is_entry("install"),5236mkvec4(strtoGENstr(s),strtoGENstr(code),5237strtoGENstr(gp),strtoGENstr(lib))));5238set_avma(av);5239}524052415242