/* $Id: gp.c,v 1.290.2.1 2006/09/25 20:35:15 kb Exp $ Copyright (C) 2000 The PARI group. This file is part of the PARI/GP package. PARI/GP is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY WHATSOEVER. Check the License for details. You should have received a copy of it, along with the package; see the file 'COPYING'. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /*******************************************************************/ /** **/ /** PARI CALCULATOR **/ /** **/ /*******************************************************************/ #include "pari.h" #include "paripriv.h" #include "../language/anal.h" #include "gp.h" #include "../graph/rect.h" #ifdef _WIN32 # include # ifndef WINCE # include # endif #endif #ifdef READLINE BEGINEXTERN # ifdef READLINE_LIBRARY # include # else # include # endif ENDEXTERN #endif #define skip_space(s) while (isspace((int)*s)) s++ #define skip_alpha(s) while (isalpha((int)*s)) s++ /*******************************************************************/ /** **/ /** TEXMACS-SPECIFIC STUFF **/ /** **/ /*******************************************************************/ static int tm_is_waiting = 0, tm_did_complete = 0; /* tell TeXmacs GP will start outputing data */ static void tm_start_output(void) { if (!tm_is_waiting) { printf("%cverbatim:",DATA_BEGIN); fflush(stdout); } tm_is_waiting = 1; } /* tell TeXmacs GP is done and is waiting for new data */ static void tm_end_output(void) { if (tm_is_waiting) { printf("%c", DATA_END); fflush(stdout); } tm_is_waiting = 0; } static char * fgets_texmacs(char *s, int n, FILE *f) { if (!tm_did_complete) { tm_start_output(); tm_end_output(); /* tell TeXmacs we need input */ } return fgets(s,n,f); } #ifdef READLINE typedef struct { char *cmd; long n; /* number of args */ char **v; /* args */ } tm_cmd; static void parse_texmacs_command(tm_cmd *c, const char *ch) { long l = strlen(ch); char *t, *s = (char*)ch, *send = s+l-1; growarray A; if (*s != DATA_BEGIN || *send-- != DATA_END) pari_err(talker, "missing DATA_[BEGIN | END] in TeXmacs command"); s++; if (strncmp(s, "special:", 8)) pari_err(talker, "unrecognized TeXmacs command"); s += 8; if (*s != '(' || *send-- != ')') pari_err(talker, "missing enclosing parentheses for TeXmacs command"); s++; t = s; skip_alpha(s); c->cmd = pari_strndup(t, s - t); grow_init(A); for (c->n = 0; s <= send; c->n++) { char *u = gpmalloc(strlen(s) + 1); skip_space(s); if (*s == '"') s = readstring(s, u); else { /* read integer */ t = s; while (isdigit((int)*s)) s++; strncpy(u, t, s - t); u[s-t] = 0; } grow_append(A, (void*)u); } c->v = (char**)A->v; } static void free_cmd(tm_cmd *c) { while (c->n--) free((void*)c->v[c->n]); free((void*)c->v); } static void handle_texmacs_command(const char *s) { tm_cmd c; parse_texmacs_command(&c, s); if (strcmp(c.cmd, "complete")) pari_err(talker,"Texmacs_stdin command %s not implemented", c.cmd); if (c.n != 2) pari_err(talker,"was expecting 2 arguments for Texmacs_stdin command"); texmacs_completion(c.v[0], atol(c.v[1])); free_cmd(&c); tm_did_complete = 1; } #else static void handle_texmacs_command(const char *s) { pari_err(talker, "readline not available"); } #endif /*******************************************************************/ /** **/ /** BUFFERS **/ /** **/ /*******************************************************************/ #define current_buffer (bufstack?((Buffer*)(bufstack->value)):NULL) static stack *bufstack = NULL; static void pop_buffer(void) { Buffer *b = (Buffer*) pop_stack(&bufstack); delete_buffer(b); } /* kill all buffers until B is met or nothing is left */ static void kill_all_buffers(Buffer *B) { for(;;) { Buffer *b = current_buffer; if (b == B || !b) break; pop_buffer(); } } static void jump_to_given_buffer(Buffer *buf) { Buffer *b; while ( (b = current_buffer) ) { if (b == buf) break; pop_buffer(); } if (!b || !b->env) longjmp(GP_DATA->env, 0); longjmp(b->env, 0); } /********************************************************************/ /** **/ /** HELP **/ /** **/ /********************************************************************/ static int has_ext_help(void) { if (GP_DATA->help) { char *buf = pari_strdup(GP_DATA->help), *s, *t; FILE *file; for (t = s = buf; *s; *t++ = *s++) { if (*s == '\\') s++; else if (*s == ' ') break; } *t = 0; file = fopen(buf,"r"); free(buf); if (file) { fclose(file); return 1; } } return 0; } static int compare_str(char **s1, char **s2) { return strcmp(*s1, *s2); } /* Print all elements of list in columns, pausing every nbli lines * if nbli is non-zero. * list is a NULL terminated list of function names */ void print_fun_list(char **list, long nbli) { long i=0, j=0, maxlen=0, nbcol,len, w = term_width(); char **l; while (list[i]) i++; qsort (list, i, sizeof(char *), (QSCOMP)compare_str); for (l=list; *l; l++) { len = strlen(*l); if (len > maxlen) maxlen=len; } maxlen++; nbcol= w / maxlen; if (nbcol * maxlen == w) nbcol--; if (!nbcol) nbcol = 1; pariputc('\n'); i=0; for (l=list; *l; l++) { pariputs(*l); i++; if (i >= nbcol) { i=0; pariputc('\n'); if (nbli && j++ > nbli) { j = 0; hit_return(); } continue; } len = maxlen - strlen(*l); while (len--) pariputc(' '); } if (i) pariputc('\n'); } static void commands(long n) { const size_t LIST_LEN = 1023UL; size_t size = LIST_LEN, s = 0; long i; entree *ep; char **list = (char **) gpmalloc((size+1)*sizeof(char *)); for (i = 0; i < functions_tblsz; i++) for (ep = functions_hash[i]; ep; ep = ep->next) if ((n < 0 && ep->menu) || ep->menu == n) { list[s] = ep->name; if (++s >= size) { size += (LIST_LEN+1); list = (char**) gprealloc(list, size*sizeof(char *)); } } list[s] = NULL; print_fun_list(list, term_height()-4); free(list); } static void center(char *s) { long i, l = strlen(s), pad = term_width() - l; char *buf, *u; if (pad<0) pad=0; else pad >>= 1; u = buf = (char*)gpmalloc(l + pad + 2); for (i=0; i-subscribe@list.cr.yp.to. \ An archive is kept at the WWW site mentioned above. You can also reach the \ authors directly by email: pari@math.u-bordeaux.fr (answer not guaranteed)."); } static void gentypes(void) { pariputs("List of the PARI types:\n\ t_INT : long integers [ cod1 ] [ cod2 ] [ man_1 ] ... [ man_k ]\n\ t_REAL : long real numbers [ cod1 ] [ cod2 ] [ man_1 ] ... [ man_k ]\n\ t_INTMOD : integermods [ code ] [ mod ] [ integer ]\n\ t_FRAC : irred. rationals [ code ] [ num. ] [ den. ]\n\ t_COMPLEX: complex numbers [ code ] [ real ] [ imag ]\n\ t_PADIC : p-adic numbers [ cod1 ] [ cod2 ] [ p ] [ p^r ] [ int ]\n\ t_QUAD : quadratic numbers [ cod1 ] [ mod ] [ real ] [ imag ]\n\ t_POLMOD : poly mod [ code ] [ mod ] [ polynomial ]\n\ -------------------------------------------------------------\n\ t_POL : polynomials [ cod1 ] [ cod2 ] [ man_1 ] ... [ man_k ]\n\ t_SER : power series [ cod1 ] [ cod2 ] [ man_1 ] ... [ man_k ]\n\ t_RFRAC : irred. rat. func. [ code ] [ num. ] [ den. ]\n\ t_QFR : real qfb [ code ] [ a ] [ b ] [ c ] [ del ]\n\ t_QFI : imaginary qfb [ code ] [ a ] [ b ] [ c ]\n\ t_VEC : row vector [ code ] [ x_1 ] ... [ x_k ]\n\ t_COL : column vector [ code ] [ x_1 ] ... [ x_k ]\n\ t_MAT : matrix [ code ] [ col_1 ] ... [ col_k ]\n\ t_LIST : list [ code ] [ cod2 ] [ x_1 ] ... [ x_k ]\n\ t_STR : string [ code ] [ man_1 ] ... [ man_k ]\n\ t_VECSMALL: vec. small ints [ code ] [ x_1 ] ... [ x_k ]\n\ \n"); } static void menu_commands(void) { pariputs("Help topics: for a list of relevant subtopics, type ?n for n in\n\ 0: user-defined identifiers (variable, alias, function)\n\ 1: Standard monadic or dyadic OPERATORS\n\ 2: CONVERSIONS and similar elementary functions\n\ 3: TRANSCENDENTAL functions\n\ 4: NUMBER THEORETICAL functions\n\ 5: Functions related to ELLIPTIC CURVES\n\ 6: Functions related to general NUMBER FIELDS\n\ 7: POLYNOMIALS and power series\n\ 8: Vectors, matrices, LINEAR ALGEBRA and sets\n\ 9: SUMS, products, integrals and similar functions\n\ 10: GRAPHIC functions\n\ 11: PROGRAMMING under GP\n\ 12: The PARI community\n\ \n\ Also:\n\ ? functionname (short on-line help)\n\ ?\\ (keyboard shortcuts)\n\ ?. (member functions)\n"); if (has_ext_help()) pariputs("\ Extended help looks available:\n\ ?? (opens the full user's manual in a dvi previewer)\n\ ?? tutorial / refcard / libpari (tutorial/reference card/libpari manual)\n\ ?? keyword (long help text about \"keyword\" from the user's manual)\n\ ??? keyword (a propos: list of related functions)."); } static void slash_commands(void) { pariputs("# : enable/disable timer\n\ ## : print time for last result\n\ \\\\ : comment up to end of line\n\ \\a {n} : print result in raw format (readable by PARI)\n\ \\b {n} : print result in beautified format\n\ \\c : list all commands (same effect as ?*)\n\ \\d : print all defaults\n\ \\e {n} : enable/disable echo (set echo=n)\n\ \\g {n} : set debugging level\n\ \\gf{n} : set file debugging level\n\ \\gm{n} : set memory debugging level\n\ \\h {m-n}: hashtable information\n\ \\l {f} : enable/disable logfile (set logfile=f)\n\ \\m {n} : print result in prettymatrix format\n\ \\o {n} : change output method (0=raw, 1=prettymatrix, 2=prettyprint, 3=2-dim)\n\ \\p {n} : change real precision\n\ \\ps{n} : change series precision\n\ \\q : quit completely this GP session\n\ \\r {f} : read in a file\n\ \\s {n} : print stack information\n\ \\t : print the list of PARI types\n\ \\u : print the list of user-defined functions\n\ \\um : print the list of user-defined member functions\n\ \\v : print current version of GP\n\ \\w {nf} : write to a file\n\ \\x {n} : print complete inner structure of result\n\ \\y {n} : disable/enable automatic simplification (set simplify=n)\n\ \n\ {f}=optional filename. {n}=optional integer\n"); } static void member_commands(void) { pariputs("\ Member functions, followed by relevant objects\n\n\ a1-a6, b2-b8, c4-c6 : coeff. of the curve. ell\n\ area : area ell\n\ bid : big ideal bnr\n\ bnf : big number field bnf, bnr\n\ clgp : class group bid, bnf, bnr\n\ cyc : cyclic decomposition (SNF) bid, clgp, bnf, bnr\n\ diff, codiff: different and codifferent nf, bnf, bnr\n\ disc : discriminant ell, nf, bnf, bnr\n\ e, f : inertia/residue degree prid\n\ fu : fundamental units bnf, bnr\n\ gen : generators bid, prid, clgp, bnf, bnr\n\ index: index nf, bnf, bnr\n\ j : j-invariant ell\n"); /* split: some compilers can't handle long constant strings */ pariputs("\ mod : modulus bid, bnr\n\ nf : number field bnf, bnr\n\ no : number of elements bid, clgp, bnf, bnr\n\ omega, eta: [omega1,omega2] and [eta1, eta2] ell\n\ p : rational prime below prid prid\n\ pol : defining polynomial nf, bnf, bnr\n\ reg : regulator bnf, bnr\n\ roots: roots ell nf, bnf, bnr\n\ sign,r1,r2 : signature nf, bnf, bnr\n\ t2 : t2 matrix nf, bnf, bnr\n\ tate : Tate's [u^2, u, q] ell\n\ tu : torsion unit and its order bnf, bnr\n\ w : Mestre's w ell\n\ zk : integral basis nf, bnf, bnr\n"); } #define QUOTE "_QUOTE" #define DOUBQUOTE "_DOUBQUOTE" #define BACKQUOTE "_BACKQUOTE" static char * _cat(char *s, char *t) { *s = 0; strcat(s,t); return s + strlen(t); } static char * filter_quotes(char *s) { int i, l = strlen(s); int quote = 0; int backquote = 0; int doubquote = 0; char *str, *t; for (i=0; i < l; i++) switch(s[i]) { case '\'': quote++; break; case '`' : backquote++; break; case '"' : doubquote++; } str = (char*)gpmalloc(l + quote * (strlen(QUOTE)-1) + doubquote * (strlen(DOUBQUOTE)-1) + backquote * (strlen(BACKQUOTE)-1) + 1); t = str; for (i=0; i < l; i++) switch(s[i]) { case '\'': t = _cat(t, QUOTE); break; case '`' : t = _cat(t, BACKQUOTE); break; case '"' : t = _cat(t, DOUBQUOTE); break; default: *t++ = s[i]; } *t = 0; return str; } static int nl_read(char *s) { size_t l = strlen(s); return s[l-1] == '\n'; } #define nbof(a) sizeof(a) / sizeof(a[0]) /* query external help program for s. num < 0 [keyword] or chapter number */ static void external_help(char *s, int num) { long nbli = term_height()-3, li = 0; char buf[256], ar[32], *str, *opt = ""; pariFILE *z; FILE *f; if (!GP_DATA->help) pari_err(talker,"no external help program"); s = filter_quotes(s); str = gpmalloc(strlen(GP_DATA->help) + strlen(s) + 64); *ar = 0; if (num < 0) opt = "-k"; else if (s[strlen(s)-1] != '@') sprintf(ar,"@%d",num); sprintf(str,"%s -fromgp %s %c%s%s%c",GP_DATA->help,opt, SHELL_Q,s,ar,SHELL_Q); z = try_pipe(str,0); f = z->file; free(str); free(s); while (fgets(buf, nbof(buf), f)) { if (!strncmp("ugly_kludge_done",buf,16)) break; pariputs(buf); if (nl_read(buf) && ++li > nbli) { hit_return(); li = 0; } } pari_fclose(z); } char *keyword_list[]={ "operator", "libpari", "member", "integer", "real", "readline", "refcard", "tutorial", "nf", "bnf", "bnr", "ell", "rnf", "bid", "modulus", NULL }; static int ok_external_help(char **s) { long n; if (!**s) return 1; if (!isalpha((int)**s)) return 3; /* operator or section number */ if (!strncmp(*s,"t_",2)) { *s += 2; return 2; } /* type name */ for (n=0; keyword_list[n]; n++) if (!strcmp(*s,keyword_list[n])) return 3; return 0; } /* don't mess readline display */ static void aide_print(char *s1, char *s2) { pariprintf("%s: %s\n", s1, s2); } static void aide0(char *s, int flag) { long n, long_help = flag & h_LONG; entree *ep,*ep1; char *s1; s = get_sep(s); if (isdigit((int)*s)) { n = atoi(s); if (n == 12) { community(); return; } if (n < 0 || n > 12) pari_err(talker2,"no such section in help: ?",s,s); if (long_help) external_help(s,3); else commands(n); return; } /* Get meaningful entry on \ps 5 */ if (*s == '\\') { s1 = s+1; skip_alpha(s1); *s1 = '\0';} if (flag & h_APROPOS) { external_help(s,-1); return; } if (long_help && (n = ok_external_help(&s))) { external_help(s,n); return; } switch (*s) { case '*' : commands(-1); return; case '\0': menu_commands(); return; case '\\': slash_commands(); return; case '.' : member_commands(); return; } ep = is_entry(s); if (ep && long_help) { if (!strcmp(ep->name, "default")) { char *t = s+7, *e; skip_space(t); if (*t == '(') { t++; skip_space(t); e = t; skip_alpha(e); *e = '\0'; /* safe: get_sep() made it a copy */ if (is_default(t)) { external_help(t, 2); return; } } } } if (!ep) { n = is_default(s)? 2: 3; if (long_help) external_help(s,n); else { if (n == 2) { aide_print(s,"default"); return; } n = whatnow(s,1); if (!n) { aide_print(s,"unknown identifier"); return; } aide_print(s, "obsolete function"); whatnow_new_syntax(s, n); } return; } ep1 = ep; ep = do_alias(ep); if (ep1 != ep) pariprintf("%s is aliased to:\n\n",s); switch(EpVALENCE(ep)) { case EpUSER: if (!ep->help || long_help) print_user_fun(ep); if (!ep->help) return; if (long_help) { pariputs("\n\n"); long_help=0; } break; case EpGVAR: case EpVAR: if (!ep->help) { aide_print(s, "user defined variable"); return; } long_help=0; break; case EpINSTALL: if (!ep->help) { aide_print(s, "installed function"); return; } long_help=0; break; case EpNEW: aide_print(s, "new identifier (no valence assigned)"); return; } if (long_help) { external_help(ep->name,3); return; } if (ep->help) { print_text(ep->help); return; } pari_err(bugparier,"aide (no help found)"); } void aide(char *s, long flag) { if ((flag & h_RL) == 0) { if (*s == '?') { flag |= h_LONG; s++; } if (*s == '?') { flag |= h_APROPOS; s++; } } term_color(c_HELP); aide0(s,flag); term_color(c_NONE); if ((flag & h_RL) == 0) pariputc('\n'); } /********************************************************************/ /** **/ /** GP HEADER **/ /** **/ /********************************************************************/ static char * what_readline() { char *s; #ifdef READLINE char *ver, *extra = stackmalloc(strlen(READLINE) + 32); # if defined(HAS_RL_LIBRARY_VERSION) || defined(FAKE_RL_LIBRARY_VERSION) # ifdef FAKE_RL_LIBRARY_VERSION extern char *rl_library_version; # endif if (strcmp(READLINE, rl_library_version)) { ver = (char*)rl_library_version; (void)sprintf(extra, " [was v%s in Configure]", READLINE); } else # endif { ver = READLINE; extra[0] = 0; } s = stackmalloc(3 + strlen(ver) + 8 + strlen(extra)); (void)sprintf(s, "v%s %s%s", ver, (GP_DATA->flags & USE_READLINE)? "enabled": "disabled", extra); #else s = "not compiled in"; #endif return s; } static void print_shortversion(void) { const ulong mask = (1<>= PARI_VERSION_SHIFT; minor = n & mask; n >>= PARI_VERSION_SHIFT; major = n; pariprintf("%lu.%lu.%lu\n", major,minor,patch); exit(0); } static char * what_cc() { char *s; #ifdef GCC_VERSION # ifdef __cplusplus # define Format "g++-%s" # else # define Format "gcc-%s" # endif s = stackmalloc(4 + strlen(GCC_VERSION) + 1); (void)sprintf(s, Format, GCC_VERSION); #else # ifdef _MSC_VER s = stackmalloc(32); (void)sprintf(s, "MSVC-%i", _MSC_VER); # else s = NULL; # endif #endif return s; } static void print_version(void) { pari_sp av = avma; char *buf, *ver; center(PARIVERSION); center(PARIINFO); ver = what_cc(); buf = stackmalloc(strlen(__DATE__) + 32 + (ver? strlen(ver): 0)); if (ver) (void)sprintf(buf, "compiled: %s, %s", __DATE__, ver); else (void)sprintf(buf, "compiled: %s", __DATE__); center(buf); ver = what_readline(); buf = stackmalloc(strlen(ver) + 64); (void)sprintf(buf, "(readline %s, extended help%s available)", ver, has_ext_help()? "": " not"); center(buf); avma = av; } static void gp_head(void) { #ifdef READLINE if (GP_DATA->flags & TEXMACS) printf("%ccommand:(cas-supports-completions-set! \"pari\")%c\n", DATA_BEGIN, DATA_END); #endif print_version(); pariputs("\n"); center("Copyright (C) 2000-2006 The PARI Group"); print_text("\nPARI/GP is free software, covered by the GNU General Public \ License, and comes WITHOUT ANY WARRANTY WHATSOEVER"); pariputs("\n\ Type ? for help, \\q to quit.\n\ Type ?12 for how to get moral (and possibly technical) support.\n\n"); pariprintf("parisize = %lu, primelimit = %lu\n", top-bot, GP_DATA->primelimit); } /********************************************************************/ /** **/ /** METACOMMANDS **/ /** **/ /********************************************************************/ #define pariputs_opt(s) if (!(GP_DATA->flags & QUIET)) pariputs(s) void gp_quit(void) { free_graph(); pari_close(); kill_all_buffers(NULL); term_color(c_NONE); pariputs_opt("Goodbye!\n"); if (GP_DATA->flags & TEXMACS) tm_end_output(); exit(0); } static GEN gpreadbin(const char *s) { GEN x = readbin(s,infile); popinfile(); return x; } static void escape0(char *tch) { const char *s; char c; if (compatible != NONE) { s = tch; while (*s) if (*s++ == '=') { char *f = NULL; long len = (s-tch) - 1; if (!strncmp(tch,"precision",len)) f = "realprecision"; else if (!strncmp(tch,"serieslength",len)) f = "seriesprecision"; else if (!strncmp(tch,"format",len)) f = "format"; else if (!strncmp(tch,"prompt",len)) f = "prompt"; if (f) { (void)setdefault(f, s, d_ACKNOWLEDGE); return; } break; } } s = tch; switch ((c = *s++)) { case 'w': case 'x': case 'a': case 'b': case 'B': case 'm': { /* history things */ long d; GEN x; if (c != 'w' && c != 'x') d = get_int(s,0); else { d = atol(s); if (*s == '-') s++; while (isdigit((int)*s)) s++; } x = gp_history(GP_DATA->hist, d, tch+1,tch-1); switch (c) { case 'B': { /* prettyprinter */ gp_data G = *GP_DATA; /* copy */ gp_hist h = *(G.hist); /* copy */ pariout_t f = *(G.fmt); /* copy */ G.hist = &h; h.total = 0; /* no hist number */ G.fmt = &f; f.prettyp = f_PRETTY; G.flags &= ~(TEST|TEXMACS); G.lim_lines = 0; gp_output(x, &G); break; } case 'a': brute (x, GP_DATA->fmt->format, -1); break; case 'm': matbrute(x, GP_DATA->fmt->format, -1); break; case 'b': sor(x, GP_DATA->fmt->format, -1, GP_DATA->fmt->fieldw); break; case 'x': voir(x, get_int(s, -1)); break; case 'w': s = get_sep(s); if (!*s) s = current_logfile; write0(s, mkvec(x)); return; } pariputc('\n'); return; } case 'c': commands(-1); break; case 'd': (void)setdefault("",NULL,0); break; case 'e': s = get_sep(s); if (!*s) s = (GP_DATA->flags & ECHO)? "0": "1"; (void)sd_echo(s,d_ACKNOWLEDGE); break; case 'g': switch (*s) { case 'm': (void)sd_debugmem(++s,d_ACKNOWLEDGE); break; case 'f': (void)sd_debugfiles(++s,d_ACKNOWLEDGE); break; default : (void)sd_debug(s,d_ACKNOWLEDGE); break; } break; case 'h': print_functions_hash(s); break; case 'l': s = get_sep(s); if (*s) { (void)sd_logfile(s,d_ACKNOWLEDGE); if (logfile) break; } (void)sd_log(logfile?"0":"1",d_ACKNOWLEDGE); break; case 'o': (void)sd_output(s,d_ACKNOWLEDGE); break; case 'p': switch (*s) { case 's': (void)sd_seriesprecision(++s,d_ACKNOWLEDGE); break; default : (void)sd_realprecision(s,d_ACKNOWLEDGE); break; } break; case 'q': gp_quit(); break; case 'r': s = get_sep(s); switchin(s); if (file_is_binary(infile)) { GEN x = gpreadbin(s); if (isclone(x)) /* many BIN_GEN */ { long i, l = lg(x); pari_warn(warner,"setting %ld history entries", l-1); for (i=1; ihist, (GEN)x[i]); } } break; case 's': etatpile(); break; case 't': gentypes(); break; case 'u': switch (*s) { case 'm': print_all_user_member(); break; default: print_all_user_fun(); } break; case 'v': print_version(); break; case 'y': s = get_sep(s); if (!*s) s = (GP_DATA->flags & SIMPLIFY)? "0": "1"; (void)sd_simplify(s,d_ACKNOWLEDGE); break; default: pari_err(caracer1,tch,tch-1); } } static void escape(char *tch) { char *old = get_analyseur(); set_analyseur(tch); /* for error messages */ escape0(tch); set_analyseur(old); } enum { ti_NOPRINT, ti_REGULAR, ti_LAST, ti_INTERRUPT }; /* flag: * ti_NOPRINT don't print * ti_REGULAR print elapsed time (flags & CHRONO) * ti_LAST print last elapsed time (##) * ti_INTERRUPT received a SIGINT */ static char * gp_format_time(long flag) { static char buf[64]; static long last = 0; long delay = (flag == ti_LAST)? last: TIMER(GP_DATA->T); char *s; last = delay; switch(flag) { case ti_REGULAR: s = "time = "; break; case ti_INTERRUPT: s = "user interrupt after "; break; case ti_LAST: s = " *** last result computed in "; break; default: return NULL; } strcpy(buf,s); s = buf+strlen(s); strcpy(s, term_get_color(c_TIME)); s+=strlen(s); if (delay >= 3600000) { sprintf(s, "%ldh, ", delay / 3600000); s+=strlen(s); delay %= 3600000; } if (delay >= 60000) { sprintf(s, "%ldmn, ", delay / 60000); s+=strlen(s); delay %= 60000; } if (delay >= 1000) { sprintf(s, "%ld,", delay / 1000); s+=strlen(s); delay %= 1000; if (delay < 100) { sprintf(s, "%s", (delay<10)? "00": "0"); s+=strlen(s); } } sprintf(s, "%ld ms", delay); s+=strlen(s); strcpy(s, term_get_color(c_NONE)); if (flag != ti_INTERRUPT) { s+=strlen(s); *s++='.'; *s++='\n'; *s=0; } return buf; } static int chron(char *s) { if (*s) { /* if "#" or "##" timer metacommand. Otherwise let the parser get it */ if (*s == '#') s++; if (*s) return 0; pariputs(gp_format_time(ti_LAST)); } else { GP_DATA->flags ^= CHRONO; (void)sd_timer("",d_ACKNOWLEDGE); } return 1; } /* return 0: can't interpret *buf as a metacommand * 1: did interpret *buf as a metacommand or empty command */ static int check_meta(char *buf) { switch(*buf++) { case '?': aide(buf, h_REGULAR); break; case '#': return chron(buf); case '\\': escape(buf); break; case '\0': break; default: return 0; } return 1; } /********************************************************************/ /* */ /* GPRC */ /* */ /********************************************************************/ #if defined(UNIX) || defined(__EMX__) # include #endif static int get_line_from_file(char *prompt, filtre_t *F, FILE *file); #define err_gprc(s,t,u) { fprintferr("\n"); pari_err(talker2,s,t,u); } /* LOCATE GPRC */ /* return $HOME or the closest we can find */ static char * get_home(int *free_it) { char *drv, *pth = os_getenv("HOME"); if (pth) return pth; if ((drv = os_getenv("HOMEDRIVE")) && (pth = os_getenv("HOMEPATH"))) { /* looks like WinNT */ char *buf = gpmalloc(strlen(pth) + strlen(drv) + 1); sprintf(buf, "%s%s",drv,pth); *free_it = 1; return buf; } #if defined(__EMX__) || defined(UNIX) { struct passwd *p = getpwuid(geteuid()); if (p) return p->pw_dir; } #endif return "."; } static FILE * gprc_chk(char *s) { FILE *f = fopen(s, "r"); if (f && !(GP_DATA->flags & QUIET)) fprintferr("Reading GPRC: %s ...", s); return f; } /* Look for [._]gprc: $GPRC, then in $HOME, ., /etc, path [ to gp binary ] */ static FILE * gprc_get(char *path) { FILE *f = NULL; char *str, *s, c; long l; s = os_getenv("GPRC"); if (s) f = gprc_chk(s); if (!f) { int free_it = 0; s = get_home(&free_it); l = strlen(s); c = s[l-1]; str = strcpy(gpmalloc(l+7), s); if (free_it) free(s); s = str + l; if (c != '/' && c != '\\') *s++ = '/'; #ifdef UNIX *s = '.'; /* .gprc */ #else *s = '_'; /* _gprc */ #endif strcpy(s+1, "gprc"); f = gprc_chk(str); /* in $HOME */ if (!f) f = gprc_chk(s); /* in . */ if (!f) f = gprc_chk("/etc/gprc"); if (!f) f = gprc_chk("C:/_gprc"); if (!f) { /* in 'gp' directory */ char *t = path + strlen(path); while (t > path && *t != '/') t--; if (*t == '/') { long l = t - path + 1; t = gpmalloc(l + 6); strncpy(t, path, l); strcpy(t+l, s); f = gprc_chk(t); free(t); } } free(str); } return f; } /* PREPROCESSOR */ static ulong read_uint(char **s) { long v = atol(*s); if (!isdigit((int)**s)) err_gprc("not an integer", *s, *s); while (isdigit((int)**s)) (*s)++; return v; } static ulong read_dot_uint(char **s) { if (**s != '.') return 0; (*s)++; return read_uint(s); } /* read a.b.c */ static long read_version(char **s) { long a, b, c; a = read_uint(s); b = read_dot_uint(s); c = read_dot_uint(s); return PARI_VERSION(a,b,c); } static int get_preproc_value(char **s) { if (!strncmp(*s,"EMACS",5)) { *s += 5; return GP_DATA->flags & (EMACS|TEXMACS); } if (!strncmp(*s,"READL",5)) { *s += 5; return GP_DATA->flags & USE_READLINE; } if (!strncmp(*s,"VERSION",7)) { int less = 0, orequal = 0; long d; *s += 7; switch(**s) { case '<': (*s)++; less = 1; break; case '>': (*s)++; less = 0; break; default: return -1; } if (**s == '=') { (*s)++; orequal = 1; } d = PARI_VERSION_CODE - read_version(s); if (!d) return orequal; return less? (d < 0): (d > 0); } return -1; } /* PARSE GPRC */ /* 1) replace next separator by '\0' (t must be writeable) * 2) return the next expression ("" if none) * see get_sep() */ static char * next_expr(char *t) { int outer = 1; char *s = t; for(;;) { char c; switch ((c = *s++)) { case '"': if (outer || (s >= t+2 && s[-2] != '\\')) outer = !outer; break; case '\0': return ""; default: if (outer && c == ';') { s[-1] = 0; return s; } } } } static void gp_initrc(growarray A, char *path) { char *nexts,*s,*t; FILE *file = gprc_get(path); Buffer *b; filtre_t F; VOLATILE long c = 0; if (!file) return; b = new_buffer(); init_filtre(&F, b); for(;;) { if (setjmp(GP_DATA->env)) fprintferr("...skipping line %ld.\n", c); c++; if (!get_line_from_file(NULL,&F,file)) break; s = b->buf; if (*s == '#') { /* preprocessor directive */ int z, NOT = 0; s++; if (strncmp(s,"if",2)) err_gprc("unknown directive",s,b->buf); s += 2; if (!strncmp(s,"not",3)) { NOT = !NOT; s += 3; } if (*s == '!') { NOT = !NOT; s++; } t = s; z = get_preproc_value(&s); if (z < 0) err_gprc("unknown preprocessor variable",t,b->buf); if (NOT) z = !z; if (!*s) { /* make sure at least an expr follows the directive */ if (!get_line_from_file(NULL,&F,file)) break; s = b->buf; } if (!z) continue; /* dump current line */ } /* parse line */ for ( ; *s; s = nexts) { nexts = next_expr(s); if (!strncmp(s,"read",4) && (s[4] == ' ' || s[4] == '\t' || s[4] == '"')) { /* read file */ s += 4; t = gpmalloc(strlen(s) + 1); if (*s == '"') (void)readstring(s, t); else strcpy(t,s); grow_append(A, t); } else { /* set default */ t = s; while (*t && *t != '=') t++; if (*t != '=') err_gprc("missing '='",t,b->buf); *t++ = 0; if (*t == '"') (void)readstring(t, t); (void)setdefault(s,t,d_INITRC); } } } delete_buffer(b); if (!(GP_DATA->flags & QUIET)) fprintferr("Done.\n\n"); fclose(file); } /********************************************************************/ /* */ /* GP MAIN LOOP */ /* */ /********************************************************************/ static void brace_color(char *s, int c, int force) { if (disable_color || (gp_colors[c] == c_NONE && !force)) return; #ifdef RL_PROMPT_START_IGNORE if (GP_DATA->flags & USE_READLINE) *s++ = RL_PROMPT_START_IGNORE; #endif strcpy(s, term_get_color(c)); #ifdef RL_PROMPT_START_IGNORE if (GP_DATA->flags & USE_READLINE) { s+=strlen(s); *s++ = RL_PROMPT_END_IGNORE; *s = 0; } #endif } char * color_prompt(char *prompt) { static char buf[MAX_PROMPT_LEN + 24]; /* + room for color codes */ char *s; if (GP_DATA->flags & TEST) return prompt; s = buf; *s = 0; /* escape sequences bug readline, so use special bracing (if available) */ brace_color(s, c_PROMPT, 0); s += strlen(s); strcpy(s, prompt); s += strlen(s); brace_color(s, c_INPUT, 1); return buf; } void update_logfile(const char *prompt, const char *s) { switch (logstyle) { case logstyle_TeX: fprintf(logfile, "\\PARIpromptSTART|%s\\PARIpromptEND|%s\\PARIinputEND|%%\n", prompt,s); break; case logstyle_plain: fprintf(logfile, "%s%s\n",prompt,s); break; case logstyle_color: /* Can't do in one pass, since term_get_color() returns a static */ fprintf(logfile, "%s%s", term_get_color(c_PROMPT), prompt); fprintf(logfile, "%s%s", term_get_color(c_INPUT), s); fprintf(logfile, "%s\n", term_get_color(c_NONE)); break; } } /* prompt = NULL --> from gprc. Return 1 if new input, and 0 if EOF */ static int get_line_from_file(char *PROMPT, filtre_t *F, FILE *file) { const int Texmacs_stdin = ((GP_DATA->flags & TEXMACS) && file == stdin); char *s; input_method IM; IM.file = file; IM.fgets= Texmacs_stdin? &fgets_texmacs: &fgets; IM.prompt = NULL; IM.getline= &file_input; IM.free = 0; if (! input_loop(F,&IM)) { if (Texmacs_stdin) tm_start_output(); return 0; } s = ((Buffer*)F->buf)->buf; if (*s && PROMPT) /* don't echo if from gprc */ { if (GP_DATA->flags & ECHO) { pariputs(PROMPT); pariputs(s); pariputc('\n'); } else if (logfile) update_logfile(PROMPT, s); pariflush(); } if (GP_DATA->flags & TEXMACS) { tm_did_complete = 0; if (Texmacs_stdin && *s == DATA_BEGIN) { handle_texmacs_command(s); *s = 0; } else tm_start_output(); } return 1; } static int is_interactive(void) { ulong f = GP_DATA->flags; #if defined(UNIX) || defined(__EMX__) || defined(_WIN32) return (infile == stdin && !(f & TEXMACS) && (f & EMACS || isatty(fileno(stdin)))); #else return (infile == stdin && !(f & TEXMACS)); #endif } /* return 0 if no line could be read (EOF) */ static int gp_read_line(filtre_t *F, char *PROMPT) { Buffer *b = (Buffer*)F->buf; int res; if (compatible == OLDALL) F->downcase = 1; if (b->len > 100000) fix_buffer(b, 100000); if (is_interactive()) { #ifdef READLINE if (GP_DATA->flags & USE_READLINE) res = get_line_from_readline(PROMPT? PROMPT: GP_DATA->prompt, GP_DATA->prompt_cont, F); else #endif { if (!PROMPT) PROMPT = color_prompt( expand_prompt(GP_DATA->prompt, F) ); pariputs(PROMPT); res = get_line_from_file(PROMPT,F,infile); } if (!disable_color) { term_color(c_NONE); pariflush(); } } else res = get_line_from_file(DFT_PROMPT,F,infile); return res; } /* kill all history entries since loc */ static void prune_history(gp_hist *H, long loc) { long i, j; i = (H->total-1) % H->size; j = H->total - loc; for ( ; j > 0; i--,j--) { if (H->res[i]) { gunclone(H->res[i]); H->res[i] = NULL; } if (!i) i = H->size; } H->total = loc; } static int is_silent(char *s) { return s[strlen(s) - 1] == ';'; } /* If there are other buffers open (bufstack != NULL), we are doing an * immediate read (with read, extern...) */ static GEN gp_main_loop(int ismain) { gp_hist *H = GP_DATA->hist; VOLATILE GEN z = gnil; VOLATILE pari_sp av = top - avma; Buffer *b = new_buffer(); filtre_t F; init_filtre(&F, b); push_stack(&bufstack, (void*)b); for(;;) { if (ismain) { static long tloc, outtyp; tloc = H->total; outtyp = GP_DATA->fmt->prettyp; recover(0); if (setjmp(GP_DATA->env)) { /* recover from error */ char *s = (char*)global_err_data; if (s && *s) outerr(readseq(s)); avma = top; av = 0; prune_history(H, tloc); GP_DATA->fmt->prettyp = outtyp; kill_all_buffers(b); } } if (! gp_read_line(&F, NULL)) { if (popinfile()) gp_quit(); if (ismain) continue; pop_buffer(); return z; } if (check_meta(b->buf)) continue; if (ismain) { #if defined(_WIN32) || defined(__CYGWIN32__) win32ctrlc = 0; #endif TIMERstart(GP_DATA->T); } avma = top - av; pari_set_last_newline(1); z = gpreadseq(b->buf, GP_DATA->flags & STRICTMATCH); if (! ismain) continue; if (!pari_last_was_newline()) pariputc('\n'); if (GP_DATA->flags & CHRONO) pariputs(gp_format_time(ti_REGULAR)); else (void)gp_format_time(ti_NOPRINT); if (z == gnil) continue; if (GP_DATA->flags & SIMPLIFY) z = simplify_i(z); z = set_hist_entry(H, z); if (! is_silent(b->buf) ) gp_output(z, GP_DATA); } } /********************************************************************/ /* */ /* EXCEPTION HANDLER */ /* */ /********************************************************************/ void gp_sigint_fun(void) { pari_err(siginter, gp_format_time(ti_INTERRUPT)); } static void gp_handle_SIGINT(void) { #if defined(_WIN32) || defined(__CYGWIN32__) win32ctrlc++; #else if (GP_DATA->flags & TEXMACS) tm_start_output(); gp_sigint_fun(); #endif } static void gp_sighandler(int sig) { char *msg; #ifndef HAS_SIGACTION /*SYSV reset the signal handler in the handler*/ (void)os_signal(sig,gp_sighandler); #endif switch(sig) { #ifdef SIGBREAK case SIGBREAK: gp_handle_SIGINT(); return; #endif #ifdef SIGINT case SIGINT: gp_handle_SIGINT(); return; #endif #ifdef SIGSEGV case SIGSEGV: msg = "GP (Segmentation Fault)"; break; #endif #ifdef SIGBUS case SIGBUS: msg = "GP (Bus Error)"; break; #endif #ifdef SIGFPE case SIGFPE: msg = "GP (Floating Point Exception)"; break; #endif #ifdef SIGPIPE case SIGPIPE: { pariFILE *f = GP_DATA->pp->file; if (f && pari_outfile == f->file) { pari_err(talker, "Broken Pipe, resetting file stack..."); GP_DATA->pp->file = NULL; /* to avoid oo recursion on error */ pari_outfile = stdout; pari_fclose(f); } /*Do not attempt to write to stdout in case it triggered the SIGPIPE*/ return; /* not reached */ } #endif default: msg = "signal handling"; break; } pari_err(bugparier, msg); } int break_loop(long numerr) { static FILE *oldinfile = NULL; static char *old = NULL; static Buffer *b = NULL; VOLATILE int go_on = 0; char *s, *t; filtre_t F; if (b) jump_to_given_buffer(b); b = new_buffer(); push_stack(&bufstack, (void*)b); (void)&s; /* emulate volatile */ old = s = get_analyseur(); t = NULL; if (bufstack->prev) { Buffer *oldb = (Buffer*)bufstack->prev->value; t = oldb->buf; /* something fishy, probably a ^C, or we overran analyseur */ if (!s || !s[-1] || s < t || s >= t + oldb->len) s = NULL; } oldinfile = infile; init_filtre(&F, b); term_color(c_ERR); pariputc('\n'); errcontext("Break loop (type 'break' or Control-d to go back to GP)", s, t); if (s) pariputc('\n'); term_color(c_NONE); if (numerr == siginter) pariputs("[type in empty line to continue]\n"); infile = stdin; for(;;) { GEN x; if (setjmp(b->env)) pariputc('\n'); if (! gp_read_line(&F, BREAK_LOOP_PROMPT)) { if (popinfile()) break; continue; } #if defined(_WIN32) || defined(__CYGWIN32__) win32ctrlc = 0; #endif if (check_meta(b->buf)) { /* break loop initiated by ^C? Empty input --> continue computation */ if (numerr == siginter && *(b->buf) == 0) { go_on=1; break; } continue; } x = readseq(b->buf); if (did_break()) break; if (x == gnil || is_silent(b->buf)) continue; term_color(c_OUTPUT); gen_output(x, GP_DATA->fmt); term_color(c_NONE); pariputc('\n'); } if (old && !s) set_analyseur(old); b = NULL; infile = oldinfile; pop_buffer(); return go_on; } int gp_exception_handler(long numerr) { char *s = (char*)global_err_data; if (!s) return 0; if (*s) { /* prevent infinite recursion in case s raises an exception */ static int recovering = 0; if (recovering) recovering = 0; else { recovering = 1; fprintferr("\n"); outerr(readseq(s)); recovering = 0; return 0; } } if (numerr == errpile) { var_make_safe(); avma = top; } return break_loop(numerr); } /********************************************************************/ /* */ /* GP-SPECIFIC ROUTINES */ /* */ /********************************************************************/ static void check_secure(char *s) { if (GP_DATA->flags & SECURE) pari_err(talker, "[secure mode]: system commands not allowed\nTried to run '%s'",s); } GEN read0(char *s) { switchin(s); if (file_is_binary(infile)) return gpreadbin(s); return gp_main_loop(0); } GEN extern0(char *s) { check_secure(s); infile = try_pipe(s, mf_IN)->file; return gp_main_loop(0); } GEN input0(void) { Buffer *b = new_buffer(); filtre_t F; GEN x; init_filtre(&F, b); push_stack(&bufstack, (void*)b); while (! get_line_from_file(DFT_INPROMPT,&F,infile)) if (popinfile()) { fprintferr("no input ???"); gp_quit(); } x = readseq(b->buf); pop_buffer(); return x; } void system0(char *s) { #if defined(UNIX) || defined(__EMX__) || defined(_WIN32) check_secure(s); system(s); #else pari_err(archer); #endif } /*******************************************************************/ /** **/ /** INITIALIZATION **/ /** **/ /*******************************************************************/ static void testuint(char *s, ulong *d) { if (s) *d = get_uint(s); } static char * read_arg(long *nread, char *t, long argc, char **argv) { long i = *nread; if (isdigit((int)*t)) return t; if (*t || i==argc) usage(argv[0]); *nread = i+1; return argv[i]; } static char * read_arg_equal(long *nread, char *t, long argc, char **argv) { long i = *nread; if (*t=='=' && isdigit((int)t[1])) return t+1; if (*t || i==argc) usage(argv[0]); *nread = i+1; return argv[i]; } static void init_trivial_stack() { const size_t s = 2048; bot = (pari_sp)gpmalloc(s); avma = top = bot + s; } static void read_opt(growarray A, long argc, char **argv) { char *b = NULL, *p = NULL, *s = NULL; long i = 1, initrc = 1; (void)&p; (void)&b; (void)&s; /* -Wall gcc-2.95 */ pari_outfile = stderr; while (i < argc) { char *t = argv[i]; if (*t++ != '-') break; i++; switch(*t++) { case 'b': b = read_arg(&i,t,argc,argv); pari_warn(warner, "buffersize is no longer used. -b ignored"); break; case 'p': p = read_arg(&i,t,argc,argv); break; case 's': s = read_arg(&i,t,argc,argv); break; case 'e': if (strncmp(t,"macs",4)) usage(argv[0]); /* obsolete */ GP_DATA->flags |= EMACS; break; case 'q': GP_DATA->flags |= QUIET; break; case 't': if (strncmp(t,"est",3)) usage(argv[0]); /* obsolete */ GP_DATA->flags |= TEST; /* fall through */ case 'f': initrc = 0; break; case '-': if (strcmp(t, "version-short") == 0) { print_shortversion(); exit(0); } if (strcmp(t, "version") == 0) { init_trivial_stack(); print_version(); free((void*)bot); exit(0); } if (strcmp(t, "texmacs") == 0) { GP_DATA->flags |= TEXMACS; break; } if (strcmp(t, "emacs") == 0) { GP_DATA->flags |= EMACS; break; } if (strcmp(t, "test") == 0) { GP_DATA->flags |= TEST; initrc = 0; break; } if (strcmp(t, "quiet") == 0) { GP_DATA->flags |= QUIET; break; } if (strcmp(t, "fast") == 0) { initrc = 0; break; } if (strncmp(t, "primelimit",10) == 0) {p = read_arg_equal(&i,t+10,argc,argv); break; } if (strncmp(t, "stacksize",9) == 0) {s = read_arg_equal(&i,t+9,argc,argv); break; } /* fall through */ default: usage(argv[0]); } } if (GP_DATA->flags & TEXMACS) tm_start_output(); if (GP_DATA->flags & TEST) init80col(0); if (initrc) { gp_initrc(A, argv[0]); if (setjmp(GP_DATA->env)) { pariputs("### Errors on startup, exiting...\n\n"); exit(1); } } for ( ; i < argc; i++) grow_append(A, pari_strdup(argv[i])); /* override the values from gprc */ testuint(p, &(GP_DATA->primelimit)); testuint(s, (ulong*)&top); if (GP_DATA->flags & (EMACS|TEXMACS|TEST)) disable_color = 1; pari_outfile = stdout; } #ifdef WINCE int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) { char **argv = NULL; int argc = 1; #else int main(int argc, char **argv) { #endif growarray A, *newfun, *oldfun; long i; GP_DATA = default_gp_data(); initout(1); for (i=0; ienv)) { pariputs("### Errors on startup, exiting...\n\n"); exit(1); } #ifdef __MWERKS__ argc = ccommand(&argv); #endif grow_init(A); pari_init_defaults(); read_opt(A, argc,argv); pari_init_opts(top-bot, GP_DATA->primelimit, 0); newfun = pari_get_modules(); grow_append(*newfun, functions_gp); grow_append(*newfun, functions_highlevel); oldfun = pari_get_oldmodules(); grow_append(*oldfun, functions_oldgp); if (new_fun_set) { pari_add_module(functions_gp); pari_add_module(functions_highlevel); } else pari_add_module(functions_oldgp); pari_sig_init(gp_sighandler); init_graph(); #ifdef READLINE init_readline(); #endif whatnow_fun = whatnow; sigint_fun = gp_sigint_fun; default_exception_handler = gp_exception_handler; gp_expand_path(GP_DATA->path); if (!(GP_DATA->flags & QUIET)) gp_head(); if (A->n) { VOLATILE ulong f = GP_DATA->flags; FILE *l = logfile; VOLATILE long i; GP_DATA->flags &= ~(CHRONO|ECHO); logfile = NULL; for (i = 0; i < A->n; i++) { if (setjmp(GP_DATA->env)) { fprintferr("... skipping file '%s'\n", A->v[i]); i++; if (i == A->n) break; } (void)read0((char*)A->v[i]); free(A->v[i]); } GP_DATA->flags = f; logfile = l; } grow_kill(A); (void)gp_main_loop(1); gp_quit(); return 0; /* not reached */ } /*******************************************************************/ /** **/ /** GP OUTPUT **/ /** **/ /*******************************************************************/ /* EXTERNAL PRETTYPRINTER */ /* Wait for prettinprinter to finish, to prevent new prompt from overwriting * the output. Fill the output buffer, wait until it is read. * Better than sleep(2): give possibility to print */ static void prettyp_wait(void) { char *s = " \n"; long i = 2000; pariputs("\n\n"); pariflush(); /* start translation */ while (--i) pariputs(s); pariputs("\n"); pariflush(); } /* initialise external prettyprinter (tex2mail) */ static int prettyp_init(void) { gp_pp *pp = GP_DATA->pp; if (!pp->cmd) return 0; if (pp->file || (pp->file = try_pipe(pp->cmd, mf_OUT))) return 1; pari_warn(warner,"broken prettyprinter: '%s'",pp->cmd); free(pp->cmd); pp->cmd = NULL; return 0; } /* n = history number. if n = 0 no history */ static int tex2mail_output(GEN z, long n) { pariout_t T = *(GP_DATA->fmt); /* copy */ FILE *o_out, *o_logfile = logfile; if (!prettyp_init()) return 0; o_out = pari_outfile; /* save state */ /* Emit first: there may be lines before the prompt */ if (n) term_color(c_OUTPUT); pariflush(); pari_outfile = GP_DATA->pp->file->file; T.prettyp = f_TEX; logfile = NULL; /* history number */ if (n) { char s[128], c_hist[16], c_out[16]; strcpy(c_hist, term_get_color(c_HIST)); strcpy(c_out , term_get_color(c_OUTPUT)); if (*c_hist || *c_out) sprintf(s, "\\LITERALnoLENGTH{%s}\\%%%ld =\\LITERALnoLENGTH{%s} ", c_hist, n, c_out); else sprintf(s, "\\%%%ld = ", n); pariputs_opt(s); if (o_logfile) { switch (logstyle) { case logstyle_plain: fprintf(o_logfile, "%%%ld = ", n); break; case logstyle_color: fprintf(o_logfile, "%s%%%ld = %s", c_hist, n, c_out); break; case logstyle_TeX: fprintf(o_logfile, "\\PARIout{%ld}", n); break; } } } /* output */ gen_output(z, &T); /* flush and restore */ prettyp_wait(); if (o_logfile) { pari_outfile = o_logfile; /* XXXX Maybe it is better to output in another format? */ if (logstyle == logstyle_TeX) { T.TeXstyle |= TEXSTYLE_BREAK; gen_output(z, &T); pariputc('%'); } else outbrute(z); pariputc('\n'); pariflush(); } logfile = o_logfile; pari_outfile = o_out; if (n) term_color(c_NONE); return 1; } /* TEXMACS */ static void texmacs_output(GEN z, long n) { pariout_t T = *(GP_DATA->fmt); /* copy */ char *sz; T.prettyp = f_TEX; T.fieldw = 0; sz = GENtostr0(z, &T, &gen_output); printf("%clatex:", DATA_BEGIN); if (n) printf("\\magenta\\%%%ld = ", n); printf("$\\blue %s$%c", sz,DATA_END); free(sz); fflush(stdout); } /* REGULAR */ static void normal_output(GEN z, long n) { long l = 0; /* history number */ if (n) { char s[64]; term_color(c_HIST); sprintf(s, "%%%ld = ", n); pariputs_opt(s); l = strlen(s); } /* output */ term_color(c_OUTPUT); if (GP_DATA->lim_lines) lim_lines_output(z, GP_DATA->fmt, l, GP_DATA->lim_lines); else gen_output(z, GP_DATA->fmt); term_color(c_NONE); pariputc('\n'); } void gp_output(GEN z, gp_data *G) { if (G->flags & TEST) { init80col(0); gen_output(z, G->fmt); pariputc('\n'); } else if (G->flags & TEXMACS) texmacs_output(z, G->hist->total); else if (G->fmt->prettyp != f_PRETTY || !tex2mail_output(z, G->hist->total)) normal_output(z, G->hist->total); pariflush(); }